diff options
984 files changed, 37752 insertions, 10678 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index a2994088cfb2..e18470498f39 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -100,7 +100,7 @@ aconfig_declarations_group { "com.android.media.flags.performance-aconfig-java", "com.android.media.flags.projection-aconfig-java", "com.android.net.thread.platform.flags-aconfig-java", - "com.android.ranging.flags.ranging-aconfig-java", + "com.android.ranging.flags.ranging-aconfig-java-export", "com.android.server.contextualsearch.flags-java", "com.android.server.flags.services-aconfig-java", "com.android.text.flags-aconfig-java", @@ -373,6 +373,11 @@ java_aconfig_library { name: "android.security.flags-aconfig-java-export", aconfig_declarations: "android.security.flags-aconfig", mode: "exported", + min_sdk_version: "30", + apex_available: [ + "//apex_available:platform", + "com.android.wifi", + ], defaults: ["framework-minus-apex-aconfig-java-defaults"], } @@ -1651,13 +1656,6 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } -// Ranging -java_aconfig_library { - name: "com.android.ranging.flags.ranging-aconfig-java", - aconfig_declarations: "ranging_aconfig_flags", - defaults: ["framework-minus-apex-aconfig-java-defaults"], -} - // System Server aconfig_declarations { name: "android.systemserver.flags-aconfig", diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index a92a54337f96..d748a3bebfef 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -18,7 +18,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp tests/ tools/ bpfmt = -d -ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform +ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} diff --git a/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java b/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java index 7ef8c53d1d62..7168fbec07bc 100644 --- a/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java +++ b/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java @@ -19,6 +19,9 @@ import android.app.Activity; import android.os.Bundle; import android.os.ServiceManager.ServiceNotFoundException; import android.perftests.utils.Stats; +import android.tracing.perfetto.DataSourceParams; +import android.tracing.perfetto.InitArguments; +import android.tracing.perfetto.Producer; import androidx.test.InstrumentationRegistry; @@ -70,6 +73,8 @@ public class ProtoLogPerfTest { } private IProtoLog mProcessedProtoLogger; + private static ProtoLogDataSource sTestDataSource; + private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog"; private static final String MOCK_TEST_FILE_PATH = "mock/file/path"; private static final perfetto.protos.Protolog.ProtoLogViewerConfig VIEWER_CONFIG = perfetto.protos.Protolog.ProtoLogViewerConfig.newBuilder() @@ -89,6 +94,17 @@ public class ProtoLogPerfTest { @BeforeClass public static void init() { + Producer.init(InitArguments.DEFAULTS); + + sTestDataSource = new ProtoLogDataSource(TEST_PROTOLOG_DATASOURCE_NAME); + DataSourceParams params = + new DataSourceParams.Builder() + .setBufferExhaustedPolicy( + DataSourceParams + .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP) + .build(); + sTestDataSource.register(params); + ProtoLog.init(TestProtoLogGroup.values()); } @@ -98,9 +114,10 @@ public class ProtoLogPerfTest { TestProtoLogGroup.TEST_GROUP.setLogToLogcat(mLogToLogcat); mProcessedProtoLogger = new ProcessedPerfettoProtoLogImpl( + sTestDataSource, MOCK_TEST_FILE_PATH, () -> new AutoClosableProtoInputStream(VIEWER_CONFIG.toByteArray()), - () -> {}, + (instance) -> {}, TestProtoLogGroup.values() ); } diff --git a/apex/jobscheduler/service/aconfig/app_idle.aconfig b/apex/jobscheduler/service/aconfig/app_idle.aconfig index f079c02707e0..74d2a590086f 100644 --- a/apex/jobscheduler/service/aconfig/app_idle.aconfig +++ b/apex/jobscheduler/service/aconfig/app_idle.aconfig @@ -21,3 +21,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "adjust_default_bucket_elevation_params" + namespace: "backstage_power" + description: "Adjust the default bucket evaluation parameters" + bug: "379909479" +} diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 963307b110cf..a5a08fb9997c 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -573,6 +573,7 @@ public class JobSchedulerService extends com.android.server.SystemService case Constants.KEY_MIN_LINEAR_BACKOFF_TIME_MS: case Constants.KEY_MIN_EXP_BACKOFF_TIME_MS: case Constants.KEY_SYSTEM_STOP_TO_FAILURE_RATIO: + case Constants.KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF: mConstants.updateBackoffConstantsLocked(); break; case Constants.KEY_CONN_CONGESTION_DELAY_FRAC: @@ -679,6 +680,8 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String KEY_MIN_EXP_BACKOFF_TIME_MS = "min_exp_backoff_time_ms"; private static final String KEY_SYSTEM_STOP_TO_FAILURE_RATIO = "system_stop_to_failure_ratio"; + private static final String KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF = + "abandoned_job_timeouts_before_aggressive_backoff"; private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac"; private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac"; private static final String KEY_CONN_USE_CELL_SIGNAL_STRENGTH = @@ -750,6 +753,7 @@ public class JobSchedulerService extends com.android.server.SystemService private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS; private static final long DEFAULT_MIN_EXP_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS; private static final int DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO = 3; + private static final int DEFAULT_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF = 3; private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; private static final boolean DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH = true; @@ -845,7 +849,12 @@ public class JobSchedulerService extends com.android.server.SystemService * incremental failure in the backoff policy calculation. */ int SYSTEM_STOP_TO_FAILURE_RATIO = DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO; - + /** + * Number of consecutive timeouts by abandoned jobs before we change to aggressive backoff + * policy. + */ + int ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF = + DEFAULT_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF; /** * The fraction of a job's running window that must pass before we * consider running it when the network is congested. @@ -1078,6 +1087,10 @@ public class JobSchedulerService extends com.android.server.SystemService SYSTEM_STOP_TO_FAILURE_RATIO = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_SYSTEM_STOP_TO_FAILURE_RATIO, DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO); + ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF, + DEFAULT_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF); } // TODO(141645789): move into ConnectivityController.CcConfig @@ -1287,6 +1300,8 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println(); pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println(); pw.print(KEY_SYSTEM_STOP_TO_FAILURE_RATIO, SYSTEM_STOP_TO_FAILURE_RATIO).println(); + pw.print(KEY_ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF, + ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF).println(); pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); pw.print(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println(); pw.print(KEY_CONN_USE_CELL_SIGNAL_STRENGTH, CONN_USE_CELL_SIGNAL_STRENGTH).println(); @@ -2997,6 +3012,7 @@ public class JobSchedulerService extends com.android.server.SystemService final long initialBackoffMillis = job.getInitialBackoffMillis(); int numFailures = failureToReschedule.getNumFailures(); + int numAbandonedFailures = failureToReschedule.getNumAbandonedFailures(); int numSystemStops = failureToReschedule.getNumSystemStops(); // We should back off slowly if JobScheduler keeps stopping the job, // but back off immediately if the issue appeared to be the app's fault @@ -3006,9 +3022,19 @@ public class JobSchedulerService extends com.android.server.SystemService || internalStopReason == JobParameters.INTERNAL_STOP_REASON_ANR || stopReason == JobParameters.STOP_REASON_USER) { numFailures++; + } else if (android.app.job.Flags.handleAbandonedJobs() + && internalStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED) { + numAbandonedFailures++; + numFailures++; } else { numSystemStops++; } + + int backoffPolicy = job.getBackoffPolicy(); + if (shouldUseAggressiveBackoff(numAbandonedFailures)) { + backoffPolicy = JobInfo.BACKOFF_POLICY_EXPONENTIAL; + } + final int backoffAttempts = numFailures + numSystemStops / mConstants.SYSTEM_STOP_TO_FAILURE_RATIO; final long earliestRuntimeMs; @@ -3017,7 +3043,7 @@ public class JobSchedulerService extends com.android.server.SystemService earliestRuntimeMs = JobStatus.NO_EARLIEST_RUNTIME; } else { long delayMillis; - switch (job.getBackoffPolicy()) { + switch (backoffPolicy) { case JobInfo.BACKOFF_POLICY_LINEAR: { long backoff = initialBackoffMillis; if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME_MS) { @@ -3046,7 +3072,7 @@ public class JobSchedulerService extends com.android.server.SystemService } JobStatus newJob = new JobStatus(failureToReschedule, earliestRuntimeMs, - JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops, + JobStatus.NO_LATEST_RUNTIME, numFailures, numAbandonedFailures, numSystemStops, failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis(), failureToReschedule.getCumulativeExecutionTimeMs()); if (stopReason == JobParameters.STOP_REASON_USER) { @@ -3069,6 +3095,20 @@ public class JobSchedulerService extends com.android.server.SystemService } /** + * Returns {@code true} if the given number of abandoned failures indicates that JobScheduler + * should use an aggressive backoff policy. + * + * @param numAbandonedFailures The number of abandoned failures. + * @return {@code true} if the given number of abandoned failures indicates that JobScheduler + * should use an aggressive backoff policy. + */ + public boolean shouldUseAggressiveBackoff(int numAbandonedFailures) { + return android.app.job.Flags.handleAbandonedJobs() + && numAbandonedFailures + > mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF; + } + + /** * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This * does not cause a job's period to be larger than requested (eg: if the requested period is * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene @@ -3147,6 +3187,7 @@ public class JobSchedulerService extends com.android.server.SystemService return new JobStatus(periodicToReschedule, elapsedNow + period - flex, elapsedNow + period, 0 /* numFailures */, 0 /* numSystemStops */, + 0 /* numAbandonedFailures */, sSystemClock.millis() /* lastSuccessfulRunTime */, periodicToReschedule.getLastFailedRunTime(), 0 /* Reset cumulativeExecutionTime because of successful execution */); @@ -3163,6 +3204,7 @@ public class JobSchedulerService extends com.android.server.SystemService return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed, newLatestRuntimeElapsed, 0 /* numFailures */, 0 /* numSystemStops */, + 0 /* numAbandonedFailures */, sSystemClock.millis() /* lastSuccessfulRunTime */, periodicToReschedule.getLastFailedRunTime(), 0 /* Reset cumulativeExecutionTime because of successful execution */); @@ -3171,6 +3213,10 @@ public class JobSchedulerService extends com.android.server.SystemService @VisibleForTesting void maybeProcessBuggyJob(@NonNull JobStatus jobStatus, int debugStopReason) { boolean jobTimedOut = debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT; + if (android.app.job.Flags.handleAbandonedJobs()) { + jobTimedOut |= (debugStopReason + == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); + } // If madeActive = 0, the job never actually started. if (!jobTimedOut && jobStatus.madeActive > 0) { final long executionDurationMs = sUptimeMillisClock.millis() - jobStatus.madeActive; @@ -3252,9 +3298,12 @@ public class JobSchedulerService extends com.android.server.SystemService // we stop it. final JobStatus rescheduledJob = needsReschedule ? getRescheduleJobForFailureLocked(jobStatus, stopReason, debugStopReason) : null; + final boolean isStopReasonAbandoned = android.app.job.Flags.handleAbandonedJobs() + && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); if (rescheduledJob != null && !rescheduledJob.shouldTreatAsUserInitiatedJob() && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT + || isStopReasonAbandoned || debugStopReason == JobParameters.INTERNAL_STOP_REASON_PREEMPT)) { rescheduledJob.disallowRunInBatterySaverAndDoze(); } diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java index d8934d8f83b8..dfb36818c818 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java @@ -269,7 +269,9 @@ public final class JobStore { convertRtcBoundsToElapsed(utcTimes, elapsedNow); JobStatus newJob = new JobStatus(job, elapsedRuntimes.first, elapsedRuntimes.second, - 0, 0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime(), + 0 /* numFailures */, 0 /* numAbandonedFailures */, + 0 /* numSystemStops */, job.getLastSuccessfulRunTime(), + job.getLastFailedRunTime(), job.getCumulativeExecutionTimeMs()); newJob.prepareLocked(); toAdd.add(newJob); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index a3eaefd5f057..5a33aa0ab7eb 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -316,6 +316,12 @@ public final class JobStatus { private final int numFailures; /** + * How many times this job has stopped due to {@link + * JobParameters#STOP_REASON_TIMEOUT_ABANDONED}. + */ + private final int mNumAbandonedFailures; + + /** * The number of times JobScheduler has forced this job to stop due to reasons mostly outside * of the app's control. */ @@ -605,6 +611,8 @@ public final class JobStatus { * @param tag A string associated with the job for debugging/logging purposes. * @param numFailures Count of how many times this job has requested a reschedule because * its work was not yet finished. + * @param mNumAbandonedFailures Count of how many times this job has requested a reschedule + * because it was abandoned. * @param numSystemStops Count of how many times JobScheduler has forced this job to stop due to * factors mostly out of the app's control. * @param earliestRunTimeElapsedMillis Milestone: earliest point in time at which the job @@ -617,7 +625,7 @@ public final class JobStatus { */ private JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, int standbyBucket, @Nullable String namespace, String tag, - int numFailures, int numSystemStops, + int numFailures, int mNumAbandonedFailures, int numSystemStops, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, int internalFlags, @@ -677,6 +685,7 @@ public final class JobStatus { this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis; this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis; this.numFailures = numFailures; + this.mNumAbandonedFailures = mNumAbandonedFailures; mNumSystemStops = numSystemStops; int requiredConstraints = job.getConstraintFlags(); @@ -750,7 +759,8 @@ public final class JobStatus { this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(), jobStatus.getStandbyBucket(), jobStatus.getNamespace(), - jobStatus.getSourceTag(), jobStatus.getNumFailures(), jobStatus.getNumSystemStops(), + jobStatus.getSourceTag(), jobStatus.getNumFailures(), + jobStatus.getNumAbandonedFailures(), jobStatus.getNumSystemStops(), jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(), jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(), jobStatus.getCumulativeExecutionTimeMs(), @@ -787,6 +797,7 @@ public final class JobStatus { this(job, callingUid, sourcePkgName, sourceUserId, standbyBucket, namespace, sourceTag, /* numFailures */ 0, /* numSystemStops */ 0, + /* mNumAbandonedFailures */ 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs, innerFlags, dynamicConstraints); @@ -806,13 +817,15 @@ public final class JobStatus { /** Create a new job to be rescheduled with the provided parameters. */ public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, - long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops, + long newLatestRuntimeElapsedMillis, int numFailures, + int mNumAbandonedFailures, int numSystemStops, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs) { this(rescheduling.job, rescheduling.getUid(), rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(), rescheduling.getStandbyBucket(), rescheduling.getNamespace(), - rescheduling.getSourceTag(), numFailures, numSystemStops, + rescheduling.getSourceTag(), numFailures, + mNumAbandonedFailures, numSystemStops, newEarliestRuntimeElapsedMillis, newLatestRuntimeElapsedMillis, lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs, @@ -851,7 +864,8 @@ public final class JobStatus { int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage, sourceUserId, elapsedNow); return new JobStatus(job, callingUid, sourcePkg, sourceUserId, - standbyBucket, namespace, tag, /* numFailures */ 0, /* numSystemStops */ 0, + standbyBucket, namespace, tag, /* numFailures */ 0, + /* mNumAbandonedFailures */ 0, /* numSystemStops */ 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */, /* cumulativeExecutionTime */ 0, @@ -1146,6 +1160,13 @@ public final class JobStatus { } /** + * Returns the number of times the job stopped previously for STOP_REASON_TIMEOUT_ABANDONED. + */ + public int getNumAbandonedFailures() { + return mNumAbandonedFailures; + } + + /** * Returns the number of times the system stopped a previous execution of this job for reasons * that were likely outside the app's control. */ diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index c9d340757c6b..9871d713178e 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -337,11 +337,11 @@ public class AppStandbyController */ long[] mAppStandbyElapsedThresholds = DEFAULT_ELAPSED_TIME_THRESHOLDS; /** Minimum time a strong usage event should keep the bucket elevated. */ - long mStrongUsageTimeoutMillis = ConstantsObserver.DEFAULT_STRONG_USAGE_TIMEOUT; + long mStrongUsageTimeoutMillis = ConstantsObserver.DEFAULT_LEGACY_STRONG_USAGE_TIMEOUT; /** Minimum time a notification seen event should keep the bucket elevated. */ long mNotificationSeenTimeoutMillis = ConstantsObserver.DEFAULT_NOTIFICATION_TIMEOUT; /** Minimum time a slice pinned event should keep the bucket elevated. */ - long mSlicePinnedTimeoutMillis = ConstantsObserver.DEFAULT_SLICE_PINNED_TIMEOUT; + long mSlicePinnedTimeoutMillis = ConstantsObserver.DEFAULT_LEGACY_SLICE_PINNED_TIMEOUT; /** The standby bucket that an app will be promoted on a notification-seen event */ int mNotificationSeenPromotedBucket = ConstantsObserver.DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET; @@ -362,7 +362,9 @@ public class AppStandbyController /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */ long mPredictionTimeoutMillis = DEFAULT_PREDICTION_TIMEOUT; /** Maximum time a sync adapter associated with a CP should keep the buckets elevated. */ - long mSyncAdapterTimeoutMillis = ConstantsObserver.DEFAULT_SYNC_ADAPTER_TIMEOUT; + long mSyncAdapterTimeoutMillis = ConstantsObserver.DEFAULT_LEGACY_SYNC_ADAPTER_TIMEOUT; + /** The bucket that an app will be promoted on a sync adapter associated with a CP */ + int mSyncAdapaterPromotedBucket = STANDBY_BUCKET_ACTIVE; /** * Maximum time an exempted sync should keep the buckets elevated, when sync is scheduled in * non-doze @@ -751,7 +753,7 @@ public class AppStandbyController userId); synchronized (mAppIdleLock) { reportNoninteractiveUsageCrossUserLocked(packageName, userId, - STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER, + mSyncAdapaterPromotedBucket, REASON_SUB_USAGE_SYNC_ADAPTER, elapsedRealtime, mSyncAdapterTimeoutMillis, linkedProfiles); } } @@ -2446,6 +2448,8 @@ public class AppStandbyController pw.println("Flags: "); pw.println(" " + Flags.FLAG_AVOID_IDLE_CHECK + ": " + Flags.avoidIdleCheck()); + pw.println(" " + Flags.FLAG_ADJUST_DEFAULT_BUCKET_ELEVATION_PARAMS + + ": " + Flags.adjustDefaultBucketElevationParams()); pw.println(); synchronized (mCarrierPrivilegedLock) { @@ -2481,6 +2485,9 @@ public class AppStandbyController pw.print(" mSyncAdapterTimeoutMillis="); TimeUtils.formatDuration(mSyncAdapterTimeoutMillis, pw); pw.println(); + pw.print(" mSyncAdapaterPromotedBucket="); + pw.print(standbyBucketToString(mSyncAdapaterPromotedBucket)); + pw.println(); pw.print(" mSystemInteractionTimeoutMillis="); TimeUtils.formatDuration(mSystemInteractionTimeoutMillis, pw); pw.println(); @@ -3059,12 +3066,18 @@ public class AppStandbyController public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS = COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR; - public static final long DEFAULT_STRONG_USAGE_TIMEOUT = + public static final long DEFAULT_LEGACY_STRONG_USAGE_TIMEOUT = COMPRESS_TIME ? ONE_MINUTE : 1 * ONE_HOUR; + + public static final long DEFAULT_CURRENT_STRONG_USAGE_TIMEOUT = + COMPRESS_TIME ? ONE_MINUTE : 5 * ONE_MINUTE; public static final long DEFAULT_NOTIFICATION_TIMEOUT = COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR; - public static final long DEFAULT_SLICE_PINNED_TIMEOUT = + public static final long DEFAULT_LEGACY_SLICE_PINNED_TIMEOUT = COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR; + + public static final long DEFAULT_CURRENT_SLICE_PINNED_TIMEOUT = + COMPRESS_TIME ? 12 * ONE_MINUTE : 2 * ONE_HOUR; public static final int DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET = STANDBY_BUCKET_WORKING_SET; public static final boolean DEFAULT_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS = false; @@ -3073,8 +3086,11 @@ public class AppStandbyController COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR; public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE; - public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = + public static final long DEFAULT_LEGACY_SYNC_ADAPTER_TIMEOUT = COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE; + + public static final long DEFAULT_CURRENT_SYNC_ADAPTER_TIMEOUT = + COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR; public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_NON_DOZE_TIMEOUT = COMPRESS_TIME ? (ONE_MINUTE / 2) : 10 * ONE_MINUTE; public static final long DEFAULT_EXEMPTED_SYNC_SCHEDULED_DOZE_TIMEOUT = @@ -3117,6 +3133,9 @@ public class AppStandbyController cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED), false, this); mInjector.registerDeviceConfigPropertiesChangedListener(this); + + processDefaultConstants(); + // Load all the constants. // postOneTimeCheckIdleStates() doesn't need to be called on boot. processProperties(mInjector.getDeviceConfigProperties()); @@ -3135,6 +3154,17 @@ public class AppStandbyController postOneTimeCheckIdleStates(); } + private void processDefaultConstants() { + if (!Flags.adjustDefaultBucketElevationParams()) { + return; + } + + mSlicePinnedTimeoutMillis = DEFAULT_CURRENT_SLICE_PINNED_TIMEOUT; + mSyncAdapterTimeoutMillis = DEFAULT_CURRENT_SYNC_ADAPTER_TIMEOUT; + mSyncAdapaterPromotedBucket = STANDBY_BUCKET_WORKING_SET; + mStrongUsageTimeoutMillis = DEFAULT_CURRENT_STRONG_USAGE_TIMEOUT; + } + private void processProperties(DeviceConfig.Properties properties) { boolean timeThresholdsUpdated = false; synchronized (mAppIdleLock) { @@ -3182,11 +3212,16 @@ public class AppStandbyController case KEY_SLICE_PINNED_HOLD_DURATION: mSlicePinnedTimeoutMillis = properties.getLong( KEY_SLICE_PINNED_HOLD_DURATION, - DEFAULT_SLICE_PINNED_TIMEOUT); + Flags.adjustDefaultBucketElevationParams() + ? DEFAULT_CURRENT_SLICE_PINNED_TIMEOUT + : DEFAULT_LEGACY_SLICE_PINNED_TIMEOUT); break; case KEY_STRONG_USAGE_HOLD_DURATION: mStrongUsageTimeoutMillis = properties.getLong( - KEY_STRONG_USAGE_HOLD_DURATION, DEFAULT_STRONG_USAGE_TIMEOUT); + KEY_STRONG_USAGE_HOLD_DURATION, + Flags.adjustDefaultBucketElevationParams() + ? DEFAULT_CURRENT_STRONG_USAGE_TIMEOUT + : DEFAULT_LEGACY_STRONG_USAGE_TIMEOUT); break; case KEY_PREDICTION_TIMEOUT: mPredictionTimeoutMillis = properties.getLong( @@ -3203,7 +3238,10 @@ public class AppStandbyController break; case KEY_SYNC_ADAPTER_HOLD_DURATION: mSyncAdapterTimeoutMillis = properties.getLong( - KEY_SYNC_ADAPTER_HOLD_DURATION, DEFAULT_SYNC_ADAPTER_TIMEOUT); + KEY_SYNC_ADAPTER_HOLD_DURATION, + Flags.adjustDefaultBucketElevationParams() + ? DEFAULT_CURRENT_SYNC_ADAPTER_TIMEOUT + : DEFAULT_LEGACY_SYNC_ADAPTER_TIMEOUT); break; case KEY_EXEMPTED_SYNC_SCHEDULED_DOZE_HOLD_DURATION: mExemptedSyncScheduledDozeTimeoutMillis = properties.getLong( diff --git a/boot/boot-image-profile-extra.txt b/boot/boot-image-profile-extra.txt index e31eb3a993f0..fd51f9cbcee1 100644 --- a/boot/boot-image-profile-extra.txt +++ b/boot/boot-image-profile-extra.txt @@ -23,4 +23,25 @@ HSPLandroid/graphics/Color;->luminance()F # For now, compile all methods in MessageQueue to avoid performance cliffs for # flagged/evolving hot code paths. See: b/338098106 HSPLandroid/os/MessageQueue;->* -HSPLandroid/os/MessageQueue$*;->* +HSPLandroid/os/MessageQueue$FileDescriptorRecord;->* +HSPLandroid/os/MessageQueue$IdleHandler;->* +HSPLandroid/os/MessageQueue$MessageCompare;->* +HSPLandroid/os/MessageQueue$MatchAllFutureMessages;->* +HSPLandroid/os/MessageQueue$MatchAllMessages;->* +HSPLandroid/os/MessageQueue$MatchBarrierToken;->* +HSPLandroid/os/MessageQueue$MatchDeliverableMessages;->* +HSPLandroid/os/MessageQueue$MatchHandler;->* +HSPLandroid/os/MessageQueue$MatchHandlerAndObject;->* +HSPLandroid/os/MessageQueue$MatchHandlerAndObjectEquals;->* +HSPLandroid/os/MessageQueue$MatchHandlerRunnableAndObject;->* +HSPLandroid/os/MessageQueue$MatchHandlerRunnableAndObjectEquals;->* +HSPLandroid/os/MessageQueue$MatchHandlerWhatAndObject;->* +HSPLandroid/os/MessageQueue$MatchHandlerWhatAndObjectEquals;->* +HSPLandroid/os/MessageQueue$MessageCounts;->* +HSPLandroid/os/MessageQueue$StackNode;->* +HSPLandroid/os/MessageQueue$MessageNode;->* +HSPLandroid/os/MessageQueue$OnFileDescriptorEventListener$Events;->* +HSPLandroid/os/MessageQueue$OnFileDescriptorEventListener;->* +HSPLandroid/os/MessageQueue$StackNodeType;->* +HSPLandroid/os/MessageQueue$StateNode;->* +HSPLandroid/os/MessageQueue$TimedParkStateNode;->* diff --git a/cmds/idmap2/idmap2/CreateMultiple.cpp b/cmds/idmap2/idmap2/CreateMultiple.cpp index 68800cde6101..2608c69be66f 100644 --- a/cmds/idmap2/idmap2/CreateMultiple.cpp +++ b/cmds/idmap2/idmap2/CreateMultiple.cpp @@ -99,7 +99,7 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) { std::vector<std::string> idmap_paths; for (const std::string& overlay_apk_path : overlay_apk_paths) { - const std::string idmap_path = Idmap::CanonicalIdmapPathFor(idmap_dir, overlay_apk_path); + std::string idmap_path = Idmap::CanonicalIdmapPathFor(idmap_dir, overlay_apk_path); const uid_t uid = getuid(); if (!UidHasWriteAccessToPath(uid, idmap_path)) { LOG(WARNING) << "uid " << uid << "does not have write access to " << idmap_path.c_str(); @@ -111,7 +111,7 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) { !ignore_overlayable)) { const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path); if (!overlay) { - LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str(); + LOG(WARNING) << "failed to load apk " << overlay_apk_path; continue; } @@ -138,7 +138,7 @@ Result<Unit> CreateMultiple(const std::vector<std::string>& args) { } } - idmap_paths.emplace_back(idmap_path); + idmap_paths.emplace_back(std::move(idmap_path)); } for (const std::string& idmap_path : idmap_paths) { diff --git a/core/api/current.txt b/core/api/current.txt index 8b17908b6171..af7d6f1a5da1 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1502,6 +1502,7 @@ package android { field public static final int shadowRadius = 16843108; // 0x1010164 field public static final int shape = 16843162; // 0x101019a field public static final int shareInterpolator = 16843195; // 0x10101bb + field @FlaggedApi("android.nfc.nfc_associated_role_services") public static final int shareRolePriority; field @Deprecated public static final int sharedUserId = 16842763; // 0x101000b field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261 field public static final int sharedUserMaxSdkVersion = 16844365; // 0x101064d @@ -2451,6 +2452,7 @@ package android { field public static final int primary = 16908300; // 0x102000c field public static final int progress = 16908301; // 0x102000d field public static final int redo = 16908339; // 0x1020033 + field @FlaggedApi("android.appwidget.flags.engagement_metrics") public static final int remoteViewsMetricsId; field public static final int replaceText = 16908340; // 0x1020034 field public static final int secondaryProgress = 16908303; // 0x102000f field public static final int selectAll = 16908319; // 0x102001f @@ -9892,6 +9894,8 @@ package android.appwidget { field public static final String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK"; field public static final String ACTION_APPWIDGET_RESTORED = "android.appwidget.action.APPWIDGET_RESTORED"; field public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE"; + field @FlaggedApi("android.appwidget.flags.engagement_metrics") public static final String EVENT_CATEGORY_APPWIDGET = "android.appwidget"; + field @FlaggedApi("android.appwidget.flags.engagement_metrics") public static final String EVENT_TYPE_WIDGET_INTERACTION = "widget_interaction"; field public static final String EXTRA_APPWIDGET_ID = "appWidgetId"; field public static final String EXTRA_APPWIDGET_IDS = "appWidgetIds"; field public static final String EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds"; @@ -9901,6 +9905,10 @@ package android.appwidget { field public static final String EXTRA_APPWIDGET_PROVIDER_PROFILE = "appWidgetProviderProfile"; field public static final String EXTRA_CUSTOM_EXTRAS = "customExtras"; field public static final String EXTRA_CUSTOM_INFO = "customInfo"; + field @FlaggedApi("android.appwidget.flags.engagement_metrics") public static final String EXTRA_EVENT_CLICKED_VIEWS = "android.appwidget.extra.EVENT_CLICKED_VIEWS"; + field @FlaggedApi("android.appwidget.flags.engagement_metrics") public static final String EXTRA_EVENT_DURATION_MS = "android.appwidget.extra.EVENT_DURATION_MS"; + field @FlaggedApi("android.appwidget.flags.engagement_metrics") public static final String EXTRA_EVENT_POSITION_RECT = "android.appwidget.extra.EVENT_POSITION_RECT"; + field @FlaggedApi("android.appwidget.flags.engagement_metrics") public static final String EXTRA_EVENT_SCROLLED_VIEWS = "android.appwidget.extra.EVENT_SCROLLED_VIEWS"; field public static final String EXTRA_HOST_ID = "hostId"; field public static final int INVALID_APPWIDGET_ID = 0; // 0x0 field public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider"; @@ -13146,9 +13154,9 @@ package android.content.pm { method public void setAppLabel(@Nullable CharSequence); method public void setAppPackageName(@Nullable String); method public void setApplicationEnabledSettingPersistent(); + method @FlaggedApi("android.content.pm.sdk_dependency_installer") public void setAutoInstallDependenciesEnabled(boolean); method @Deprecated public void setAutoRevokePermissionsMode(boolean); method public void setDontKillApp(boolean); - method @FlaggedApi("android.content.pm.sdk_dependency_installer") public void setEnableAutoInstallDependencies(boolean); method public void setInstallLocation(int); method public void setInstallReason(int); method public void setInstallScenario(int); @@ -13217,8 +13225,8 @@ package android.content.pm { public abstract class PackageManager { ctor @Deprecated public PackageManager(); method @Deprecated public abstract void addPackageToPreferred(@NonNull String); - method public abstract boolean addPermission(@NonNull android.content.pm.PermissionInfo); - method public abstract boolean addPermissionAsync(@NonNull android.content.pm.PermissionInfo); + method @Deprecated @FlaggedApi("android.permission.flags.permission_tree_apis_deprecated") public abstract boolean addPermission(@NonNull android.content.pm.PermissionInfo); + method @Deprecated @FlaggedApi("android.permission.flags.permission_tree_apis_deprecated") public abstract boolean addPermissionAsync(@NonNull android.content.pm.PermissionInfo); method @Deprecated public abstract void addPreferredActivity(@NonNull android.content.IntentFilter, int, @Nullable android.content.ComponentName[], @NonNull android.content.ComponentName); method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean addWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int); method public boolean canPackageQuery(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; @@ -13359,7 +13367,7 @@ package android.content.pm { method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryServiceProperty(@NonNull String); method public void relinquishUpdateOwnership(@NonNull String); method @Deprecated public abstract void removePackageFromPreferred(@NonNull String); - method public abstract void removePermission(@NonNull String); + method @Deprecated @FlaggedApi("android.permission.flags.permission_tree_apis_deprecated") public abstract void removePermission(@NonNull String); method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int); method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int); @@ -18954,6 +18962,7 @@ package android.hardware { method @FlaggedApi("android.hardware.flags.luts_api") @NonNull public int[] getSamplingKeys(); method @FlaggedApi("android.hardware.flags.luts_api") public int getSize(); field @FlaggedApi("android.hardware.flags.luts_api") public static final int ONE_DIMENSION = 1; // 0x1 + field @FlaggedApi("android.hardware.flags.luts_api") public static final int SAMPLING_KEY_CIE_Y = 2; // 0x2 field @FlaggedApi("android.hardware.flags.luts_api") public static final int SAMPLING_KEY_MAX_RGB = 1; // 0x1 field @FlaggedApi("android.hardware.flags.luts_api") public static final int SAMPLING_KEY_RGB = 0; // 0x0 field @FlaggedApi("android.hardware.flags.luts_api") public static final int THREE_DIMENSION = 3; // 0x3 @@ -24166,6 +24175,8 @@ package android.media { field public static final String KEY_OPERATING_RATE = "operating-rate"; field public static final String KEY_OUTPUT_REORDER_DEPTH = "output-reorder-depth"; field public static final String KEY_PCM_ENCODING = "pcm-encoding"; + field @FlaggedApi("android.media.tv.flags.apply_picture_profiles") public static final String KEY_PICTURE_PROFILE_ID = "picture-profile-id"; + field @FlaggedApi("android.media.tv.flags.apply_picture_profiles") public static final String KEY_PICTURE_PROFILE_INSTANCE = "picture-profile-instance"; field public static final String KEY_PICTURE_TYPE = "picture-type"; field public static final String KEY_PIXEL_ASPECT_RATIO_HEIGHT = "sar-height"; field public static final String KEY_PIXEL_ASPECT_RATIO_WIDTH = "sar-width"; @@ -25613,6 +25624,7 @@ package android.media { method public void addOnSpatializerStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerStateChangedListener); method public boolean canBeSpatialized(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioFormat); method public int getImmersiveAudioLevel(); + method @FlaggedApi("android.media.audio.spatializer_capabilities") @NonNull public java.util.List<java.lang.Integer> getSpatializedChannelMasks(); method public boolean isAvailable(); method public boolean isEnabled(); method public boolean isHeadTrackerAvailable(); @@ -33600,9 +33612,14 @@ package android.os { @FlaggedApi("android.os.cpu_gpu_headrooms") public final class CpuHeadroomParams { ctor public CpuHeadroomParams(); method public int getCalculationType(); + method @IntRange(from=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) public long getCalculationWindowMillis(); method public void setCalculationType(int); + method public void setCalculationWindowMillis(@IntRange(from=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int); + method public void setTids(@NonNull int...); field public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1 field public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0 + field public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; // 0x2710 + field public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; // 0x32 } public final class CpuUsageInfo implements android.os.Parcelable { @@ -33855,9 +33872,13 @@ package android.os { @FlaggedApi("android.os.cpu_gpu_headrooms") public final class GpuHeadroomParams { ctor public GpuHeadroomParams(); method public int getCalculationType(); + method @IntRange(from=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) public int getCalculationWindowMillis(); method public void setCalculationType(int); + method public void setCalculationWindowMillis(@IntRange(from=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int); field public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1 field public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0 + field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; // 0x2710 + field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; // 0x32 } public class Handler { @@ -51925,7 +51946,7 @@ package android.view { method public int getState(); method @FlaggedApi("com.android.server.display.feature.flags.enable_get_suggested_frame_rate") public float getSuggestedFrameRate(int); method public android.view.Display.Mode[] getSupportedModes(); - method @Deprecated public float[] getSupportedRefreshRates(); + method @FlaggedApi("com.android.server.display.feature.flags.enable_get_supported_refresh_rates") @NonNull public float[] getSupportedRefreshRates(); method @Deprecated public int getWidth(); method @FlaggedApi("com.android.server.display.feature.flags.enable_has_arr_support") public boolean hasArrSupport(); method public boolean isHdr(); @@ -56483,6 +56504,7 @@ package android.view.accessibility { method public float getMin(); method public int getType(); method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.RangeInfo obtain(int, float, float, float); + field @FlaggedApi("android.view.accessibility.indeterminate_range_info") @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.RangeInfo INDETERMINATE; field public static final int RANGE_TYPE_FLOAT = 1; // 0x1 field @FlaggedApi("android.view.accessibility.indeterminate_range_info") public static final int RANGE_TYPE_INDETERMINATE = 3; // 0x3 field public static final int RANGE_TYPE_INT = 0; // 0x0 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 9c5eb3e20866..83699ac30939 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -34,6 +34,7 @@ package android { field public static final String ACCESS_VIBRATOR_STATE = "android.permission.ACCESS_VIBRATOR_STATE"; field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING"; field public static final String ADD_ALWAYS_UNLOCKED_DISPLAY = "android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY"; + field @FlaggedApi("android.companion.virtualdevice.flags.enable_limited_vdm_role") public static final String ADD_MIRROR_DISPLAY = "android.permission.ADD_MIRROR_DISPLAY"; field public static final String ADD_TRUSTED_DISPLAY = "android.permission.ADD_TRUSTED_DISPLAY"; field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY"; field public static final String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE"; @@ -3435,14 +3436,14 @@ package android.app.wearable { public class WearableSensingManager { method @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public int getAvailableConnectionCount(); method @Nullable public static android.app.wearable.WearableSensingDataRequest getDataRequestFromIntent(@NonNull android.content.Intent); - method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @Deprecated @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideConnection(@NonNull android.app.wearable.WearableConnection, @NonNull java.util.concurrent.Executor); method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideData(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideDataStream(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("android.app.wearable.enable_provide_read_only_pfd") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideReadOnlyParcelFileDescriptor(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void registerDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void removeAllConnections(); - method @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public boolean removeConnection(@NonNull android.app.wearable.WearableConnection); + method @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void removeConnection(@NonNull android.app.wearable.WearableConnection); method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void startHotwordRecognition(@Nullable android.content.ComponentName, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void stopHotwordRecognition(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void unregisterDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); @@ -3939,6 +3940,7 @@ package android.content { field public static final String WIFI_NL80211_SERVICE = "wifinl80211"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; field public static final String WIFI_SCANNING_SERVICE = "wifiscanner"; + field @FlaggedApi("android.net.wifi.flags.usd") public static final String WIFI_USD_SERVICE = "wifi_usd"; } public final class ContextParams { @@ -4249,6 +4251,7 @@ package android.content.pm { method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean); field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL"; field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL"; + field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final String ACTION_INSTALL_DEPENDENCY = "android.content.pm.action.INSTALL_DEPENDENCY"; field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2 field public static final int DATA_LOADER_TYPE_NONE = 0; // 0x0 field public static final int DATA_LOADER_TYPE_STREAMING = 1; // 0x1 @@ -5207,6 +5210,11 @@ package android.hardware.contexthub { method @NonNull public java.util.Collection<android.hardware.contexthub.HubServiceInfo> getServiceInfoCollection(); method @Nullable public String getTag(); method public int getVersion(); + field public static final int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4; // 0x4 + field public static final int REASON_ENDPOINT_INVALID = 5; // 0x5 + field public static final int REASON_ENDPOINT_STOPPED = 6; // 0x6 + field public static final int REASON_FAILURE = 0; // 0x0 + field public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; // 0x3 } public static final class HubEndpoint.Builder { @@ -5273,9 +5281,8 @@ package android.hardware.contexthub { } @FlaggedApi("android.chre.flags.offload_api") public final class HubServiceInfo implements android.os.Parcelable { - ctor public HubServiceInfo(@NonNull String, int, int, int, @NonNull android.os.ParcelableHolder); + ctor public HubServiceInfo(@NonNull String, int, int, int); method public int describeContents(); - method @NonNull public android.os.ParcelableHolder getExtendedInfo(); method public int getFormat(); method public int getMajorVersion(); method public int getMinorVersion(); @@ -5290,16 +5297,17 @@ package android.hardware.contexthub { public static final class HubServiceInfo.Builder { ctor public HubServiceInfo.Builder(@NonNull String, int, int, int); method @NonNull public android.hardware.contexthub.HubServiceInfo build(); - method @NonNull public android.hardware.contexthub.HubServiceInfo.Builder setExtendedInfo(@Nullable android.os.Parcelable); + } + + @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointDiscoveryCallback { + method public void onEndpointsStarted(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>); + method public void onEndpointsStopped(@NonNull java.util.List<android.hardware.contexthub.HubDiscoveryInfo>, int); } @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointLifecycleCallback { method public void onSessionClosed(@NonNull android.hardware.contexthub.HubEndpointSession, int); method @NonNull public android.hardware.contexthub.HubEndpointSessionResult onSessionOpenRequest(@NonNull android.hardware.contexthub.HubEndpointInfo, @Nullable android.hardware.contexthub.HubServiceInfo); method public void onSessionOpened(@NonNull android.hardware.contexthub.HubEndpointSession); - field public static final int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4; // 0x4 - field public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; // 0x3 - field public static final int REASON_UNSPECIFIED = 0; // 0x0 } @FlaggedApi("android.chre.flags.offload_api") public interface IHubEndpointMessageCallback { @@ -6315,11 +6323,16 @@ package android.hardware.location { method @Deprecated public int registerCallback(@NonNull android.hardware.location.ContextHubManager.Callback); method @Deprecated public int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler); method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpoint(@NonNull android.hardware.contexthub.HubEndpoint); + method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(long, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback); + method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(long, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback, @NonNull java.util.concurrent.Executor); + method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull String, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback); + method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void registerEndpointDiscoveryCallback(@NonNull String, @NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback, @NonNull java.util.concurrent.Executor); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessage(int, int, @NonNull android.hardware.location.ContextHubMessage); method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int unloadNanoApp(int); method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long); method @Deprecated public int unregisterCallback(@NonNull android.hardware.location.ContextHubManager.Callback); method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void unregisterEndpoint(@NonNull android.hardware.contexthub.HubEndpoint); + method @FlaggedApi("android.chre.flags.offload_api") @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void unregisterEndpointDiscoveryCallback(@NonNull android.hardware.contexthub.IHubEndpointDiscoveryCallback); field public static final int AUTHORIZATION_DENIED = 0; // 0x0 field public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1; // 0x1 field public static final int AUTHORIZATION_GRANTED = 2; // 0x2 @@ -7624,9 +7637,11 @@ package android.media { method public boolean isActive(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isMuted(); method public boolean isSpatialized(); - field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_APP_OPS = 8; // 0x8 + field @Deprecated @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_APP_OPS = 8; // 0x8 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_CLIENT_VOLUME = 16; // 0x10 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_MASTER = 1; // 0x1 + field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_OP_CONTROL_AUDIO = 128; // 0x80 + field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_OP_PLAY_AUDIO = 8; // 0x8 field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_PORT_VOLUME = 64; // 0x40 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_MUTED = 4; // 0x4 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_VOLUME = 2; // 0x2 @@ -11110,6 +11125,7 @@ package android.nfc.cardemulation { method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup); method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String); method @FlaggedApi("android.nfc.nfc_observe_mode") public void setShouldDefaultToObserveMode(boolean); + method @FlaggedApi("android.nfc.nfc_associated_role_services") public boolean shareRolePriority(); method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean shouldDefaultToObserveMode(); method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int); field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR; @@ -12865,6 +12881,30 @@ package android.security.authenticationpolicy { package android.security.intrusiondetection { + @FlaggedApi("android.security.afl_api") public final class IntrusionDetectionEvent implements android.os.Parcelable { + ctor public IntrusionDetectionEvent(@NonNull android.app.admin.SecurityLog.SecurityEvent); + ctor public IntrusionDetectionEvent(@NonNull android.app.admin.DnsEvent); + ctor public IntrusionDetectionEvent(@NonNull android.app.admin.ConnectEvent); + method @FlaggedApi("android.security.afl_api") public int describeContents(); + method @NonNull public android.app.admin.ConnectEvent getConnectEvent(); + method @NonNull public android.app.admin.DnsEvent getDnsEvent(); + method @NonNull public android.app.admin.SecurityLog.SecurityEvent getSecurityEvent(); + method @NonNull public int getType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.security.intrusiondetection.IntrusionDetectionEvent> CREATOR; + field public static final int NETWORK_EVENT_CONNECT = 2; // 0x2 + field public static final int NETWORK_EVENT_DNS = 1; // 0x1 + field public static final int SECURITY_EVENT = 0; // 0x0 + } + + @FlaggedApi("android.security.afl_api") public class IntrusionDetectionEventTransport { + ctor public IntrusionDetectionEventTransport(); + method public boolean addData(@NonNull java.util.List<android.security.intrusiondetection.IntrusionDetectionEvent>); + method @NonNull public android.os.IBinder getBinder(); + method public boolean initialize(); + method public boolean release(); + } + @FlaggedApi("android.security.afl_api") public class IntrusionDetectionManager { method @RequiresPermission(android.Manifest.permission.READ_INTRUSION_DETECTION_STATE) public void addStateCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @RequiresPermission(android.Manifest.permission.MANAGE_INTRUSION_DETECTION_STATE) public void disable(@NonNull java.util.concurrent.Executor, @NonNull android.security.intrusiondetection.IntrusionDetectionManager.CommandCallback); @@ -14551,7 +14591,7 @@ package android.service.wearable { method @BinderThread public abstract void onDataStreamProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @BinderThread public abstract void onQueryServiceStatus(@NonNull java.util.Set<java.lang.Integer>, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>); method @FlaggedApi("android.app.wearable.enable_provide_read_only_pfd") @BinderThread public void onReadOnlyParcelFileDescriptorProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.PersistableBundle, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @BinderThread public void onSecureConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @Deprecated @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @BinderThread public void onSecureConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @FlaggedApi("android.app.wearable.enable_concurrent_wearable_connections") @BinderThread public void onSecureConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull android.os.PersistableBundle, @NonNull java.util.function.Consumer<java.lang.Integer>); method @BinderThread public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionResult>); method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @BinderThread public void onStartHotwordRecognition(@NonNull java.util.function.Consumer<android.service.voice.HotwordAudioStream>, @NonNull java.util.function.Consumer<java.lang.Integer>); @@ -16196,6 +16236,7 @@ package android.telephony { method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @FlaggedApi("com.android.internal.telephony.flags.carrier_id_from_carrier_identifier") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getCarrierIdFromCarrierIdentifier(@NonNull android.service.carrier.CarrierIdentifier); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 1a97f7beb7fa..967f6194969e 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3730,6 +3730,7 @@ package android.view { method @NonNull public android.view.Display.Mode getDefaultMode(); method public int getRemoveMode(); method @NonNull public int[] getReportedHdrTypes(); + method @NonNull public float[] getSupportedRefreshRatesLegacy(); method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut(); method @Nullable public android.view.Display.Mode getSystemPreferredDisplayMode(); method public int getType(); @@ -3752,6 +3753,7 @@ package android.view { public static final class Display.Mode implements android.os.Parcelable { ctor public Display.Mode(int, int, float); + method public float getVsyncRate(); method public boolean isSynthetic(); method public boolean matches(int, int, float); } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index d9f8d33f0545..eccb6ffb281c 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -1164,6 +1164,19 @@ public abstract class ActivityManagerInternal { @UserIdInt int userId, int notificationId); /** + * Notifies that a media service associated with a media session has transitioned to a + * "user-engaged" state. Upon receiving this notification, service will transition to the + * foreground state. It should only be called by + * {@link com.android.server.media.MediaSessionService} + * + * @param packageName The package name of the app running the media service. + * @param userId The user ID associated with the service. + * @param notificationId The ID of the media notification associated with the service. + */ + public abstract void notifyActiveMediaForegroundService(@NonNull String packageName, + @UserIdInt int userId, int notificationId); + + /** * Same as {@link android.app.IActivityManager#startProfile(int userId)}, but it would succeed * even if the profile is disabled - it should only be called by * {@link com.android.server.devicepolicy.DevicePolicyManagerService} when starting a profile diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index cb7b1153988a..f8186d68e210 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -8843,10 +8843,10 @@ public final class ActivityThread extends ClientTransactionHandler // Call per-process mainline module initialization. initializeMainlineModules(); - Process.setArgV0("<pre-initialized>"); - Looper.prepareMainLooper(); + Process.setArgV0("<pre-initialized>"); + // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line. // It will be in the format "seq=114" long startSeq = 0; diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index f2228f94ff01..4a9a28607796 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -178,6 +178,11 @@ oneway interface ITaskStackListener { void onRecentTaskListFrozenChanged(boolean frozen); /** + * Called when a task is removed from the recent tasks list as a result of adding a new task. + */ + void onRecentTaskRemovedForAddTask(int taskId); + + /** * Called when a task gets or loses focus. * * @param taskId id of the task. diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index b84c91b8276b..7fcae45ea452 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -441,8 +441,8 @@ public class Notification implements Parcelable /** * A large-format version of {@link #contentView}, giving the Notification an - * opportunity to show more detail. The system UI may choose to show this - * instead of the normal content view at its discretion. + * opportunity to show more detail when expanded. The system UI may choose + * to show this instead of the normal content view at its discretion. * * As of N, this field may be null. The expanded notification view is determined by the * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be @@ -814,16 +814,16 @@ public class Notification implements Parcelable if (Flags.notificationsRedesignTemplates()) { return switch (layoutId) { case R.layout.notification_2025_template_collapsed_base, + R.layout.notification_2025_template_expanded_base, R.layout.notification_2025_template_heads_up_base, R.layout.notification_2025_template_header, - R.layout.notification_template_material_big_base, + R.layout.notification_2025_template_collapsed_messaging, + R.layout.notification_2025_template_collapsed_media, R.layout.notification_template_material_big_picture, R.layout.notification_template_material_big_text, R.layout.notification_template_material_inbox, - R.layout.notification_template_material_messaging, R.layout.notification_template_material_big_messaging, R.layout.notification_template_material_conversation, - R.layout.notification_template_material_media, R.layout.notification_template_material_big_media, R.layout.notification_template_material_call, R.layout.notification_template_material_big_call, @@ -1337,7 +1337,7 @@ public class Notification implements Parcelable public static final String EXTRA_SUMMARY_TEXT = "android.summaryText"; /** - * {@link #extras} key: this is the longer text shown in the big form of a + * {@link #extras} key: this is the longer text shown in the expanded form of a * {@link BigTextStyle} notification, as supplied to * {@link BigTextStyle#bigText(CharSequence)}. */ @@ -3263,6 +3263,7 @@ public class Notification implements Parcelable public boolean hasPromotableCharacteristics() { return isColorizedRequested() && hasTitle() + && !isGroupSummary() && !containsCustomViews() && hasPromotableStyle(); } @@ -5918,12 +5919,12 @@ public class Notification implements Parcelable private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p, TemplateBindResult result) { - p.headerless(resId == getBaseLayoutResource() + p.headerless(resId == getCollapsedBaseLayoutResource() || resId == getHeadsUpBaseLayoutResource() || resId == getCompactHeadsUpBaseLayoutResource() || resId == getMessagingCompactHeadsUpLayoutResource() - || resId == getMessagingLayoutResource() - || resId == R.layout.notification_template_material_media); + || resId == getCollapsedMessagingLayoutResource() + || resId == getCollapsedMediaLayoutResource()); RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId); resetStandardTemplate(contentView); @@ -6377,7 +6378,7 @@ public class Notification implements Parcelable boolean hideSnoozeButton = mN.isFgsOrUij() || mN.fullScreenIntent != null || isBackgroundColorized(p) - || p.mViewType != StandardTemplateParams.VIEW_TYPE_BIG; + || p.mViewType != StandardTemplateParams.VIEW_TYPE_EXPANDED; big.setBoolean(R.id.snooze_button, "setEnabled", !hideSnoozeButton); if (hideSnoozeButton) { // Only hide; NotificationContentView will show it when it adds the click listener @@ -6582,19 +6583,21 @@ public class Notification implements Parcelable .decorationType(StandardTemplateParams.DECORATION_MINIMAL) .fillTextsFrom(this); TemplateBindResult result = new TemplateBindResult(); - RemoteViews standard = applyStandardTemplate(getBaseLayoutResource(), p, result); + RemoteViews standard = applyStandardTemplate(getCollapsedBaseLayoutResource(), + p, result); buildCustomContentIntoTemplate(mContext, standard, customContent, p, result); return standard; } - private RemoteViews minimallyDecoratedBigContentView(@NonNull RemoteViews customContent) { + private RemoteViews minimallyDecoratedExpandedContentView( + @NonNull RemoteViews customContent) { StandardTemplateParams p = mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .decorationType(StandardTemplateParams.DECORATION_MINIMAL) .fillTextsFrom(this); TemplateBindResult result = new TemplateBindResult(); - RemoteViews standard = applyStandardTemplateWithActions(getBigBaseLayoutResource(), + RemoteViews standard = applyStandardTemplateWithActions(getExpandedBaseLayoutResource(), p, result); buildCustomContentIntoTemplate(mContext, standard, customContent, p, result); @@ -6640,7 +6643,7 @@ public class Notification implements Parcelable StandardTemplateParams p = mParams.reset() .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) .fillTextsFrom(this); - return applyStandardTemplate(getBaseLayoutResource(), p, null /* result */); + return applyStandardTemplate(getCollapsedBaseLayoutResource(), p, null /* result */); } private boolean useExistingRemoteView(RemoteViews customContent) { @@ -6678,24 +6681,29 @@ public class Notification implements Parcelable */ @Deprecated public RemoteViews createBigContentView() { + return createExpandedContentView(); + } + + private RemoteViews createExpandedContentView() { RemoteViews result = null; if (useExistingRemoteView(mN.bigContentView)) { return fullyCustomViewRequiresDecoration(false /* fromStyle */) - ? minimallyDecoratedBigContentView(mN.bigContentView) : mN.bigContentView; + ? minimallyDecoratedExpandedContentView(mN.bigContentView) + : mN.bigContentView; } if (mStyle != null) { - result = mStyle.makeBigContentView(); + result = mStyle.makeExpandedContentView(); if (fullyCustomViewRequiresDecoration(true /* fromStyle */)) { - result = minimallyDecoratedBigContentView(result); + result = minimallyDecoratedExpandedContentView(result); } } if (result == null) { - if (bigContentViewRequired()) { + if (expandedContentViewRequired()) { StandardTemplateParams p = mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .allowTextWithProgress(true) .fillTextsFrom(this); - result = applyStandardTemplateWithActions(getBigBaseLayoutResource(), p, + result = applyStandardTemplateWithActions(getExpandedBaseLayoutResource(), p, null /* result */); } } @@ -6709,7 +6717,7 @@ public class Notification implements Parcelable // apps can detect the change, it's most likely that the changes will simply result in // visual regressions. @SuppressWarnings("AndroidFrameworkCompatChange") - private boolean bigContentViewRequired() { + private boolean expandedContentViewRequired() { if (Flags.notificationExpansionOptional()) { // Notifications without a bigContentView, style, or actions do not need to expand boolean exempt = mN.bigContentView == null @@ -7520,7 +7528,7 @@ public class Notification implements Parcelable } @UnsupportedAppUsage - private int getBaseLayoutResource() { + private int getCollapsedBaseLayoutResource() { if (Flags.notificationsRedesignTemplates()) { return R.layout.notification_2025_template_collapsed_base; } else { @@ -7544,8 +7552,12 @@ public class Notification implements Parcelable return R.layout.notification_template_material_messaging_compact_heads_up; } - private int getBigBaseLayoutResource() { - return R.layout.notification_template_material_big_base; + private int getExpandedBaseLayoutResource() { + if (Flags.notificationsRedesignTemplates()) { + return R.layout.notification_2025_template_expanded_base; + } else { + return R.layout.notification_template_material_big_base; + } } private int getBigPictureLayoutResource() { @@ -7560,14 +7572,26 @@ public class Notification implements Parcelable return R.layout.notification_template_material_inbox; } - private int getMessagingLayoutResource() { - return R.layout.notification_template_material_messaging; + private int getCollapsedMessagingLayoutResource() { + if (Flags.notificationsRedesignTemplates()) { + return R.layout.notification_2025_template_collapsed_messaging; + } else { + return R.layout.notification_template_material_messaging; + } } - private int getBigMessagingLayoutResource() { + private int getExpandedMessagingLayoutResource() { return R.layout.notification_template_material_big_messaging; } + private int getCollapsedMediaLayoutResource() { + if (Flags.notificationsRedesignTemplates()) { + return R.layout.notification_2025_template_collapsed_media; + } else { + return R.layout.notification_template_material_media; + } + } + private int getConversationLayoutResource() { return R.layout.notification_template_material_conversation; } @@ -8033,7 +8057,7 @@ public class Notification implements Parcelable protected Builder mBuilder; /** - * Overrides ContentTitle in the big form of the template. + * Overrides ContentTitle in the expanded form of the template. * This defaults to the value passed to setContentTitle(). */ protected void internalSetBigContentTitle(CharSequence title) { @@ -8041,7 +8065,7 @@ public class Notification implements Parcelable } /** - * Set the first line of text after the detail section in the big form of the template. + * Set the first line of text after the detail section in the expanded form of the template. */ protected void internalSetSummaryText(CharSequence cs) { mSummaryText = cs; @@ -8104,10 +8128,10 @@ public class Notification implements Parcelable } /** - * Construct a Style-specific RemoteViews for the final big notification layout. + * Construct a Style-specific RemoteViews for the final expanded notification layout. * @hide */ - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { return null; } @@ -8271,7 +8295,7 @@ public class Notification implements Parcelable } /** - * Overrides ContentTitle in the big form of the template. + * Overrides ContentTitle in the expanded form of the template. * This defaults to the value passed to setContentTitle(). */ @NonNull @@ -8281,7 +8305,7 @@ public class Notification implements Parcelable } /** - * Set the first line of text after the detail section in the big form of the template. + * Set the first line of text after the detail section in the expanded form of the template. */ @NonNull public BigPictureStyle setSummaryText(@Nullable CharSequence cs) { @@ -8340,7 +8364,7 @@ public class Notification implements Parcelable } /** - * Override the large icon when the big notification is shown. + * Override the large icon when the expanded notification is shown. */ @NonNull public BigPictureStyle bigLargeIcon(@Nullable Bitmap b) { @@ -8348,7 +8372,7 @@ public class Notification implements Parcelable } /** - * Override the large icon when the big notification is shown. + * Override the large icon when the expanded notification is shown. */ @NonNull public BigPictureStyle bigLargeIcon(@Nullable Icon icon) { @@ -8412,7 +8436,7 @@ public class Notification implements Parcelable .viewType(StandardTemplateParams.VIEW_TYPE_NORMAL) .fillTextsFrom(mBuilder) .promotedPicture(mPictureIcon); - return getStandardView(mBuilder.getBaseLayoutResource(), p, null /* result */); + return getStandardView(mBuilder.getCollapsedBaseLayoutResource(), p, null /* result */); } /** @@ -8434,7 +8458,7 @@ public class Notification implements Parcelable /** * @hide */ - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet // This covers the following cases: // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides @@ -8453,7 +8477,7 @@ public class Notification implements Parcelable } StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG).fillTextsFrom(mBuilder); + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED).fillTextsFrom(mBuilder); RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource(), p, null /* result */); if (mSummaryTextSet) { @@ -8600,7 +8624,7 @@ public class Notification implements Parcelable } /** - * Overrides ContentTitle in the big form of the template. + * Overrides ContentTitle in the expanded form of the template. * This defaults to the value passed to setContentTitle(). */ public BigTextStyle setBigContentTitle(CharSequence title) { @@ -8609,7 +8633,7 @@ public class Notification implements Parcelable } /** - * Set the first line of text after the detail section in the big form of the template. + * Set the first line of text after the detail section in the expanded form of the template. */ public BigTextStyle setSummaryText(CharSequence cs) { internalSetSummaryText(safeCharSequence(cs)); @@ -8617,7 +8641,7 @@ public class Notification implements Parcelable } /** - * Provide the longer text to be displayed in the big form of the + * Provide the longer text to be displayed in the expanded form of the * template in place of the content text. */ public BigTextStyle bigText(CharSequence cs) { @@ -8661,7 +8685,7 @@ public class Notification implements Parcelable if (increasedHeight) { ArrayList<Action> originalActions = mBuilder.mActions; mBuilder.mActions = new ArrayList<>(); - RemoteViews remoteViews = makeBigContentView(); + RemoteViews remoteViews = makeExpandedContentView(); mBuilder.mActions = originalActions; return remoteViews; } @@ -8675,7 +8699,7 @@ public class Notification implements Parcelable public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { if (increasedHeight && mBuilder.mActions.size() > 0) { // TODO(b/163626038): pass VIEW_TYPE_HEADS_UP? - return makeBigContentView(); + return makeExpandedContentView(); } return super.makeHeadsUpContentView(increasedHeight); } @@ -8683,9 +8707,9 @@ public class Notification implements Parcelable /** * @hide */ - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .allowTextWithProgress(true) .textViewId(R.id.big_text) .fillTextsFrom(mBuilder); @@ -9357,20 +9381,20 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeBigContentView() { - return makeMessagingView(StandardTemplateParams.VIEW_TYPE_BIG); + public RemoteViews makeExpandedContentView() { + return makeMessagingView(StandardTemplateParams.VIEW_TYPE_EXPANDED); } /** * Create a messaging layout. * - * @param viewType one of StandardTemplateParams.VIEW_TYPE_NORMAL, VIEW_TYPE_BIG, + * @param viewType one of StandardTemplateParams.VIEW_TYPE_NORMAL, VIEW_TYPE_EXPANDEDIG, * VIEW_TYPE_HEADS_UP * @return the created remoteView. */ @NonNull private RemoteViews makeMessagingView(int viewType) { - boolean isCollapsed = viewType != StandardTemplateParams.VIEW_TYPE_BIG; + boolean isCollapsed = viewType != StandardTemplateParams.VIEW_TYPE_EXPANDED; boolean hideRightIcons = viewType != StandardTemplateParams.VIEW_TYPE_NORMAL; boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY; boolean isImportantConversation = mConversationType == CONVERSATION_TYPE_IMPORTANT; @@ -9414,8 +9438,8 @@ public class Notification implements Parcelable isConversationLayout ? mBuilder.getConversationLayoutResource() : isCollapsed - ? mBuilder.getMessagingLayoutResource() - : mBuilder.getBigMessagingLayoutResource(), + ? mBuilder.getCollapsedMessagingLayoutResource() + : mBuilder.getExpandedMessagingLayoutResource(), p, bindResult); if (isConversationLayout) { @@ -10038,7 +10062,7 @@ public class Notification implements Parcelable } /** - * Overrides ContentTitle in the big form of the template. + * Overrides ContentTitle in the expanded form of the template. * This defaults to the value passed to setContentTitle(). */ public InboxStyle setBigContentTitle(CharSequence title) { @@ -10047,7 +10071,7 @@ public class Notification implements Parcelable } /** - * Set the first line of text after the detail section in the big form of the template. + * Set the first line of text after the detail section in the expanded form of the template. */ public InboxStyle setSummaryText(CharSequence cs) { internalSetSummaryText(safeCharSequence(cs)); @@ -10095,9 +10119,9 @@ public class Notification implements Parcelable /** * @hide */ - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .fillTextsFrom(mBuilder).text(null); TemplateBindResult result = new TemplateBindResult(); RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), p, result); @@ -10352,8 +10376,8 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeBigContentView() { - return makeMediaBigContentView(null /* customContent */); + public RemoteViews makeExpandedContentView() { + return makeMediaExpandedContentView(null /* customContent */); } /** @@ -10468,7 +10492,7 @@ public class Notification implements Parcelable .fillTextsFrom(mBuilder); TemplateBindResult result = new TemplateBindResult(); RemoteViews template = mBuilder.applyStandardTemplate( - R.layout.notification_template_material_media, p, + mBuilder.getCollapsedMediaLayoutResource(), p, null /* result */); for (int i = 0; i < MAX_MEDIA_BUTTONS_IN_COMPACT; i++) { @@ -10489,10 +10513,10 @@ public class Notification implements Parcelable } /** @hide */ - protected RemoteViews makeMediaBigContentView(@Nullable RemoteViews customContent) { + protected RemoteViews makeMediaExpandedContentView(@Nullable RemoteViews customContent) { final int actionCount = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS); StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .hideProgress(true) .fillTextsFrom(mBuilder); TemplateBindResult result = new TemplateBindResult(); @@ -10798,8 +10822,8 @@ public class Notification implements Parcelable /** * @hide */ - public RemoteViews makeBigContentView() { - return makeCallLayout(StandardTemplateParams.VIEW_TYPE_BIG); + public RemoteViews makeExpandedContentView() { + return makeCallLayout(StandardTemplateParams.VIEW_TYPE_EXPANDED); } @NonNull @@ -11545,7 +11569,7 @@ public class Notification implements Parcelable .hideProgress(true) .fillTextsFrom(mBuilder); - return getStandardView(mBuilder.getBaseLayoutResource(), p, null /* result */); + return getStandardView(mBuilder.getCollapsedBaseLayoutResource(), p, null /* result */); } /** * @hide @@ -11563,9 +11587,9 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .allowTextWithProgress(true) .hideProgress(true) .fillTextsFrom(mBuilder); @@ -12009,8 +12033,8 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeBigContentView() { - return makeDecoratedBigContentView(); + public RemoteViews makeExpandedContentView() { + return makeDecoratedExpandedContentView(); } /** @@ -12053,13 +12077,13 @@ public class Notification implements Parcelable .decorationType(StandardTemplateParams.DECORATION_PARTIAL) .fillTextsFrom(mBuilder); RemoteViews remoteViews = mBuilder.applyStandardTemplate( - mBuilder.getBaseLayoutResource(), p, result); + mBuilder.getCollapsedBaseLayoutResource(), p, result); buildCustomContentIntoTemplate(mBuilder.mContext, remoteViews, customContent, p, result); return remoteViews; } - private RemoteViews makeDecoratedBigContentView() { + private RemoteViews makeDecoratedExpandedContentView() { RemoteViews bigContentView = mBuilder.mN.bigContentView == null ? mBuilder.mN.contentView : mBuilder.mN.bigContentView; @@ -12068,11 +12092,11 @@ public class Notification implements Parcelable } TemplateBindResult result = new TemplateBindResult(); StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(StandardTemplateParams.VIEW_TYPE_EXPANDED) .decorationType(StandardTemplateParams.DECORATION_PARTIAL) .fillTextsFrom(mBuilder); RemoteViews remoteViews = mBuilder.applyStandardTemplateWithActions( - mBuilder.getBigBaseLayoutResource(), p, result); + mBuilder.getExpandedBaseLayoutResource(), p, result); buildCustomContentIntoTemplate(mBuilder.mContext, remoteViews, bigContentView, p, result); return remoteViews; @@ -12145,11 +12169,11 @@ public class Notification implements Parcelable * @hide */ @Override - public RemoteViews makeBigContentView() { + public RemoteViews makeExpandedContentView() { RemoteViews customContent = mBuilder.mN.bigContentView != null ? mBuilder.mN.bigContentView : mBuilder.mN.contentView; - return makeMediaBigContentView(customContent); + return makeMediaExpandedContentView(customContent); } /** @@ -12160,7 +12184,7 @@ public class Notification implements Parcelable RemoteViews customContent = mBuilder.mN.headsUpContentView != null ? mBuilder.mN.headsUpContentView : mBuilder.mN.contentView; - return makeMediaBigContentView(customContent); + return makeMediaExpandedContentView(customContent); } /** @@ -14486,7 +14510,7 @@ public class Notification implements Parcelable public static int VIEW_TYPE_UNSPECIFIED = 0; public static int VIEW_TYPE_NORMAL = 1; - public static int VIEW_TYPE_BIG = 2; + public static int VIEW_TYPE_EXPANDED = 2; public static int VIEW_TYPE_HEADS_UP = 3; public static int VIEW_TYPE_MINIMIZED = 4; // header only for minimized state public static int VIEW_TYPE_PUBLIC = 5; // header only for automatic public version diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index e218418336c5..3973c58c0708 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -61,6 +61,8 @@ import java.util.Random; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** @@ -680,12 +682,17 @@ public class PropertyInvalidatedCache<Query, Result> { @GuardedBy("mLock") private boolean mTestMode = false; - /** - * The local value of the handler, used during testing but also used directly by the - * NonceLocal handler. - */ + // This is the local value of the nonce, as last set by the NonceHandler. It is always + // updated by the setNonce() operation. The getNonce() operation returns this value in + // NonceLocal handlers and handlers in test mode. + @GuardedBy("mLock") + protected long mShadowNonce = NONCE_UNSET; + + // A list of watchers to be notified of changes. This is null until at least one watcher + // registers. Checking for null is meant to be the fastest way the handler can determine + // that there are no watchers to be notified. @GuardedBy("mLock") - protected long mTestNonce = NONCE_UNSET; + private ArrayList<Semaphore> mWatchers; /** * The methods to get and set a nonce from whatever storage is being used. mLock may be @@ -701,27 +708,60 @@ public class PropertyInvalidatedCache<Query, Result> { /** * Get a nonce from storage. If the handler is in test mode, the nonce is returned from - * the local mTestNonce. + * the local mShadowNonce. */ long getNonce() { synchronized (mLock) { - if (mTestMode) return mTestNonce; + if (mTestMode) return mShadowNonce; } return getNonceInternal(); } /** - * Write a nonce to storage. If the handler is in test mode, the nonce is written to the - * local mTestNonce and storage is not affected. + * Write a nonce to storage. The nonce is always written to the local mShadowNonce. If + * the handler is not in test mode the nonce is also written to storage. */ void setNonce(long val) { synchronized (mLock) { - if (mTestMode) { - mTestNonce = val; - return; + mShadowNonce = val; + if (!mTestMode) { + setNonceInternal(val); + } + wakeAllWatchersLocked(); + } + } + + @GuardedBy("mLock") + private void wakeAllWatchersLocked() { + if (mWatchers != null) { + for (int i = 0; i < mWatchers.size(); i++) { + mWatchers.get(i).release(); + } + } + } + + /** + * Register a watcher to be notified when a nonce changes. There is no check for + * duplicates. In general, this method is called only from {@link NonceWatcher}. + */ + void registerWatcher(Semaphore s) { + synchronized (mLock) { + if (mWatchers == null) { + mWatchers = new ArrayList<>(); + } + mWatchers.add(s); + } + } + + /** + * Unregister a watcher. Nothing happens if the watcher is not registered. + */ + void unregisterWatcher(Semaphore s) { + synchronized (mLock) { + if (mWatchers != null) { + mWatchers.remove(s); } } - setNonceInternal(val); } /** @@ -854,7 +894,7 @@ public class PropertyInvalidatedCache<Query, Result> { void setTestMode(boolean mode) { synchronized (mLock) { mTestMode = mode; - mTestNonce = NONCE_UNSET; + mShadowNonce = NONCE_UNSET; } } @@ -1028,7 +1068,7 @@ public class PropertyInvalidatedCache<Query, Result> { /** * SystemProperties and shared storage are protected and cannot be written by random * processes. So, for testing purposes, the NonceLocal handler stores the nonce locally. The - * NonceLocal uses the mTestNonce in the superclass, regardless of test mode. + * NonceLocal uses the mShadowNonce in the superclass, regardless of test mode. */ private static class NonceLocal extends NonceHandler { // The saved nonce. @@ -1040,16 +1080,130 @@ public class PropertyInvalidatedCache<Query, Result> { @Override long getNonceInternal() { - return mTestNonce; + return mShadowNonce; } @Override void setNonceInternal(long value) { - mTestNonce = value; + mShadowNonce = value; + } + } + + /** + * A NonceWatcher lets an external client test if a nonce value has changed from the last time + * the watcher was checked. + * @hide + */ + public static class NonceWatcher implements AutoCloseable { + // The handler for the key. + private final NonceHandler mHandler; + + // The last-seen value. This is initialized to "unset". + private long mLastSeen = NONCE_UNSET; + + // The semaphore that the watcher waits on. A permit is released every time the nonce + // changes. Permits are acquired in the wait method. + private final Semaphore mSem = new Semaphore(0); + + /** + * Create a watcher for a handler. The last-seen value is not set here and will be + * "unset". Therefore, a call to isChanged() will return true if the nonce has ever been + * set, no matter when the watcher is first created. Clients may want to flush that + * change by calling isChanged() immediately after constructing the object. + */ + private NonceWatcher(@NonNull NonceHandler handler) { + mHandler = handler; + mHandler.registerWatcher(mSem); + } + + /** + * Unregister to be notified when a nonce changes. NonceHandler allows a call to + * unregisterWatcher with a semaphore that is not registered, so there is no check inside + * this method to guard against multiple closures. + */ + @Override + public void close() { + mHandler.unregisterWatcher(mSem); + } + + /** + * Return the last seen value of the nonce. This does not update that value. Only + * {@link #isChanged()} updates the value. + */ + public long lastSeen() { + return mLastSeen; + } + + /** + * Return true if the nonce has changed from the last time isChanged() was called. The + * method is not thread safe. + * @hide + */ + public boolean isChanged() { + long current = mHandler.getNonce(); + if (current != mLastSeen) { + mLastSeen = current; + return true; + } + return false; + } + + /** + * Wait for the nonce value to change. It is not guaranteed that the nonce has changed when + * this returns: clients must confirm with {@link #isChanged}. The wait operation is only + * effective in a process that writes the nonces. The function returns the number of times + * the nonce had changed since the last call to the method. + * @hide + */ + public int waitForChange() throws InterruptedException { + mSem.acquire(1); + return 1 + mSem.drainPermits(); + } + + /** + * Wait for the nonce value to change. It is not guaranteed that the nonce has changed when + * this returns: clients must confirm with {@link #isChanged}. The wait operation is only + * effective in a process that writes the nonces. The function returns the number of times + * the nonce changed since the last call to the method. A return value of zero means the + * timeout expired. Beware that a timeout of 0 means the function will not wait at all. + * @hide + */ + public int waitForChange(long timeout, TimeUnit timeUnit) throws InterruptedException { + if (mSem.tryAcquire(1, timeout, timeUnit)) { + return 1 + mSem.drainPermits(); + } else { + return 0; + } + } + + /** + * Wake the watcher by releasing the semaphore. This can be used to wake clients that are + * blocked in {@link #waitForChange} without affecting the underlying nonce. + * @hide + */ + public void wakeUp() { + mSem.release(); } } /** + * Return a NonceWatcher for the cache. + * @hide + */ + public NonceWatcher getNonceWatcher() { + return new NonceWatcher(mNonce); + } + + /** + * Return a NonceWatcher for the given property. If a handler does not exist for the + * property, one is created. This throws if the property name is not a valid cache key. + * @hide + */ + public static NonceWatcher getNonceWatcher(@NonNull String propertyName) { + return new NonceWatcher(getNonceHandler(propertyName)); + } + + /** * Complete key prefixes. */ private static final String PREFIX_TEST = CACHE_KEY_PREFIX + "." + MODULE_TEST + "."; @@ -1663,6 +1817,26 @@ public class PropertyInvalidatedCache<Query, Result> { } /** + * Non-static version of corkInvalidations() for situations in which the cache instance is + * available. This is slightly faster than than the static versions because it does not have + * to look up the NonceHandler for a given property name. + * @hide + */ + public void corkInvalidations() { + mNonce.cork(); + } + + /** + * Non-static version of uncorkInvalidations() for situations in which the cache instance is + * available. This is slightly faster than than the static versions because it does not have + * to look up the NonceHandler for a given property name. + * @hide + */ + public void uncorkInvalidations() { + mNonce.uncork(); + } + + /** * Invalidate caches in all processes that are keyed for the module and api. * @hide */ diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index 36f61fd3ef59..b9b582a7d660 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -198,6 +198,10 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override + public void onRecentTaskRemovedForAddTask(int taskId) { + } + + @Override public void onTaskFocusChanged(int taskId, boolean focused) { } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e766ae2fce0d..74d729857cc8 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4177,6 +4177,35 @@ public class DevicePolicyManager { } } + + /** + * Similar to the public variant of {@link #setMtePolicy} but for use by the system. + * + * <p>Called by a system service only, meaning that the caller's UID must be equal to + * {@link Process#SYSTEM_UID}. + * + * @throws SecurityException if caller is not permitted to set Mte policy + * @throws UnsupportedOperationException if the device does not support MTE + * @param systemEntity The service entity that adds the restriction. A user restriction set by + * a service entity can only be cleared by the same entity. This can be + * just the calling package name, or any string of the caller's choice + * can be used. + * @param policy the MTE policy to be set + * @hide + */ + public void setMtePolicy(@NonNull String systemEntity, @MtePolicy int policy) { + throwIfParentInstance("setMtePolicyForUser"); + if (mService != null) { + try { + mService.setMtePolicyBySystem(systemEntity, policy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + + /** * Called by a device owner, profile owner of an organization-owned device to * get the Memory Tagging Extension (MTE) policy @@ -14453,7 +14482,7 @@ public class DevicePolicyManager { * </ul> * <p> * The following methods are supported for the parent instance but can only be called by the - * profile owner of a managed profile that was created during the device provisioning flow: + * profile owner on an <a href="#organization-owned">organization owned</a> managed profile: * <ul> * <li>{@link #getPasswordComplexity}</li> * <li>{@link #setCameraDisabled}</li> @@ -14461,11 +14490,6 @@ public class DevicePolicyManager { * <li>{@link #setAccountManagementDisabled(ComponentName, String, boolean)}</li> * <li>{@link #setPermittedInputMethods}</li> * <li>{@link #getPermittedInputMethods}</li> - * </ul> - * - * <p>The following methods can be called by the profile owner of a managed profile - * on an organization-owned device: - * <ul> * <li>{@link #wipeData}</li> * </ul> * @@ -18177,4 +18201,4 @@ public class DevicePolicyManager { } return HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED; } -}
\ No newline at end of file +} diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index d048b5371fc4..e7be822d52d3 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -617,6 +617,7 @@ interface IDevicePolicyManager { int[] getApplicationExemptions(String packageName); void setMtePolicy(int flag, String callerPackageName); + void setMtePolicyBySystem(in String systemEntity, int policy); int getMtePolicy(String callerPackageName); void setManagedSubscriptionsPolicy(in ManagedSubscriptionsPolicy policy); diff --git a/core/java/android/app/background_install_control_manager.aconfig b/core/java/android/app/background_install_control_manager.aconfig index d29c5b58092d..39f10dc0647f 100644 --- a/core/java/android/app/background_install_control_manager.aconfig +++ b/core/java/android/app/background_install_control_manager.aconfig @@ -9,3 +9,10 @@ flag { is_fixed_read_only: true bug: "287507984" } + +flag { + name: "background_install_control_callback_api" + namespace: "preload_safety" + description: "Feature flag to enable the use of push API in background install control service" + bug: "369382811" +}
\ No newline at end of file diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java index f0763752e054..5b14b038f130 100644 --- a/core/java/android/app/wearable/WearableSensingManager.java +++ b/core/java/android/app/wearable/WearableSensingManager.java @@ -55,6 +55,7 @@ import java.io.OutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Map; +import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -248,7 +249,11 @@ public class WearableSensingManager { * @param executor Executor on which to run the consumer callback * @param statusConsumer A consumer that handles the status codes for providing the connection * and errors in the encrypted channel. + * @deprecated Use {@link #provideConnection(WearableConnection, Executor)} instead to provide a + * remote wearable device connection to the WearableSensingService */ + @FlaggedApi(Flags.FLAG_ENABLE_CONCURRENT_WEARABLE_CONNECTIONS) + @Deprecated @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideConnection( @NonNull ParcelFileDescriptor wearableConnection, @@ -348,9 +353,7 @@ public class WearableSensingManager { wearableConnection.getMetadata(), createWearableSensingCallback(executor), statusCallback); - if (connectionId != CONNECTION_ID_INVALID) { - mWearableConnectionIdMap.put(wearableConnection, connectionId); - } + mWearableConnectionIdMap.put(wearableConnection, connectionId); // For invalid connection IDs, the status callback will remove the connection from // mWearableConnectionIdMap } catch (RemoteException e) { @@ -367,36 +370,37 @@ public class WearableSensingManager { * <p>After this method returns, there will be no new invocation to callback methods in the * removed {@link WearableConnection}. Ongoing invocations will continue to run. * - * <p>This method does nothing if the provided {@code wearableConnection} does not match any - * open connection. + * <p>This method throws a {@link NoSuchElementException} if the provided {@code + * wearableConnection} does not match any open connection. * * <p>This method should not be called before the corresponding {@link * #provideConnection(WearableConnection, Executor)} invocation returns. Otherwise, the - * connection may not be removed. + * connection may not be removed, and an {@link IllegalStateException} may be thrown. * * @param wearableConnection The WearableConnection instance previously provided to {@link * #provideConnection(WearableConnection, Executor)}. - * @return true if a concurrent connection quota has been freed due to this method invocation. - * Returns false otherwise. + * @throws NoSuchElementException if the connection was never provided or was already removed. + * @throws IllegalStateException if the {@link #provideConnection(WearableConnection, Executor)} + * invocation for the given connection has not returned. */ @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) @FlaggedApi(Flags.FLAG_ENABLE_CONCURRENT_WEARABLE_CONNECTIONS) - public boolean removeConnection(@NonNull WearableConnection wearableConnection) { - int connectionId = - mWearableConnectionIdMap.getOrDefault(wearableConnection, CONNECTION_ID_INVALID); - if (connectionId == CONNECTION_ID_INVALID) { - return false; + public void removeConnection(@NonNull WearableConnection wearableConnection) { + Integer connectionId = mWearableConnectionIdMap.remove(wearableConnection); + if (connectionId == null || connectionId == CONNECTION_ID_INVALID) { + throw new NoSuchElementException( + "The provided connection was never provided or was already removed."); } if (connectionId == CONNECTION_ID_PLACEHOLDER) { - Slog.w( - TAG, + throw new IllegalStateException( "Attempt to remove connection before provideConnection returns. The connection" + " will not be removed."); - return false; } - mWearableConnectionIdMap.remove(wearableConnection); try { - return mService.removeConnection(connectionId); + if (!mService.removeConnection(connectionId)) { + throw new NoSuchElementException( + "The provided connection was never provided or was already removed."); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 8f20ea034cf6..40de2985f68a 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -31,6 +31,7 @@ import android.annotation.UiThread; import android.annotation.UserIdInt; import android.app.IServiceConnection; import android.app.PendingIntent; +import android.app.usage.UsageStatsManager; import android.appwidget.flags.Flags; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -41,12 +42,14 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutInfo; +import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.Looper; +import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; @@ -485,6 +488,67 @@ public class AppWidgetManager { public static final String ACTION_APPWIDGET_HOST_RESTORED = "android.appwidget.action.APPWIDGET_HOST_RESTORED"; + /** + * This is the value of {@link UsageStatsManager.EXTRA_EVENT_ACTION} in the event bundle for + * widget user interaction events. + * + * A single widget interaction event describes what user interactions happened during a single + * impression of the widget. + */ + @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS) + public static final String EVENT_TYPE_WIDGET_INTERACTION = "widget_interaction"; + + /** + * This is the value of {@link UsageStatsManager.EXTRA_EVENT_CATEGORY} in the event bundle for + * widget user interaction events. + */ + @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS) + public static final String EVENT_CATEGORY_APPWIDGET = "android.appwidget"; + + /** + * This bundle extra describes which views have been clicked during a single impression of the + * widget. It is an integer array of view IDs of the clicked views. + * + * Widget providers may set a different ID for event purposes by setting the + * {@link android.R.id.remoteViewsMetricsId} int tag on the view. + * + * @see android.views.RemoteViews.setIntTag + */ + @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS) + public static final String EXTRA_EVENT_CLICKED_VIEWS = + "android.appwidget.extra.EVENT_CLICKED_VIEWS"; + + /** + * This bundle extra describes which views have been scrolled during a single impression of the + * widget. It is an integer array of view IDs of the scrolled views. + * + * Widget providers may set a different ID for event purposes by setting the + * {@link android.R.id.remoteViewsMetricsId} int tag on the view. + * + * @see android.views.RemoteViews.setIntTag + */ + @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS) + public static final String EXTRA_EVENT_SCROLLED_VIEWS = + "android.appwidget.extra.EVENT_SCROLLED_VIEWS"; + + /** + * This bundle extra contains a long that represents the duration of time in milliseconds + * during which the widget was visible. + */ + @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS) + public static final String EXTRA_EVENT_DURATION_MS = + "android.appwidget.extra.EVENT_DURATION_MS"; + + /** + * This bundle extra contains an integer array with 4 elements that describe the left, top, + * right, and bottom coordinates of the widget at the end of the interaction event. + * + * This Rect indicates the current position and size of the widget. + */ + @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS) + public static final String EXTRA_EVENT_POSITION_RECT = + "android.appwidget.extra.EVENT_POSITION_RECT"; + private static final String TAG = "AppWidgetManager"; private static Executor sUpdateExecutor; @@ -1516,6 +1580,39 @@ public class AppWidgetManager { } } + /** + * Create a {@link PersistableBundle} that represents a single widget interaction event. + * + * @param appWidgetId App Widget ID of the widget. + * @param durationMs Duration of the impression in milliseconds + * @param position Current position of the widget. + * @param clickedIds IDs of views clicked during this event. + * @param scrolledIds IDs of views scrolled during this event. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_ENGAGEMENT_METRICS) + @NonNull + public static PersistableBundle createWidgetInteractionEvent(int appWidgetId, long durationMs, + @Nullable Rect position, @Nullable int[] clickedIds, @Nullable int[] scrolledIds) { + PersistableBundle extras = new PersistableBundle(); + extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, EVENT_TYPE_WIDGET_INTERACTION); + extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, EVENT_CATEGORY_APPWIDGET); + extras.putInt(EXTRA_APPWIDGET_ID, appWidgetId); + extras.putLong(EXTRA_EVENT_DURATION_MS, durationMs); + if (position != null) { + extras.putIntArray(EXTRA_EVENT_POSITION_RECT, + new int[]{position.left, position.top, position.right, position.bottom}); + } + if (clickedIds != null && clickedIds.length > 0) { + extras.putIntArray(EXTRA_EVENT_CLICKED_VIEWS, clickedIds); + } + if (scrolledIds != null && scrolledIds.length > 0) { + extras.putIntArray(EXTRA_EVENT_SCROLLED_VIEWS, scrolledIds); + } + return extras; + } + @UiThread private static @NonNull Executor createUpdateExecutorIfNull() { diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig index 4499a723cfb8..17bcdb013673 100644 --- a/core/java/android/appwidget/flags.aconfig +++ b/core/java/android/appwidget/flags.aconfig @@ -105,3 +105,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "engagement_metrics" + namespace: "app_widgets" + description: "Enable collection of widget engagement metrics" + bug: "364655296" +} diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java index 5ad2348254e2..db080fcc7702 100644 --- a/core/java/android/companion/CompanionDeviceService.java +++ b/core/java/android/companion/CompanionDeviceService.java @@ -249,7 +249,7 @@ public abstract class CompanionDeviceService extends Service { // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut. /** - * Called by system whenever a device associated with this app is connected. + * Called by the system when an associated device is nearby or connected. * * @param associationInfo A record for the companion device. */ @@ -262,7 +262,7 @@ public abstract class CompanionDeviceService extends Service { // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut. /** - * Called by system whenever a device associated with this app is disconnected. + * Called by the system when an associated device is out of range or disconnected. * * @param associationInfo A record for the companion device. */ @@ -274,7 +274,7 @@ public abstract class CompanionDeviceService extends Service { } /** - * Called by the system during device events. + * Called by the system when an associated device's presence state changes. * * @see CompanionDeviceManager#startObservingDevicePresence(ObservingDevicePresenceRequest) */ diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index d3a1c25b74d5..367f1afc912b 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -90,11 +90,6 @@ interface IVirtualDevice { */ boolean hasCustomAudioInputSupport(); - /** - * Returns whether this device is allowed to create mirror displays. - */ - boolean canCreateMirrorDisplays(); - /* * Turns off all trusted non-mirror displays of the virtual device. */ diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig index 3e6919bac5fa..46da4a3d99bc 100644 --- a/core/java/android/companion/virtual/flags.aconfig +++ b/core/java/android/companion/virtual/flags.aconfig @@ -81,14 +81,3 @@ flag { description: "Enable virtual stylus input" bug: "304829446" } - -flag { - name: "impulse_velocity_strategy_for_touch_navigation" - is_exported: true - namespace: "virtual_devices" - description: "Use impulse velocity strategy during conversion of touch navigation flings into Dpad events" - bug: "338426241" - metadata { - purpose: PURPOSE_BUGFIX - } -}
\ No newline at end of file diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index acad92c90c59..6e2ca2c109f1 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4250,6 +4250,7 @@ public abstract class Context { //@hide: WIFI_RTT_SERVICE, //@hide: ETHERNET_SERVICE, WIFI_RTT_RANGING_SERVICE, + WIFI_USD_SERVICE, NSD_SERVICE, AUDIO_SERVICE, AUDIO_DEVICE_VOLUME_SERVICE, @@ -5096,6 +5097,19 @@ public abstract class Context { */ public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.wifi.usd.UsdManager} for Unsynchronized Service Discovery (USD) operation. + * + * @see #getSystemService(String) + * @see android.net.wifi.usd.UsdManager + * @hide + */ + @FlaggedApi(android.net.wifi.flags.Flags.FLAG_USD) + @SystemApi + public static final String WIFI_USD_SERVICE = "wifi_usd"; + /** * Use with {@link #getSystemService(String)} to retrieve a {@link * android.net.lowpan.LowpanManager} for handling management of diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 3d2d487b9d06..02eed1a7553f 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -7720,6 +7720,7 @@ public class Intent implements Parcelable, Cloneable { @IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = { EXTENDED_FLAG_FILTER_MISMATCH, EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN, + EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED, }) @Retention(RetentionPolicy.SOURCE) public @interface ExtendedFlags {} @@ -7740,6 +7741,13 @@ public class Intent implements Parcelable, Cloneable { */ public static final int EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN = 1 << 1; + /** + * This flag indicates this intent called {@link #collectExtraIntentKeys()}. + * + * @hide + */ + public static final int EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED = 1 << 2; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // toUri() and parseUri() options. @@ -12328,7 +12336,8 @@ public class Intent implements Parcelable, Cloneable { } private void collectNestedIntentKeysRecur(Set<Intent> visited) { - if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) { + addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED); + if (mExtras != null && !mExtras.isEmpty()) { for (String key : mExtras.keySet()) { Object value = mExtras.get(key); diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index b10f5e4fe66c..37f3f17ebe42 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -453,6 +453,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * Value of {@link #colorMode} indicating that the activity should use a * high dynamic range if the presentation display supports it. * + * <p>Note: This does not impact SurfaceViews or SurfaceControls, as those have their own + * independent HDR support.</p> + * + * <p><b>Important:</b> Although this value was added in API 26, it is strongly recommended + * to avoid using it until API 34 which is when HDR support for the UI toolkit was officially + * added.</p> + * * @see android.R.attr#colorMode */ public static final int COLOR_MODE_HDR = 2; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 45303e6043bc..cd62573f6e4e 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -218,6 +218,17 @@ public class PackageInstaller { "android.content.pm.action.CONFIRM_PRE_APPROVAL"; /** + * Intent action to be sent to the implementer of + * {@link android.content.pm.dependencyinstaller.DependencyInstallerService}. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER) + @SystemApi + public static final String ACTION_INSTALL_DEPENDENCY = + "android.content.pm.action.INSTALL_DEPENDENCY"; + + /** * An integer session ID that an operation is working with. * * @see Intent#getIntExtra(String, int) @@ -3614,7 +3625,7 @@ public class PackageInstaller { * dependencies aren't already installed. */ @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER) - public void setEnableAutoInstallDependencies(boolean enableAutoInstallDependencies) { + public void setAutoInstallDependenciesEnabled(boolean enableAutoInstallDependencies) { isAutoInstallDependenciesEnabled = enableAutoInstallDependencies; } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index d2b43b9bd2b4..b85111fca703 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -6729,6 +6729,11 @@ public abstract class PackageManager { * If the given permission already exists, the info you supply here * will be used to update it. * + * @deprecated Support for dynamic permissions is going to be removed, and apps that use dynamic + * permissions should declare their permissions statically inside their app manifest instead. + * This method will become a no-op in a future Android release and eventually be removed from + * the SDK. + * * @param info Description of the permission to be added. * * @return Returns true if a new permission was created, false if an @@ -6739,7 +6744,9 @@ public abstract class PackageManager { * * @see #removePermission(String) */ - //@Deprecated + @SuppressWarnings("HiddenAbstractMethod") + @FlaggedApi(android.permission.flags.Flags.FLAG_PERMISSION_TREE_APIS_DEPRECATED) + @Deprecated public abstract boolean addPermission(@NonNull PermissionInfo info); /** @@ -6748,8 +6755,15 @@ public abstract class PackageManager { * allowing it to return quicker and batch a series of adds at the * expense of no guarantee the added permission will be retained if * the device is rebooted before it is written. + * + * @deprecated Support for dynamic permissions is going to be removed, and apps that use dynamic + * permissions should declare their permissions statically inside their app manifest instead. + * This method will become a no-op in a future Android release and eventually be removed from + * the SDK. */ - //@Deprecated + @SuppressWarnings("HiddenAbstractMethod") + @FlaggedApi(android.permission.flags.Flags.FLAG_PERMISSION_TREE_APIS_DEPRECATED) + @Deprecated public abstract boolean addPermissionAsync(@NonNull PermissionInfo info); /** @@ -6758,6 +6772,11 @@ public abstract class PackageManager { * -- you are only allowed to remove permissions that you are allowed * to add. * + * @deprecated Support for dynamic permissions is going to be removed, and apps that use dynamic + * permissions should declare their permissions statically inside their app manifest instead. + * This method will become a no-op in a future Android release and eventually be removed from + * the SDK. + * * @param permName The name of the permission to remove. * * @throws SecurityException if you are not allowed to remove the @@ -6765,7 +6784,9 @@ public abstract class PackageManager { * * @see #addPermission(PermissionInfo) */ - //@Deprecated + @SuppressWarnings("HiddenAbstractMethod") + @FlaggedApi(android.permission.flags.Flags.FLAG_PERMISSION_TREE_APIS_DEPRECATED) + @Deprecated public abstract void removePermission(@NonNull String permName); /** diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 813208d7ff38..833260a15c45 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -341,6 +341,28 @@ flag { is_fixed_read_only: true } +flag { + name: "cache_user_start_realtime_read_only" + namespace: "multiuser" + description: "Cache getUserStartRealtime to avoid unnecessary binder calls" + bug: "350416205" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true +} + +flag { + name: "cache_user_unlock_realtime_read_only" + namespace: "multiuser" + description: "Cache getUserUnlockRealtime to avoid unnecessary binder calls" + bug: "350421407" + metadata { + purpose: PURPOSE_BUGFIX + } + is_fixed_read_only: true +} + # This flag guards the private space feature and all its implementations excluding the APIs. APIs are guarded by android.os.Flags.allow_private_profile. flag { name: "enable_private_space_features" diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index 72542036232c..18a45d8d442e 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -595,9 +595,6 @@ public class ApkLiteParseUtils { /*allowDuplicates=*/ true); break; case TAG_USES_STATIC_LIBRARY: - if (!android.content.pm.Flags.sdkDependencyInstaller()) { - break; - } String usesStaticLibName = parser.getAttributeValue( ANDROID_RES_NAMESPACE, "name"); long usesStaticLibVersion = parser.getAttributeIntValue( @@ -666,7 +663,7 @@ public class ApkLiteParseUtils { SharedLibraryInfo.TYPE_SDK_PACKAGE)); break; case TAG_STATIC_LIBRARY: - isSdkLibrary = true; + isStaticLibrary = true; // Mirrors ParsingPackageUtils#parseStaticLibrary until lite and full // parsing are combined String staticLibName = parser.getAttributeValue( diff --git a/core/java/android/hardware/DisplayLuts.java b/core/java/android/hardware/DisplayLuts.java index 6343ba19f569..0abb30f8c24d 100644 --- a/core/java/android/hardware/DisplayLuts.java +++ b/core/java/android/hardware/DisplayLuts.java @@ -177,6 +177,8 @@ public final class DisplayLuts { return "SAMPLING_KEY_RGB"; case LutProperties.SAMPLING_KEY_MAX_RGB: return "SAMPLING_KEY_MAX_RGB"; + case LutProperties.SAMPLING_KEY_CIE_Y: + return "SAMPLING_KEY_CIE_Y"; default: return ""; } diff --git a/core/java/android/hardware/LutProperties.java b/core/java/android/hardware/LutProperties.java index bf40a415b0f7..abb303adac15 100644 --- a/core/java/android/hardware/LutProperties.java +++ b/core/java/android/hardware/LutProperties.java @@ -44,7 +44,8 @@ public final class LutProperties { @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"SAMPLING_KEY_"}, value = { SAMPLING_KEY_RGB, - SAMPLING_KEY_MAX_RGB + SAMPLING_KEY_MAX_RGB, + SAMPLING_KEY_CIE_Y }) public @interface SamplingKey { } @@ -57,6 +58,10 @@ public final class LutProperties { @FlaggedApi(Flags.FLAG_LUTS_API) public static final int SAMPLING_KEY_MAX_RGB = 1; + /** use y of CIE XYZ as the gain value of a lut */ + @FlaggedApi(Flags.FLAG_LUTS_API) + public static final int SAMPLING_KEY_CIE_Y = 2; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index 3cf508a6db00..58fe4774f178 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -1066,6 +1066,7 @@ public final class CameraExtensionCharacteristics { case ImageFormat.YUV_420_888: case ImageFormat.JPEG: case ImageFormat.JPEG_R: + case ImageFormat.DEPTH_JPEG: case ImageFormat.YCBCR_P010: break; default: @@ -1096,9 +1097,10 @@ public final class CameraExtensionCharacteristics { // processed YUV_420 buffers. return getSupportedSizes( extenders.second.getSupportedPostviewResolutions(sz), format); - } else if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010) { - // Jpeg_R/UltraHDR + YCBCR_P010 is currently not supported in the basic - // extension case + } else if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010 || + (Flags.depthJpegExtensions() && (format == ImageFormat.DEPTH_JPEG))) { + // DepthJpeg/Jpeg_R/UltraHDR + YCBCR_P010 is currently not supported in the + // basic extension case return new ArrayList<>(); } else { throw new IllegalArgumentException("Unsupported format: " + format); @@ -1194,8 +1196,8 @@ public final class CameraExtensionCharacteristics { * * <p>Device-specific extensions currently support at most three * multi-frame capture surface formats. ImageFormat.JPEG will be supported by all - * extensions while ImageFormat.YUV_420_888, ImageFormat.JPEG_R, or ImageFormat.YCBCR_P010 - * may or may not be supported.</p> + * extensions while ImageFormat.YUV_420_888, ImageFormat.JPEG_R, ImageFormat.YCBCR_P010 or + * ImageFormat.DEPTH_JPEG may or may not be supported.</p> * * @param extension the extension type * @param format device-specific extension output format @@ -1203,7 +1205,8 @@ public final class CameraExtensionCharacteristics { * supported. * @throws IllegalArgumentException in case of format different from ImageFormat.JPEG, * ImageFormat.YUV_420_888, ImageFormat.JPEG_R, - * ImageFormat.YCBCR_P010; or unsupported extension. + * ImageFormat.DEPTH_JPEG, ImageFormat.YCBCR_P010; or + * unsupported extension. */ public @NonNull List<Size> getExtensionSupportedSizes(@Extension int extension, int format) { @@ -1227,6 +1230,7 @@ public final class CameraExtensionCharacteristics { case ImageFormat.YUV_420_888: case ImageFormat.JPEG: case ImageFormat.JPEG_R: + case ImageFormat.DEPTH_JPEG: case ImageFormat.YCBCR_P010: break; default: @@ -1260,8 +1264,9 @@ public final class CameraExtensionCharacteristics { } else { return generateSupportedSizes(null, format, streamMap); } - } else if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010) { - // Jpeg_R/UltraHDR + YCBCR_P010 is currently not supported in the + } else if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010 || + (Flags.depthJpegExtensions() && (format == ImageFormat.DEPTH_JPEG))) { + // DepthJpeg/Jpeg_R/UltraHDR + YCBCR_P010 is currently not supported in the // basic extension case return new ArrayList<>(); } else { @@ -1292,7 +1297,8 @@ public final class CameraExtensionCharacteristics { * or null if no capture latency info can be provided * @throws IllegalArgumentException in case of format different from {@link ImageFormat#JPEG}, * {@link ImageFormat#YUV_420_888}, {@link ImageFormat#JPEG_R} - * {@link ImageFormat#YCBCR_P010}; + * {@link ImageFormat#YCBCR_P010}, + * {@link ImageFormat#DEPTH_JPEG}; * or unsupported extension. */ public @Nullable Range<Long> getEstimatedCaptureLatencyRangeMillis(@Extension int extension, @@ -1301,6 +1307,7 @@ public final class CameraExtensionCharacteristics { case ImageFormat.YUV_420_888: case ImageFormat.JPEG: case ImageFormat.JPEG_R: + case ImageFormat.DEPTH_JPEG: case ImageFormat.YCBCR_P010: //No op break; @@ -1349,8 +1356,9 @@ public final class CameraExtensionCharacteristics { // specific and cannot be estimated accurately enough. return null; } - if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010) { - // JpegR/UltraHDR + YCBCR_P010 is not supported for basic extensions + if (format == ImageFormat.JPEG_R || format == ImageFormat.YCBCR_P010 || + (Flags.depthJpegExtensions() && (format == ImageFormat.DEPTH_JPEG))) { + // DepthJpeg/JpegR/UltraHDR + YCBCR_P010 is not supported for basic extensions return null; } diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index fc03b517e6d4..d511e9f64c17 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -186,12 +186,12 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes HashMap<Integer, List<Size>> supportedCaptureSizes = new HashMap<>(); - IntArray supportedCaptureOutputFormats = - new IntArray(CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.length); - supportedCaptureOutputFormats.addAll( - CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS); - supportedCaptureOutputFormats.add(ImageFormat.YCBCR_P010); - for (int format : supportedCaptureOutputFormats.toArray()) { + Integer[] supportedCaptureOutputFormats = + new Integer[CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.size()]; + supportedCaptureOutputFormats = + CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.toArray( + supportedCaptureOutputFormats); + for (int format : supportedCaptureOutputFormats) { List<Size> supportedSizes = extensionChars.getExtensionSupportedSizes( config.getExtension(), format); if (supportedSizes != null) { @@ -230,7 +230,7 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes Size burstCaptureSurfaceSize = new Size(burstCaptureSurfaceInfo.mWidth, burstCaptureSurfaceInfo.mHeight); HashMap<Integer, List<Size>> supportedPostviewSizes = new HashMap<>(); - for (int format : supportedCaptureOutputFormats.toArray()) { + for (int format : supportedCaptureOutputFormats) { List<Size> supportedSizesPostview = extensionChars.getPostviewSupportedSizes( config.getExtension(), burstCaptureSurfaceSize, format); if (supportedSizesPostview != null) { diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index ce1609dec4e6..ed73e624e242 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -186,12 +186,12 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } HashMap<Integer, List<Size>> supportedCaptureSizes = new HashMap<>(); - IntArray supportedCaptureOutputFormats = - new IntArray(CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.length); - supportedCaptureOutputFormats.addAll( - CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS); - supportedCaptureOutputFormats.add(ImageFormat.YCBCR_P010); - for (int format : supportedCaptureOutputFormats.toArray()) { + Integer[] supportedCaptureOutputFormats = + new Integer[CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.size()]; + supportedCaptureOutputFormats = + CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.toArray( + supportedCaptureOutputFormats); + for (int format : supportedCaptureOutputFormats) { List<Size> supportedSizes = extensionChars.getExtensionSupportedSizes( config.getExtension(), format); if (supportedSizes != null) { @@ -223,7 +223,7 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { Size burstCaptureSurfaceSize = new Size(burstCaptureSurfaceInfo.mWidth, burstCaptureSurfaceInfo.mHeight); HashMap<Integer, List<Size>> supportedPostviewSizes = new HashMap<>(); - for (int format : supportedCaptureOutputFormats.toArray()) { + for (int format : supportedCaptureOutputFormats) { List<Size> supportedSizesPostview = extensionChars.getPostviewSupportedSizes( config.getExtension(), burstCaptureSurfaceSize, format); if (supportedSizesPostview != null) { diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java index f91d277d571f..212c909bcbba 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java @@ -32,11 +32,14 @@ import android.os.Handler; import android.util.IntArray; import android.util.Log; import android.util.Size; +import android.util.SparseIntArray; import android.view.Surface; import com.android.internal.camera.flags.Flags; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; @@ -48,11 +51,16 @@ public final class CameraExtensionUtils { public final static int JPEG_DEFAULT_QUALITY = 100; public final static int JPEG_DEFAULT_ROTATION = 0; - public static final int[] SUPPORTED_CAPTURE_OUTPUT_FORMATS = { - CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT, - ImageFormat.JPEG, - ImageFormat.JPEG_R - }; + public static HashSet<Integer> SUPPORTED_CAPTURE_OUTPUT_FORMATS = new HashSet<>(); + + static { + SUPPORTED_CAPTURE_OUTPUT_FORMATS.addAll(Arrays.asList( + CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT, ImageFormat.JPEG, + ImageFormat.YCBCR_P010, ImageFormat.JPEG_R )); + if (Flags.depthJpegExtensions()) { + SUPPORTED_CAPTURE_OUTPUT_FORMATS.add(ImageFormat.DEPTH_JPEG); + } + } public static class SurfaceInfo { public int mWidth = 0; @@ -101,6 +109,13 @@ public final class CameraExtensionUtils { surfaceInfo.mFormat = ImageFormat.JPEG_R; return surfaceInfo; } + if (Flags.depthJpegExtensions()) { + if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) + && (dataspace == StreamConfigurationMap.HAL_DATASPACE_DYNAMIC_DEPTH)) { + surfaceInfo.mFormat = ImageFormat.DEPTH_JPEG; + return surfaceInfo; + } + } return surfaceInfo; } @@ -125,14 +140,14 @@ public final class CameraExtensionUtils { public static Surface getBurstCaptureSurface( @NonNull List<OutputConfiguration> outputConfigs, @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) { - IntArray supportedCaptureOutputFormats = - new IntArray(CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.length); - supportedCaptureOutputFormats.addAll( - CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS); - supportedCaptureOutputFormats.add(ImageFormat.YCBCR_P010); + Integer[] supportedCaptureOutputFormats = + new Integer[CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.size()]; + supportedCaptureOutputFormats = + CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS.toArray( + supportedCaptureOutputFormats); for (OutputConfiguration config : outputConfigs) { SurfaceInfo surfaceInfo = querySurface(config.getSurface()); - for (int supportedFormat : supportedCaptureOutputFormats.toArray()) { + for (int supportedFormat : supportedCaptureOutputFormats) { if (surfaceInfo.mFormat == supportedFormat) { Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight); if (supportedCaptureSizes.containsKey(supportedFormat)) { diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java index 078b4d4629e0..7efdd6dbdf41 100644 --- a/core/java/android/hardware/contexthub/HubEndpoint.java +++ b/core/java/android/hardware/contexthub/HubEndpoint.java @@ -18,6 +18,7 @@ package android.hardware.contexthub; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -31,6 +32,8 @@ import android.util.SparseArray; import androidx.annotation.GuardedBy; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -48,6 +51,46 @@ import java.util.concurrent.Executor; public class HubEndpoint { private static final String TAG = "HubEndpoint"; + /** + * Constants describing the outcome of operations through HubEndpoints (like opening/closing of + * sessions or stopping of endpoints). + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = {"REASON_"}, + value = { + REASON_FAILURE, + REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED, + REASON_CLOSE_ENDPOINT_SESSION_REQUESTED, + REASON_ENDPOINT_INVALID, + REASON_ENDPOINT_STOPPED, + }) + public @interface Reason {} + + /** Unclassified failure */ + public static final int REASON_FAILURE = 0; + + // The values 1 and 2 are reserved at the Context Hub HAL but not exposed to apps. + + /** The peer rejected the request to open this endpoint session. */ + public static final int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; + + /** The peer closed this endpoint session. */ + public static final int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4; + + /** The peer endpoint is invalid. */ + public static final int REASON_ENDPOINT_INVALID = 5; + + /** + * The endpoint is now stopped. The app should retrieve the endpoint info using {@link + * android.hardware.location.ContextHubManager#findEndpoints} or register updates through + * {@link android.hardware.location.ContextHubManager#registerEndpointDiscoveryCallback} + * to get notified if the endpoint restarts. + */ + public static final int REASON_ENDPOINT_STOPPED = 6; + private final Object mLock = new Object(); private final HubEndpointInfo mPendingHubEndpointInfo; @Nullable private final IHubEndpointLifecycleCallback mLifecycleCallback; @@ -173,9 +216,7 @@ public class HubEndpoint { try { mServiceToken.closeSession( - sessionId, - IHubEndpointLifecycleCallback - .REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED); + sessionId, REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED); } catch (RemoteException e) { e.rethrowFromSystemServer(); } @@ -396,9 +437,7 @@ public class HubEndpoint { try { // Oneway notification to system service - serviceToken.closeSession( - session.getId(), - IHubEndpointLifecycleCallback.REASON_CLOSE_ENDPOINT_SESSION_REQUESTED); + serviceToken.closeSession(session.getId(), REASON_CLOSE_ENDPOINT_SESSION_REQUESTED); } catch (RemoteException e) { Log.e(TAG, "closeSession: failed to close session " + session, e); e.rethrowFromSystemServer(); diff --git a/core/java/android/hardware/contexthub/HubServiceInfo.java b/core/java/android/hardware/contexthub/HubServiceInfo.java index c7fe77c4a0f1..a1c52fb5864f 100644 --- a/core/java/android/hardware/contexthub/HubServiceInfo.java +++ b/core/java/android/hardware/contexthub/HubServiceInfo.java @@ -17,12 +17,10 @@ package android.hardware.contexthub; import android.annotation.FlaggedApi; import android.annotation.IntDef; -import android.annotation.Nullable; import android.annotation.SystemApi; import android.chre.flags.Flags; import android.os.Parcel; import android.os.Parcelable; -import android.os.ParcelableHolder; import androidx.annotation.NonNull; @@ -76,15 +74,12 @@ public final class HubServiceInfo implements Parcelable { private final int mMajorVersion; private final int mMinorVersion; - @NonNull private final ParcelableHolder mExtendedInfo; - /** @hide */ public HubServiceInfo(android.hardware.contexthub.Service service) { mServiceDescriptor = service.serviceDescriptor; mFormat = service.format; mMajorVersion = service.majorVersion; mMinorVersion = service.minorVersion; - mExtendedInfo = service.extendedInfo; } private HubServiceInfo(Parcel in) { @@ -92,20 +87,17 @@ public final class HubServiceInfo implements Parcelable { mFormat = in.readInt(); mMajorVersion = in.readInt(); mMinorVersion = in.readInt(); - mExtendedInfo = ParcelableHolder.CREATOR.createFromParcel(in); } public HubServiceInfo( @NonNull String serviceDescriptor, @ServiceFormat int format, int majorVersion, - int minorVersion, - @NonNull ParcelableHolder extendedInfo) { + int minorVersion) { mServiceDescriptor = serviceDescriptor; mFormat = format; mMajorVersion = majorVersion; mMinorVersion = minorVersion; - mExtendedInfo = extendedInfo; } /** Get the unique identifier of this service. See {@link Builder} for more information. */ @@ -134,17 +126,10 @@ public final class HubServiceInfo implements Parcelable { return mMinorVersion; } - /** Get the {@link ParcelableHolder} for the extended information about the service. */ - @NonNull - public ParcelableHolder getExtendedInfo() { - return mExtendedInfo; - } - /** Parcel implementation details */ @Override public int describeContents() { - // Passthrough describeContents flags for mExtendedInfo because we don't have FD otherwise. - return mExtendedInfo.describeContents(); + return 0; } /** Parcel implementation details */ @@ -154,7 +139,6 @@ public final class HubServiceInfo implements Parcelable { dest.writeInt(mFormat); dest.writeInt(mMajorVersion); dest.writeInt(mMinorVersion); - mExtendedInfo.writeToParcel(dest, flags); } /** Builder for a {@link HubServiceInfo} object. */ @@ -165,9 +149,6 @@ public final class HubServiceInfo implements Parcelable { private final int mMajorVersion; private final int mMinorVersion; - private final ParcelableHolder mExtendedInfo = - new ParcelableHolder(Parcelable.PARCELABLE_STABILITY_VINTF); - /** * Create a builder for {@link HubServiceInfo} with a service descriptor. * @@ -220,20 +201,6 @@ public final class HubServiceInfo implements Parcelable { } /** - * Set the extended information of this service. - * - * @param extendedInfo Parcelable with extended information about this service. The - * parcelable needs to have at least VINTF stability. Null can be used to clear a - * previously set value. - * @throws android.os.BadParcelableException if the parcelable cannot be used. - */ - @NonNull - public Builder setExtendedInfo(@Nullable Parcelable extendedInfo) { - mExtendedInfo.setParcelable(extendedInfo); - return this; - } - - /** * Build the {@link HubServiceInfo} object. * * @throws IllegalStateException if the Builder is missing required info. @@ -244,7 +211,7 @@ public final class HubServiceInfo implements Parcelable { throw new IllegalStateException("Major and minor version must be set."); } return new HubServiceInfo( - mServiceDescriptor, mFormat, mMajorVersion, mMinorVersion, mExtendedInfo); + mServiceDescriptor, mFormat, mMajorVersion, mMinorVersion); } } diff --git a/core/java/android/hardware/contexthub/IContextHubEndpointDiscoveryCallback.aidl b/core/java/android/hardware/contexthub/IContextHubEndpointDiscoveryCallback.aidl new file mode 100644 index 000000000000..245be930a897 --- /dev/null +++ b/core/java/android/hardware/contexthub/IContextHubEndpointDiscoveryCallback.aidl @@ -0,0 +1,37 @@ +/* + * 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. + */ + +package android.hardware.contexthub; + +import android.hardware.contexthub.HubEndpointInfo; + +/** + * @hide + */ +oneway interface IContextHubEndpointDiscoveryCallback { + /** + * Called when endpoint(s) start. + * @param hubEndpointInfoList The list of endpoints that started. + */ + void onEndpointsStarted(in HubEndpointInfo[] hubEndpointInfoList); + + /** + * Called when endpoint(s) stopped. + * @param hubEndpointInfoList The list of endpoints that started. + * @param reason The reason why the endpoints stopped. + */ + void onEndpointsStopped(in HubEndpointInfo[] hubEndpointInfoList, int reason); +} diff --git a/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java b/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.java new file mode 100644 index 000000000000..a61a7ebd0de9 --- /dev/null +++ b/core/java/android/hardware/contexthub/IHubEndpointDiscoveryCallback.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 android.hardware.contexthub; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.chre.flags.Flags; + +import java.util.List; + +/** + * Interface for listening to updates about endpoint availability. + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_OFFLOAD_API) +public interface IHubEndpointDiscoveryCallback { + /** + * Called when a list of hub endpoints have started. + * + * @param discoveryInfoList The list containing hub discovery information. + */ + void onEndpointsStarted(@NonNull List<HubDiscoveryInfo> discoveryInfoList); + + /** + * Called when a list of hub endpoints have stopped. + * + * @param discoveryInfoList The list containing hub discovery information. + * @param reason The reason the endpoints stopped. + */ + void onEndpointsStopped( + @NonNull List<HubDiscoveryInfo> discoveryInfoList, @HubEndpoint.Reason int reason); +} diff --git a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java index 46884393b49b..fe449bb5ce0e 100644 --- a/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java +++ b/core/java/android/hardware/contexthub/IHubEndpointLifecycleCallback.java @@ -17,15 +17,11 @@ package android.hardware.contexthub; import android.annotation.FlaggedApi; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.chre.flags.Flags; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** * Interface for listening to lifecycle events of a hub endpoint. * @@ -34,24 +30,6 @@ import java.lang.annotation.RetentionPolicy; @SystemApi @FlaggedApi(Flags.FLAG_OFFLOAD_API) public interface IHubEndpointLifecycleCallback { - /** Unknown reason. */ - int REASON_UNSPECIFIED = 0; - - /** The peer rejected the request to open this endpoint session. */ - int REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED = 3; - - /** The peer closed this endpoint session. */ - int REASON_CLOSE_ENDPOINT_SESSION_REQUESTED = 4; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - REASON_UNSPECIFIED, - REASON_OPEN_ENDPOINT_SESSION_REQUEST_REJECTED, - REASON_CLOSE_ENDPOINT_SESSION_REQUESTED, - }) - @interface EndpointLifecycleReason {} - /** * Called when an endpoint is requesting a session be opened with another endpoint. * @@ -78,5 +56,5 @@ public interface IHubEndpointLifecycleCallback { * used. * @param reason The reason why this session was closed. */ - void onSessionClosed(@NonNull HubEndpointSession session, @EndpointLifecycleReason int reason); + void onSessionClosed(@NonNull HubEndpointSession session, @HubEndpoint.Reason int reason); } diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 426cd69f76a0..117d8fe24809 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -38,6 +38,8 @@ import android.hardware.contexthub.HubDiscoveryInfo; import android.hardware.contexthub.HubEndpoint; import android.hardware.contexthub.HubEndpointInfo; import android.hardware.contexthub.HubServiceInfo; +import android.hardware.contexthub.IContextHubEndpointDiscoveryCallback; +import android.hardware.contexthub.IHubEndpointDiscoveryCallback; import android.hardware.contexthub.IHubEndpointLifecycleCallback; import android.os.Handler; import android.os.HandlerExecutor; @@ -49,7 +51,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; /** @@ -202,6 +206,10 @@ public final class ContextHubManager { private Callback mCallback; private Handler mCallbackHandler; + /** A map of endpoint discovery callbacks currently registered */ + private Map<IHubEndpointDiscoveryCallback, IContextHubEndpointDiscoveryCallback> + mDiscoveryCallbacks = new ConcurrentHashMap<>(); + /** * @deprecated Use {@code mCallback} instead. */ @@ -694,8 +702,6 @@ public final class ContextHubManager { @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @NonNull public List<HubDiscoveryInfo> findEndpoints(long endpointId) { - // TODO(b/379323274): Consider improving these getters to avoid racing with nano app load - // timing. try { List<HubEndpointInfo> endpointInfos = mService.findEndpoints(endpointId); List<HubDiscoveryInfo> results = new ArrayList<>(endpointInfos.size()); @@ -720,8 +726,6 @@ public final class ContextHubManager { @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) @NonNull public List<HubDiscoveryInfo> findEndpoints(@NonNull String serviceDescriptor) { - // TODO(b/379323274): Consider improving these getters to avoid racing with nano app load - // timing. if (serviceDescriptor.isBlank()) { throw new IllegalArgumentException("Invalid service descriptor: " + serviceDescriptor); } @@ -744,6 +748,188 @@ public final class ContextHubManager { } /** + * Creates an interface to invoke endpoint discovery callbacks to send down to the service. + * + * @param callback the callback to invoke at the client process + * @param executor the executor to invoke callbacks for this client + * @return the callback interface + */ + private IContextHubEndpointDiscoveryCallback createDiscoveryCallback( + IHubEndpointDiscoveryCallback callback, + Executor executor, + @Nullable String serviceDescriptor) { + return new IContextHubEndpointDiscoveryCallback.Stub() { + @Override + public void onEndpointsStarted(HubEndpointInfo[] hubEndpointInfoList) { + if (hubEndpointInfoList.length == 0) { + Log.w(TAG, "onEndpointsStarted: received empty discovery list"); + return; + } + executor.execute( + () -> { + // TODO(b/380293951): Refactor + List<HubDiscoveryInfo> discoveryList = + new ArrayList<>(hubEndpointInfoList.length); + for (HubEndpointInfo info : hubEndpointInfoList) { + if (serviceDescriptor != null) { + for (HubServiceInfo sInfo : info.getServiceInfoCollection()) { + if (sInfo.getServiceDescriptor() + .equals(serviceDescriptor)) { + discoveryList.add(new HubDiscoveryInfo(info, sInfo)); + } + } + } else { + discoveryList.add(new HubDiscoveryInfo(info)); + } + } + if (discoveryList.isEmpty()) { + Log.w(TAG, "onEndpointsStarted: no matching service descriptor"); + } else { + callback.onEndpointsStarted(discoveryList); + } + }); + } + + @Override + public void onEndpointsStopped(HubEndpointInfo[] hubEndpointInfoList, int reason) { + if (hubEndpointInfoList.length == 0) { + Log.w(TAG, "onEndpointsStopped: received empty discovery list"); + return; + } + executor.execute( + () -> { + List<HubDiscoveryInfo> discoveryList = + new ArrayList<>(hubEndpointInfoList.length); + for (HubEndpointInfo info : hubEndpointInfoList) { + if (serviceDescriptor != null) { + for (HubServiceInfo sInfo : info.getServiceInfoCollection()) { + if (sInfo.getServiceDescriptor() + .equals(serviceDescriptor)) { + discoveryList.add(new HubDiscoveryInfo(info, sInfo)); + } + } + } else { + discoveryList.add(new HubDiscoveryInfo(info)); + } + } + if (discoveryList.isEmpty()) { + Log.w(TAG, "onEndpointsStopped: no matching service descriptor"); + } else { + callback.onEndpointsStopped(discoveryList, reason); + } + }); + } + }; + } + + /** + * Equivalent to {@link #registerEndpointDiscoveryCallback(long, IHubEndpointDiscoveryCallback, + * Executor)} with the default executor in the main thread. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) + @FlaggedApi(Flags.FLAG_OFFLOAD_API) + public void registerEndpointDiscoveryCallback( + long endpointId, @NonNull IHubEndpointDiscoveryCallback callback) { + registerEndpointDiscoveryCallback( + endpointId, callback, new HandlerExecutor(Handler.getMain())); + } + + /** + * Registers a callback to be notified when the hub endpoint with the corresponding endpoint ID + * has started or stopped. + * + * @param endpointId The identifier of the hub endpoint. + * @param callback The callback to be invoked. + * @param executor The executor to invoke the callback on. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) + @FlaggedApi(Flags.FLAG_OFFLOAD_API) + public void registerEndpointDiscoveryCallback( + long endpointId, + @NonNull IHubEndpointDiscoveryCallback callback, + @NonNull Executor executor) { + Objects.requireNonNull(callback, "callback cannot be null"); + Objects.requireNonNull(executor, "executor cannot be null"); + IContextHubEndpointDiscoveryCallback iCallback = + createDiscoveryCallback(callback, executor, null); + try { + mService.registerEndpointDiscoveryCallbackId(endpointId, iCallback); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + + mDiscoveryCallbacks.put(callback, iCallback); + } + + /** + * Equivalent to {@link #registerEndpointDiscoveryCallback(String, + * IHubEndpointDiscoveryCallback, Executor)} with the default executor in the main thread. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) + @FlaggedApi(Flags.FLAG_OFFLOAD_API) + public void registerEndpointDiscoveryCallback( + @NonNull String serviceDescriptor, @NonNull IHubEndpointDiscoveryCallback callback) { + registerEndpointDiscoveryCallback( + serviceDescriptor, callback, new HandlerExecutor(Handler.getMain())); + } + + /** + * Registers a callback to be notified when the hub endpoint with the corresponding service + * descriptor has started or stopped. + * + * @param serviceDescriptor The service descriptor of the hub endpoint. + * @param callback The callback to be invoked. + * @param executor The executor to invoke the callback on. + * @throws IllegalArgumentException if the serviceDescriptor is empty. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) + @FlaggedApi(Flags.FLAG_OFFLOAD_API) + public void registerEndpointDiscoveryCallback( + @NonNull String serviceDescriptor, + @NonNull IHubEndpointDiscoveryCallback callback, + @NonNull Executor executor) { + Objects.requireNonNull(serviceDescriptor, "serviceDescriptor cannot be null"); + Objects.requireNonNull(callback, "callback cannot be null"); + Objects.requireNonNull(executor, "executor cannot be null"); + if (serviceDescriptor.isBlank()) { + throw new IllegalArgumentException("Invalid service descriptor: " + serviceDescriptor); + } + + IContextHubEndpointDiscoveryCallback iCallback = + createDiscoveryCallback(callback, executor, serviceDescriptor); + try { + mService.registerEndpointDiscoveryCallbackDescriptor(serviceDescriptor, iCallback); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + + mDiscoveryCallbacks.put(callback, iCallback); + } + + /** + * Unregisters a previously registered endpoint discovery callback. + * + * @param callback The callback previously registered. + * @throws IllegalArgumentException If the callback was not previously registered. + */ + @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) + @FlaggedApi(Flags.FLAG_OFFLOAD_API) + public void unregisterEndpointDiscoveryCallback( + @NonNull IHubEndpointDiscoveryCallback callback) { + Objects.requireNonNull(callback, "callback cannot be null"); + IContextHubEndpointDiscoveryCallback iCallback = mDiscoveryCallbacks.remove(callback); + if (iCallback == null) { + throw new IllegalArgumentException("Callback not previously registered"); + } + + try { + mService.unregisterEndpointDiscoveryCallback(iCallback); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** * Set a callback to receive messages from the context hub * * @param callback Callback object diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl index f9f412446038..f14aadcab474 100644 --- a/core/java/android/hardware/location/IContextHubService.aidl +++ b/core/java/android/hardware/location/IContextHubService.aidl @@ -21,6 +21,7 @@ import android.app.PendingIntent; import android.hardware.contexthub.HubEndpointInfo; import android.hardware.contexthub.IContextHubEndpoint; import android.hardware.contexthub.IContextHubEndpointCallback; +import android.hardware.contexthub.IContextHubEndpointDiscoveryCallback; import android.hardware.location.ContextHubInfo; import android.hardware.location.ContextHubMessage; import android.hardware.location.HubInfo; @@ -137,4 +138,16 @@ interface IContextHubService { // Register an endpoint with the context hub @EnforcePermission("ACCESS_CONTEXT_HUB") IContextHubEndpoint registerEndpoint(in HubEndpointInfo pendingEndpointInfo, in IContextHubEndpointCallback callback); + + // Register an endpoint discovery callback (id) + @EnforcePermission("ACCESS_CONTEXT_HUB") + void registerEndpointDiscoveryCallbackId(long endpointId, in IContextHubEndpointDiscoveryCallback callback); + + // Register an endpoint discovery callback (descriptor) + @EnforcePermission("ACCESS_CONTEXT_HUB") + void registerEndpointDiscoveryCallbackDescriptor(String serviceDescriptor, in IContextHubEndpointDiscoveryCallback callback); + + // Unregister an endpoint with the context hub + @EnforcePermission("ACCESS_CONTEXT_HUB") + void unregisterEndpointDiscoveryCallback(in IContextHubEndpointDiscoveryCallback callback); } diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index 3219ce81c256..b270062cbffc 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -789,11 +789,17 @@ public final class VcnGatewayConnectionConfig { public Builder setMinUdpPort4500NatTimeoutSeconds( @IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS) int minUdpPort4500NatTimeoutSeconds) { - Preconditions.checkArgument( - minUdpPort4500NatTimeoutSeconds == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET - || minUdpPort4500NatTimeoutSeconds - >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, - "Timeout must be at least 120s or MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET"); + if (Flags.mainlineVcnModuleApi()) { + Preconditions.checkArgument( + minUdpPort4500NatTimeoutSeconds == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET + || minUdpPort4500NatTimeoutSeconds + >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, + "Timeout must be at least 120s or MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET"); + } else { + Preconditions.checkArgument( + minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, + "Timeout must be at least 120s"); + } mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds; return this; diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java index f0e12ca644dd..f5fee4f2d9be 100644 --- a/core/java/android/os/AggregateBatteryConsumer.java +++ b/core/java/android/os/AggregateBatteryConsumer.java @@ -17,7 +17,6 @@ package android.os; import android.annotation.NonNull; -import android.util.proto.ProtoOutputStream; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -100,19 +99,6 @@ public final class AggregateBatteryConsumer extends BatteryConsumer { } } - void writePowerComponentModelProto(@NonNull ProtoOutputStream proto) { - for (int i = 0; i < POWER_COMPONENT_COUNT; i++) { - final int powerModel = getPowerModel(i); - if (powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) continue; - - final long token = proto.start(BatteryUsageStatsAtomsProto.COMPONENT_MODELS); - proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.COMPONENT, i); - proto.write(BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_MODEL, - powerModelToProtoEnum(powerModel)); - proto.end(token); - } - } - /** * Builder for DeviceBatteryConsumer. */ diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index 14b67f64b6da..96ea1683b5cf 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -161,18 +161,27 @@ public abstract class BatteryConsumer { /** * Unspecified power model. + * + * @deprecated PowerModel is no longer supported */ + @Deprecated public static final int POWER_MODEL_UNDEFINED = 0; /** * Power model that is based on average consumption rates that hardware components * consume in various states. + * + * @deprecated PowerModel is no longer supported */ + @Deprecated public static final int POWER_MODEL_POWER_PROFILE = 1; /** * Power model that is based on energy consumption stats provided by PowerStats HAL. + * + * @deprecated PowerModel is no longer supported */ + @Deprecated public static final int POWER_MODEL_ENERGY_CONSUMPTION = 2; /** @@ -380,19 +389,17 @@ public abstract class BatteryConsumer { public final @ScreenState int screenState; public final @PowerState int powerState; - final int mPowerModelColumnIndex; final int mPowerColumnIndex; final int mDurationColumnIndex; private Key(@PowerComponentId int powerComponentId, @ProcessState int processState, - @ScreenState int screenState, @PowerState int powerState, int powerModelColumnIndex, + @ScreenState int screenState, @PowerState int powerState, int powerColumnIndex, int durationColumnIndex) { this.powerComponentId = powerComponentId; this.processState = processState; this.screenState = screenState; this.powerState = powerState; - mPowerModelColumnIndex = powerModelColumnIndex; mPowerColumnIndex = powerColumnIndex; mDurationColumnIndex = durationColumnIndex; } @@ -577,11 +584,11 @@ public abstract class BatteryConsumer { * * @param componentId The ID of the power component, e.g. * {@link BatteryConsumer#POWER_COMPONENT_CPU}. + * @deprecated PowerModel is no longer supported */ + @Deprecated public @PowerModel int getPowerModel(@PowerComponentId int componentId) { - return mPowerComponents.getPowerModel( - mData.layout.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED, - SCREEN_STATE_UNSPECIFIED, POWER_STATE_UNSPECIFIED)); + return POWER_MODEL_UNDEFINED; } /** @@ -589,9 +596,11 @@ public abstract class BatteryConsumer { * * @param key The key of the power component, obtained by calling {@link #getKey} or * {@link #getKeys} method. + * @deprecated PowerModel is no longer supported */ + @Deprecated public @PowerModel int getPowerModel(@NonNull BatteryConsumer.Key key) { - return mPowerComponents.getPowerModel(key); + return POWER_MODEL_UNDEFINED; } /** @@ -657,20 +666,6 @@ public abstract class BatteryConsumer { } /** - * Returns the name of the specified power model. Intended for logging and debugging. - */ - public static String powerModelToString(@BatteryConsumer.PowerModel int powerModel) { - switch (powerModel) { - case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION: - return "energy consumption"; - case BatteryConsumer.POWER_MODEL_POWER_PROFILE: - return "power profile"; - default: - return ""; - } - } - - /** * Returns the equivalent PowerModel enum for the specified power model. * {@see BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage.PowerModel} */ @@ -857,10 +852,8 @@ public abstract class BatteryConsumer { static class BatteryConsumerDataLayout { private static final Key[] KEY_ARRAY = new Key[0]; - public static final int POWER_MODEL_NOT_INCLUDED = -1; public final String[] customPowerComponentNames; public final int customPowerComponentCount; - public final boolean powerModelsIncluded; public final boolean processStateDataIncluded; public final boolean screenStateDataIncluded; public final boolean powerStateDataIncluded; @@ -872,11 +865,10 @@ public abstract class BatteryConsumer { private SparseArray<Key[]> mPerComponentKeys; private BatteryConsumerDataLayout(int firstColumn, String[] customPowerComponentNames, - boolean powerModelsIncluded, boolean includeProcessStateData, - boolean includeScreenState, boolean includePowerState) { + boolean includeProcessStateData, boolean includeScreenState, + boolean includePowerState) { this.customPowerComponentNames = customPowerComponentNames; this.customPowerComponentCount = customPowerComponentNames.length; - this.powerModelsIncluded = powerModelsIncluded; this.processStateDataIncluded = includeProcessStateData; this.screenStateDataIncluded = includeScreenState; this.powerStateDataIncluded = includePowerState; @@ -904,7 +896,7 @@ public abstract class BatteryConsumer { continue; } for (int i = 0; i < powerComponentIds.length; i++) { - columnIndex = addKeys(keyList, powerModelsIncluded, includeProcessStateData, + columnIndex = addKeys(keyList, includeProcessStateData, powerComponentIds[i], screenState, powerState, columnIndex); } } @@ -934,13 +926,10 @@ public abstract class BatteryConsumer { } } - private int addKeys(List<Key> keys, boolean powerModelsIncluded, - boolean includeProcessStateData, @PowerComponentId int componentId, - int screenState, int powerState, int columnIndex) { + private int addKeys(List<Key> keys, boolean includeProcessStateData, + @PowerComponentId int componentId, int screenState, int powerState, + int columnIndex) { keys.add(new Key(componentId, PROCESS_STATE_UNSPECIFIED, screenState, powerState, - powerModelsIncluded - ? columnIndex++ - : POWER_MODEL_NOT_INCLUDED, // power model columnIndex++, // power columnIndex++ // usage duration )); @@ -956,9 +945,6 @@ public abstract class BatteryConsumer { continue; } keys.add(new Key(componentId, processState, screenState, powerState, - powerModelsIncluded - ? columnIndex++ - : POWER_MODEL_NOT_INCLUDED, // power model columnIndex++, // power columnIndex++ // usage duration )); @@ -1016,7 +1002,7 @@ public abstract class BatteryConsumer { } static BatteryConsumerDataLayout createBatteryConsumerDataLayout( - String[] customPowerComponentNames, boolean includePowerModels, + String[] customPowerComponentNames, boolean includeProcessStateData, boolean includeScreenStateData, boolean includePowerStateData) { int columnCount = BatteryConsumer.COLUMN_COUNT; @@ -1025,8 +1011,7 @@ public abstract class BatteryConsumer { columnCount = Math.max(columnCount, UserBatteryConsumer.COLUMN_COUNT); return new BatteryConsumerDataLayout(columnCount, customPowerComponentNames, - includePowerModels, includeProcessStateData, includeScreenStateData, - includePowerStateData); + includeProcessStateData, includeScreenStateData, includePowerStateData); } protected abstract static class BaseBuilder<T extends BaseBuilder<?>> { @@ -1086,7 +1071,7 @@ public abstract class BatteryConsumer { public T setConsumedPower(@PowerComponentId int componentId, double componentPower, @PowerModel int powerModel) { mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED), - componentPower, powerModel); + componentPower); return (T) this; } @@ -1095,14 +1080,14 @@ public abstract class BatteryConsumer { public T addConsumedPower(@PowerComponentId int componentId, double componentPower, @PowerModel int powerModel) { mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED), - componentPower, powerModel); + componentPower); return (T) this; } @SuppressWarnings("unchecked") @NonNull public T setConsumedPower(Key key, double componentPower, @PowerModel int powerModel) { - mPowerComponentsBuilder.setConsumedPower(key, componentPower, powerModel); + mPowerComponentsBuilder.setConsumedPower(key, componentPower); return (T) this; } @@ -1110,21 +1095,14 @@ public abstract class BatteryConsumer { @NonNull public T addConsumedPower(@PowerComponentId int componentId, double componentPower) { mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED), - componentPower, POWER_MODEL_UNDEFINED); + componentPower); return (T) this; } @SuppressWarnings("unchecked") @NonNull public T addConsumedPower(Key key, double componentPower) { - mPowerComponentsBuilder.addConsumedPower(key, componentPower, POWER_MODEL_UNDEFINED); - return (T) this; - } - - @SuppressWarnings("unchecked") - @NonNull - public T addConsumedPower(Key key, double componentPower, @PowerModel int powerModel) { - mPowerComponentsBuilder.addConsumedPower(key, componentPower, powerModel); + mPowerComponentsBuilder.addConsumedPower(key, componentPower); return (T) this; } diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 8b267bf28c7e..b63ad5f0148e 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -167,76 +167,90 @@ public class BatteryManager { public static final String EXTRA_CHARGING_STATUS = "android.os.extra.CHARGING_STATUS"; /** - * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: - * Int value representing the battery's capacity level. These constants are key indicators of - * battery status and system capabilities, guiding power management decisions for both the - * system and apps: - * {@link #BATTERY_CAPACITY_LEVEL_UNSUPPORTED}: Feature not supported on this device. - * {@link #BATTERY_CAPACITY_LEVEL_UNKNOWN}: Battery status is unavailable or uninitialized. - * {@link #BATTERY_CAPACITY_LEVEL_CRITICAL}: Battery is critically low and the Android - * framework has been notified to schedule a shutdown by this value - * {@link #BATTERY_CAPACITY_LEVEL_LOW}: Android framework must limit background jobs to - * avoid impacting charging speed - * {@link #BATTERY_CAPACITY_LEVEL_NORMAL}: Battery level and charging rates are normal, - * battery temperature is within normal range and adapter power is enough to charge the - * battery at an acceptable rate. Android framework can run light background tasks without - * affecting charging performance severely. - * {@link #BATTERY_CAPACITY_LEVEL_HIGH}: Battery level is high, battery temperature is - * within normal range and adapter power is enough to charge the battery at an acceptable - * rate while running background loads. Android framework can run background tasks without - * affecting charging or battery performance. - * {@link #BATTERY_CAPACITY_LEVEL_FULL}: The battery is full, battery temperature is - * within normal range and adapter power is enough to sustain running background loads. - * Android framework can run background tasks without affecting the battery level or - * battery performance. - */ - - @FlaggedApi(FLAG_BATTERY_PART_STATUS_API) - public static final String EXTRA_CAPACITY_LEVEL = "android.os.extra.CAPACITY_LEVEL"; - - /** - * Battery capacity level is unsupported. @see EXTRA_CAPACITY_LEVEL + * Battery capacity level is unsupported. + * + * @see #EXTRA_CAPACITY_LEVEL */ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API) public static final int BATTERY_CAPACITY_LEVEL_UNSUPPORTED = -1; /** - * Battery capacity level is unknown. @see EXTRA_CAPACITY_LEVEL + * Battery capacity level is unknown. + * + * @see #EXTRA_CAPACITY_LEVEL */ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API) public static final int BATTERY_CAPACITY_LEVEL_UNKNOWN = 0; /** - * Battery capacity level is critical. @see EXTRA_CAPACITY_LEVEL + * Battery capacity level is critical. The Android framework has been notified to schedule + * a shutdown by this value. + * + * @see #EXTRA_CAPACITY_LEVEL */ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API) public static final int BATTERY_CAPACITY_LEVEL_CRITICAL = 1; /** - * Battery capacity level is low. @see EXTRA_CAPACITY_LEVEL + * Battery capacity level is low. The Android framework must limit background jobs to avoid + * impacting charging speed. + * + * @see #EXTRA_CAPACITY_LEVEL */ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API) public static final int BATTERY_CAPACITY_LEVEL_LOW = 2; /** - * Battery capacity level is normal. @see EXTRA_CAPACITY_LEVEL + * Battery capacity level is normal. Battery level and charging rates are normal, battery + * temperature is within the normal range, and adapter power is enough to charge the battery + * at an acceptable rate. The Android framework can run light background tasks without + * affecting charging performance severely. + * + * @see #EXTRA_CAPACITY_LEVEL */ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API) public static final int BATTERY_CAPACITY_LEVEL_NORMAL = 3; /** - * Battery capacity level is high. @see EXTRA_CAPACITY_LEVEL + * Battery capacity level is high. Battery level is high, battery temperature is within the + * normal range, and adapter power is enough to charge the battery at an acceptable rate + * while running background loads. The Android framework can run background tasks without + * affecting charging or battery performance. + * + * @see #EXTRA_CAPACITY_LEVEL */ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API) public static final int BATTERY_CAPACITY_LEVEL_HIGH = 4; /** - * Battery capacity level is full. @see EXTRA_CAPACITY_LEVEL + * Battery capacity level is full. The battery is full, the battery temperature is within the + * normal range, and adapter power is enough to sustain running background loads. The Android + * framework can run background tasks without affecting the battery level or battery + * performance. + * + * @see #EXTRA_CAPACITY_LEVEL */ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API) public static final int BATTERY_CAPACITY_LEVEL_FULL = 5; /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * Int value representing the battery's capacity level. These constants are key indicators of + * battery status and system capabilities, guiding power management decisions for both the + * system and apps. + * + * @see #BATTERY_CAPACITY_LEVEL_UNSUPPORTED + * @see #BATTERY_CAPACITY_LEVEL_UNKNOWN + * @see #BATTERY_CAPACITY_LEVEL_CRITICAL + * @see #BATTERY_CAPACITY_LEVEL_LOW + * @see #BATTERY_CAPACITY_LEVEL_NORMAL + * @see #BATTERY_CAPACITY_LEVEL_HIGH + * @see #BATTERY_CAPACITY_LEVEL_FULL + */ + @FlaggedApi(FLAG_BATTERY_PART_STATUS_API) + public static final String EXTRA_CAPACITY_LEVEL = "android.os.extra.CAPACITY_LEVEL"; + + /** * Extra for {@link android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED}: * Contains list of Bundles representing battery events * @hide diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 72e4cef2f6eb..f913fcfd56d4 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -114,7 +114,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { static final String XML_ATTR_POWER_STATE = "power_state"; static final String XML_ATTR_POWER = "power"; static final String XML_ATTR_DURATION = "duration"; - static final String XML_ATTR_MODEL = "model"; static final String XML_ATTR_BATTERY_CAPACITY = "battery_capacity"; static final String XML_ATTR_DISCHARGE_PERCENT = "discharge_pct"; static final String XML_ATTR_DISCHARGE_LOWER = "discharge_lower"; @@ -155,7 +154,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { private final long mBatteryTimeRemainingMs; private final long mChargeTimeRemainingMs; private final String[] mCustomPowerComponentNames; - private final boolean mIncludesPowerModels; private final boolean mIncludesProcessStateData; private final boolean mIncludesScreenStateData; private final boolean mIncludesPowerStateData; @@ -179,7 +177,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs; mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs; mCustomPowerComponentNames = builder.mCustomPowerComponentNames; - mIncludesPowerModels = builder.mIncludePowerModels; mIncludesProcessStateData = builder.mIncludesProcessStateData; mIncludesScreenStateData = builder.mIncludesScreenStateData; mIncludesPowerStateData = builder.mIncludesPowerStateData; @@ -364,14 +361,13 @@ public final class BatteryUsageStats implements Parcelable, Closeable { mBatteryTimeRemainingMs = source.readLong(); mChargeTimeRemainingMs = source.readLong(); mCustomPowerComponentNames = source.readStringArray(); - mIncludesPowerModels = source.readBoolean(); mIncludesProcessStateData = source.readBoolean(); mIncludesScreenStateData = source.readBoolean(); mIncludesPowerStateData = source.readBoolean(); mBatteryConsumersCursorWindow = CursorWindow.newFromParcel(source); mBatteryConsumerDataLayout = BatteryConsumer.createBatteryConsumerDataLayout( - mCustomPowerComponentNames, mIncludesPowerModels, mIncludesProcessStateData, + mCustomPowerComponentNames, mIncludesProcessStateData, mIncludesScreenStateData, mIncludesPowerStateData); final int numRows = mBatteryConsumersCursorWindow.getNumRows(); @@ -424,7 +420,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { dest.writeLong(mBatteryTimeRemainingMs); dest.writeLong(mChargeTimeRemainingMs); dest.writeStringArray(mCustomPowerComponentNames); - dest.writeBoolean(mIncludesPowerModels); dest.writeBoolean(mIncludesProcessStateData); dest.writeBoolean(mIncludesScreenStateData); dest.writeBoolean(mIncludesPowerStateData); @@ -506,9 +501,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { getDischargeDurationMs()); deviceBatteryConsumer.writeStatsProto(proto, BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER); - if (mIncludesPowerModels) { - deviceBatteryConsumer.writePowerComponentModelProto(proto); - } writeUidBatteryConsumersProto(proto, maxRawSize); } @@ -629,7 +621,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { printPowerComponent(pw, prefix, mBatteryConsumerDataLayout.getPowerComponentName(powerComponent), - devicePowerMah, appsPowerMah, BatteryConsumer.POWER_MODEL_UNDEFINED, + devicePowerMah, appsPowerMah, deviceConsumer.getUsageDurationMillis(powerComponent)); } @@ -716,23 +708,15 @@ public final class BatteryUsageStats implements Parcelable, Closeable { printPowerComponent(pw, prefix, mBatteryConsumerDataLayout.getPowerComponentName(powerComponent), devicePowerMah, appsPowerMah, - mIncludesPowerModels ? deviceConsumer.getPowerModel(powerComponent) - : BatteryConsumer.POWER_MODEL_UNDEFINED, deviceConsumer.getUsageDurationMillis(dimensions)); } } private void printPowerComponent(PrintWriter pw, String prefix, String label, - double devicePowerMah, double appsPowerMah, int powerModel, long durationMs) { + double devicePowerMah, double appsPowerMah, long durationMs) { StringBuilder sb = new StringBuilder(); sb.append(prefix).append(" ").append(label).append(": ") .append(BatteryStats.formatCharge(devicePowerMah)); - if (powerModel != BatteryConsumer.POWER_MODEL_UNDEFINED - && powerModel != BatteryConsumer.POWER_MODEL_POWER_PROFILE) { - sb.append(" ["); - sb.append(BatteryConsumer.powerModelToString(powerModel)); - sb.append("]"); - } sb.append(" apps: ").append(BatteryStats.formatCharge(appsPowerMah)); if (durationMs != 0) { sb.append(" duration: "); @@ -828,7 +812,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { final boolean includesPowerStateData = parser.getAttributeBoolean(null, XML_ATTR_PREFIX_INCLUDES_POWER_STATE_DATA, false); - builder = new Builder(customComponentNames.toArray(new String[0]), true, + builder = new Builder(customComponentNames.toArray(new String[0]), includesProcStateData, includesScreenStateData, includesPowerStateData, 0); builder.setStatsStartTimestamp( @@ -913,7 +897,6 @@ public final class BatteryUsageStats implements Parcelable, Closeable { private final CursorWindow mBatteryConsumersCursorWindow; @NonNull private final String[] mCustomPowerComponentNames; - private final boolean mIncludePowerModels; private final boolean mIncludesProcessStateData; private final boolean mIncludesScreenStateData; private final boolean mIncludesPowerStateData; @@ -938,22 +921,21 @@ public final class BatteryUsageStats implements Parcelable, Closeable { private BatteryStatsHistory mBatteryStatsHistory; public Builder(@NonNull String[] customPowerComponentNames) { - this(customPowerComponentNames, false, false, false, false, 0); + this(customPowerComponentNames, false, false, false, 0); } - public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels, + public Builder(@NonNull String[] customPowerComponentNames, boolean includeProcessStateData, boolean includeScreenStateData, boolean includesPowerStateData, double minConsumedPowerThreshold) { mBatteryConsumersCursorWindow = new CursorWindow(null, BATTERY_CONSUMER_CURSOR_WINDOW_SIZE); onCursorWindowAllocated(mBatteryConsumersCursorWindow); mBatteryConsumerDataLayout = BatteryConsumer.createBatteryConsumerDataLayout( - customPowerComponentNames, includePowerModels, includeProcessStateData, + customPowerComponentNames, includeProcessStateData, includeScreenStateData, includesPowerStateData); mBatteryConsumersCursorWindow.setNumColumns(mBatteryConsumerDataLayout.columnCount); mCustomPowerComponentNames = customPowerComponentNames; - mIncludePowerModels = includePowerModels; mIncludesProcessStateData = includeProcessStateData; mIncludesScreenStateData = includeScreenStateData; mIncludesPowerStateData = includesPowerStateData; diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java index 6325b003a999..6e67578fadc8 100644 --- a/core/java/android/os/BatteryUsageStatsQuery.java +++ b/core/java/android/os/BatteryUsageStatsQuery.java @@ -24,6 +24,7 @@ import com.android.internal.os.MonotonicClock; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; /** * Query parameters for the {@link BatteryStatsManager#getBatteryUsageStats()} call. @@ -65,12 +66,6 @@ public final class BatteryUsageStatsQuery implements Parcelable { */ public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY = 0x0002; - /** - * Indicates that identifiers of power models used for computations of power - * consumption should be included in the BatteryUsageStats. - */ - public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS = 0x0004; - public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA = 0x0008; public static final int FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS = 0x0010; @@ -202,7 +197,24 @@ public final class BatteryUsageStatsQuery implements Parcelable { return mAggregatedToTimestamp; } + @Override + public String toString() { + return "BatteryUsageStatsQuery{" + + "mFlags=" + Integer.toHexString(mFlags) + + ", mUserIds=" + Arrays.toString(mUserIds) + + ", mMaxStatsAgeMs=" + mMaxStatsAgeMs + + ", mAggregatedFromTimestamp=" + mAggregatedFromTimestamp + + ", mAggregatedToTimestamp=" + mAggregatedToTimestamp + + ", mMonotonicStartTime=" + mMonotonicStartTime + + ", mMonotonicEndTime=" + mMonotonicEndTime + + ", mMinConsumedPowerThreshold=" + mMinConsumedPowerThreshold + + ", mPowerComponents=" + Arrays.toString(mPowerComponents) + + '}'; + } + private BatteryUsageStatsQuery(Parcel in) { + mMonotonicStartTime = in.readLong(); + mMonotonicEndTime = in.readLong(); mFlags = in.readInt(); mUserIds = new int[in.readInt()]; in.readIntArray(mUserIds); @@ -215,6 +227,8 @@ public final class BatteryUsageStatsQuery implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mMonotonicStartTime); + dest.writeLong(mMonotonicEndTime); dest.writeInt(mFlags); dest.writeInt(mUserIds.length); dest.writeIntArray(mUserIds); @@ -311,7 +325,10 @@ public final class BatteryUsageStatsQuery implements Parcelable { * power monitoring data is available. * * Should only be used for testing and debugging. + * + * @deprecated PowerModel is no longer supported */ + @Deprecated public Builder powerProfileModeledOnly() { mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_POWER_PROFILE_MODEL; return this; @@ -322,9 +339,10 @@ public final class BatteryUsageStatsQuery implements Parcelable { * of power consumption. * * Should only be used for testing and debugging. + * @deprecated PowerModel is no longer supported */ + @Deprecated public Builder includePowerModels() { - mFlags |= BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS; return this; } diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java index 4c9f08d80d4b..476968151e18 100644 --- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java +++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java @@ -19,8 +19,6 @@ package android.os; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.TestApi; -import android.app.ActivityThread; -import android.app.Instrumentation; import android.compat.annotation.UnsupportedAppUsage; import android.os.Process; import android.os.UserHandle; @@ -88,14 +86,20 @@ public final class MessageQueue { // queue for async messages when inserting a message at the tail. private int mAsyncMessageCount; - /* + /** * Select between two implementations of message queue. The legacy implementation is used * by default as it provides maximum compatibility with applications and tests that * reach into MessageQueue via the mMessages field. The concurrent implemmentation is used for * system processes and provides a higher level of concurrency and higher enqueue throughput * than the legacy implementation. */ - private boolean mUseConcurrent; + private final boolean mUseConcurrent; + + /** + * Caches process-level checks that determine `mUseConcurrent`. + * This is to avoid redoing checks that shouldn't change during the process's lifetime. + */ + private static Boolean sIsProcessAllowedToUseConcurrent = null; @RavenwoodRedirect private native static long nativeInit(); @@ -112,32 +116,39 @@ public final class MessageQueue { private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events); MessageQueue(boolean quitAllowed) { - // Concurrent mode modifies behavior that is observable via reflection and is commonly used - // by tests. - // For now, we limit it to system processes to avoid breaking apps and their tests. - mUseConcurrent = UserHandle.isCore(Process.myUid()); - // Even then, we don't use it if instrumentation is loaded as it breaks some - // platform tests. - final Instrumentation instrumentation = getInstrumentation(); - mUseConcurrent &= instrumentation == null || !instrumentation.isInstrumenting(); - // We can lift this restriction in the future after we've made it possible for test authors - // to test Looper and MessageQueue without resorting to reflection. + if (sIsProcessAllowedToUseConcurrent == null) { + // Concurrent mode modifies behavior that is observable via reflection and is commonly + // used by tests. + // For now, we limit it to system processes to avoid breaking apps and their tests. + boolean useConcurrent = UserHandle.isCore(Process.myUid()); - mQuitAllowed = quitAllowed; - mPtr = nativeInit(); - } + // Some platform tests run in system UIDs. + // Use this awful heuristic to detect them. + if (useConcurrent) { + final String processName = Process.myProcessName(); + if (processName == null + || processName.contains("test") + || processName.contains("Test")) { + useConcurrent = false; + } + } + + // We can lift this restriction in the future after we've made it possible for test + // authors to test Looper and MessageQueue without resorting to reflection. - @android.ravenwood.annotation.RavenwoodReplace(blockedBy = ActivityThread.class) - private static Instrumentation getInstrumentation() { - final ActivityThread activityThread = ActivityThread.currentActivityThread(); - if (activityThread != null) { - return activityThread.getInstrumentation(); + // Holdback study. + if (useConcurrent && Flags.messageQueueForceLegacy()) { + useConcurrent = false; + } + + sIsProcessAllowedToUseConcurrent = useConcurrent; + mUseConcurrent = useConcurrent; + } else { + mUseConcurrent = sIsProcessAllowedToUseConcurrent; } - return null; - } - private static Instrumentation getInstrumentation$ravenwood() { - return null; // Instrumentation not supported on Ravenwood yet. + mQuitAllowed = quitAllowed; + mPtr = nativeInit(); } @Override diff --git a/core/java/android/os/CpuHeadroomParams.java b/core/java/android/os/CpuHeadroomParams.java index f0d4f7d8737f..8e78b7e355f9 100644 --- a/core/java/android/os/CpuHeadroomParams.java +++ b/core/java/android/os/CpuHeadroomParams.java @@ -18,10 +18,13 @@ package android.os; import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; import android.os.health.SystemHealthManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; /** * Headroom request params used by {@link SystemHealthManager#getCpuHeadroom(CpuHeadroomParams)}. @@ -54,6 +57,16 @@ public final class CpuHeadroomParams { public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; /** + * Minimum CPU headroom calculation window size. + */ + public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; + + /** + * Maximum CPU headroom calculation window size. + */ + public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; + + /** * Sets the headroom calculation type. * <p> * @@ -83,6 +96,63 @@ public final class CpuHeadroomParams { } /** + * Sets the headroom calculation window size in milliseconds. + * <p> + * + * @param windowMillis the window size in milliseconds, ranged from + * [{@link #CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN}, + * {@link #CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX}]. The smaller + * the value, the larger fluctuation in value should be expected. The + * default value can be retrieved from the + * {@link #getCalculationWindowMillis}. The device will try to use the + * closest feasible window size to this param. + * @throws IllegalArgumentException if the window size is not in allowed range. + */ + public void setCalculationWindowMillis( + @IntRange(from = CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to = + CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) { + if (windowMillis < CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN + || windowMillis > CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) { + throw new IllegalArgumentException("Invalid calculation window: " + windowMillis); + } + mInternal.calculationWindowMillis = windowMillis; + } + + /** + * Gets the headroom calculation window size in milliseconds. + * <p> + * This will return the default value chosen by the device if not set. + */ + public @IntRange(from = CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to = + CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) long getCalculationWindowMillis() { + return mInternal.calculationWindowMillis; + } + + /** + * Sets the thread TIDs to track. + * <p> + * The TIDs should belong to the same of the process that will the headroom call. And they + * should not have different core affinity. + * <p> + * If not set, the headroom will be based on the PID of the process making the call. + * + * @param tids non-empty list of TIDs, maximum 5. + * @throws IllegalArgumentException if the list size is not in allowed range or TID is not + * positive. + */ + public void setTids(@NonNull int... tids) { + if (tids.length == 0 || tids.length > 5) { + throw new IllegalArgumentException("Invalid number of TIDs: " + tids.length); + } + for (int tid : tids) { + if (tid <= 0) { + throw new IllegalArgumentException("Invalid TID: " + tid); + } + } + mInternal.tids = Arrays.copyOf(tids, tids.length); + } + + /** * @hide */ public CpuHeadroomParamsInternal getInternal() { diff --git a/core/java/android/os/CpuHeadroomParamsInternal.aidl b/core/java/android/os/CpuHeadroomParamsInternal.aidl index 6cc4699a809e..d572f965579b 100644 --- a/core/java/android/os/CpuHeadroomParamsInternal.aidl +++ b/core/java/android/os/CpuHeadroomParamsInternal.aidl @@ -25,6 +25,8 @@ import android.hardware.power.CpuHeadroomParams; @JavaDerive(equals = true, toString = true) parcelable CpuHeadroomParamsInternal { boolean usesDeviceHeadroom = false; + int[] tids; + int calculationWindowMillis = 1000; CpuHeadroomParams.CalculationType calculationType = CpuHeadroomParams.CalculationType.MIN; CpuHeadroomParams.SelectionType selectionType = CpuHeadroomParams.SelectionType.ALL; } diff --git a/core/java/android/os/GpuHeadroomParams.java b/core/java/android/os/GpuHeadroomParams.java index efb2a28ad2b5..4dc98264e57b 100644 --- a/core/java/android/os/GpuHeadroomParams.java +++ b/core/java/android/os/GpuHeadroomParams.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.IntRange; import android.os.health.SystemHealthManager; import java.lang.annotation.Retention; @@ -54,6 +55,16 @@ public final class GpuHeadroomParams { public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; /** + * Minimum GPU headroom calculation window size. + */ + public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; + + /** + * Maximum GPU headroom calculation window size. + */ + public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; + + /** * Sets the headroom calculation type. * <p> * @@ -83,6 +94,39 @@ public final class GpuHeadroomParams { } /** + * Sets the headroom calculation window size in milliseconds. + * <p> + * + * @param windowMillis the window size in milliseconds, ranged from + * [{@link #GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN}, + * {@link #GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX}]. The smaller + * the value, the larger fluctuation in value should be expected. The + * default value can be retrieved from the + * {@link #getCalculationWindowMillis}. If the device will try to use the + * closest feasible window size to this param. + * @throws IllegalArgumentException if the window is invalid. + */ + public void setCalculationWindowMillis( + @IntRange(from = GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to = + GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) { + if (windowMillis < GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN + || windowMillis > GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) { + throw new IllegalArgumentException("Invalid calculation window: " + windowMillis); + } + mInternal.calculationWindowMillis = windowMillis; + } + + /** + * Gets the headroom calculation window size in milliseconds. + * <p> + * This will return the default value chosen by the device if not set. + */ + public @IntRange(from = GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to = + GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int getCalculationWindowMillis() { + return mInternal.calculationWindowMillis; + } + + /** * @hide */ public GpuHeadroomParamsInternal getInternal() { diff --git a/core/java/android/os/GpuHeadroomParamsInternal.aidl b/core/java/android/os/GpuHeadroomParamsInternal.aidl index 20309e7673f2..40d5d8e409ef 100644 --- a/core/java/android/os/GpuHeadroomParamsInternal.aidl +++ b/core/java/android/os/GpuHeadroomParamsInternal.aidl @@ -24,5 +24,6 @@ import android.hardware.power.GpuHeadroomParams; */ @JavaDerive(equals = true, toString = true) parcelable GpuHeadroomParamsInternal { + int calculationWindowMillis = 1000; GpuHeadroomParams.CalculationType calculationType = GpuHeadroomParams.CalculationType.MIN; } diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl index a043da9b7a36..f1936b5e0ff9 100644 --- a/core/java/android/os/IHintManager.aidl +++ b/core/java/android/os/IHintManager.aidl @@ -21,7 +21,9 @@ import android.os.CpuHeadroomParamsInternal; import android.os.GpuHeadroomParamsInternal; import android.os.IHintSession; import android.os.SessionCreationConfig; +import android.hardware.power.CpuHeadroomResult; import android.hardware.power.ChannelConfig; +import android.hardware.power.GpuHeadroomResult; import android.hardware.power.SessionConfig; import android.hardware.power.SessionTag; @@ -53,9 +55,9 @@ interface IHintManager { */ @nullable ChannelConfig getSessionChannel(in IBinder token); oneway void closeSessionChannel(); - float[] getCpuHeadroom(in CpuHeadroomParamsInternal params); + @nullable CpuHeadroomResult getCpuHeadroom(in CpuHeadroomParamsInternal params); long getCpuHeadroomMinIntervalMillis(); - float getGpuHeadroom(in GpuHeadroomParamsInternal params); + @nullable GpuHeadroomResult getGpuHeadroom(in GpuHeadroomParamsInternal params); long getGpuHeadroomMinIntervalMillis(); /** diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index e63b6648a9ef..f9789c19b0d5 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -89,6 +89,10 @@ per-file DdmSyncState.java = sanglardf@google.com, rpaquay@google.com per-file DdmSyncStageUpdater.java = sanglardf@google.com, rpaquay@google.com # PerformanceHintManager +per-file CpuHeadroom*.aidl = file:/ADPF_OWNERS +per-file GpuHeadroom*.aidl = file:/ADPF_OWNERS +per-file CpuHeadroom*.java = file:/ADPF_OWNERS +per-file GpuHeadroom*.java = file:/ADPF_OWNERS per-file PerformanceHintManager.java = file:/ADPF_OWNERS per-file WorkDuration.java = file:/ADPF_OWNERS per-file IHintManager.aidl = file:/ADPF_OWNERS diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index d116e0737c46..4db1f1b32407 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -15,7 +15,6 @@ */ package android.os; -import static android.os.BatteryConsumer.BatteryConsumerDataLayout.POWER_MODEL_NOT_INCLUDED; import static android.os.BatteryConsumer.POWER_COMPONENT_ANY; import static android.os.BatteryConsumer.POWER_COMPONENT_BASE; import static android.os.BatteryConsumer.POWER_STATE_ANY; @@ -156,15 +155,6 @@ class PowerComponents { return mData.layout.getPowerComponentName(componentId); } - @BatteryConsumer.PowerModel - int getPowerModel(BatteryConsumer.Key key) { - if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) { - throw new IllegalStateException( - "Power model IDs were not requested in the BatteryUsageStatsQuery"); - } - return mData.getInt(key.mPowerModelColumnIndex); - } - /** * Returns the amount of time used by the specified component, e.g. CPU, WiFi etc. * @@ -378,10 +368,6 @@ class PowerComponents { if (durationMs != 0) { serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs); } - if (mData.layout.powerModelsIncluded) { - serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_MODEL, - getPowerModel(key)); - } serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT); } serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS); @@ -411,7 +397,6 @@ class PowerComponents { int powerState = POWER_STATE_UNSPECIFIED; double powerMah = 0; long durationMs = 0; - int model = BatteryConsumer.POWER_MODEL_UNDEFINED; for (int i = 0; i < parser.getAttributeCount(); i++) { switch (parser.getAttributeName(i)) { case BatteryUsageStats.XML_ATTR_ID: @@ -432,14 +417,11 @@ class PowerComponents { case BatteryUsageStats.XML_ATTR_DURATION: durationMs = parser.getAttributeLong(i); break; - case BatteryUsageStats.XML_ATTR_MODEL: - model = parser.getAttributeInt(i); - break; } } final BatteryConsumer.Key key = builder.mData.layout.getKey(componentId, processState, screenState, powerState); - builder.addConsumedPower(key, powerMah, model); + builder.addConsumedPower(key, powerMah); builder.addUsageDurationMillis(key, durationMs); break; } @@ -453,43 +435,28 @@ class PowerComponents { * Builder for PowerComponents. */ static final class Builder { - private static final byte POWER_MODEL_UNINITIALIZED = -1; - private final BatteryConsumer.BatteryConsumerData mData; private final double mMinConsumedPowerThreshold; Builder(BatteryConsumer.BatteryConsumerData data, double minConsumedPowerThreshold) { mData = data; mMinConsumedPowerThreshold = minConsumedPowerThreshold; - for (BatteryConsumer.Key key : mData.layout.keys) { - if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { - mData.putInt(key.mPowerModelColumnIndex, POWER_MODEL_UNINITIALIZED); - } - } } /** - * @deprecated use {@link #addConsumedPower(BatteryConsumer.Key, double, int)} + * @deprecated use {@link #addConsumedPower(BatteryConsumer.Key, double)} */ @Deprecated @NonNull - public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower, - int powerModel) { + public Builder setConsumedPower(BatteryConsumer.Key key, double componentPower) { mData.putDouble(key.mPowerColumnIndex, componentPower); - if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { - mData.putInt(key.mPowerModelColumnIndex, powerModel); - } return this; } @NonNull - public Builder addConsumedPower(BatteryConsumer.Key key, double componentPower, - int powerModel) { + public Builder addConsumedPower(BatteryConsumer.Key key, double componentPower) { mData.putDouble(key.mPowerColumnIndex, mData.getDouble(key.mPowerColumnIndex) + componentPower); - if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { - mData.putInt(key.mPowerModelColumnIndex, powerModel); - } return this; } @@ -547,28 +514,6 @@ class PowerComponents { mData.getLong(key.mDurationColumnIndex) + otherData.getLong(otherKey.mDurationColumnIndex)); } - if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) { - continue; - } - - boolean undefined = false; - if (otherKey.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) { - undefined = true; - } else { - final int powerModel = mData.getInt(key.mPowerModelColumnIndex); - int otherPowerModel = otherData.getInt(otherKey.mPowerModelColumnIndex); - if (powerModel == POWER_MODEL_UNINITIALIZED) { - mData.putInt(key.mPowerModelColumnIndex, otherPowerModel); - } else if (powerModel != otherPowerModel - && otherPowerModel != POWER_MODEL_UNINITIALIZED) { - undefined = true; - } - } - - if (undefined) { - mData.putInt(key.mPowerModelColumnIndex, - BatteryConsumer.POWER_MODEL_UNDEFINED); - } } } @@ -594,13 +539,6 @@ class PowerComponents { @NonNull public PowerComponents build() { for (BatteryConsumer.Key key : mData.layout.keys) { - if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) { - if (mData.getInt(key.mPowerModelColumnIndex) == POWER_MODEL_UNINITIALIZED) { - mData.putInt(key.mPowerModelColumnIndex, - BatteryConsumer.POWER_MODEL_UNDEFINED); - } - } - if (mMinConsumedPowerThreshold != 0) { if (mData.getDouble(key.mPowerColumnIndex) < mMinConsumedPowerThreshold) { mData.putDouble(key.mPowerColumnIndex, 0); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 5a53bc1552b8..a1ede5fee3f3 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -3878,7 +3878,11 @@ public class UserManager { Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS, Manifest.permission.QUERY_USERS}) + @CachedProperty(api = "user_manager_user_data") public UserInfo getUserInfo(@UserIdInt int userId) { + if (android.multiuser.Flags.cacheUserInfoReadOnly()) { + return UserManagerCache.getUserInfo(mService::getUserInfo, userId); + } try { return mService.getUserInfo(userId); } catch (RemoteException re) { diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index 5ac53f1bf39d..3001fbd4fafa 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -4,6 +4,15 @@ container: "system" # keep-sorted start block=yes newline_separated=yes flag { + # Holdback study for concurrent MessageQueue. + # Do not promote beyond trunkfood. + namespace: "system_performance" + name: "message_queue_force_legacy" + description: "Whether to holdback concurrent MessageQueue (force legacy)." + bug: "336880969" +} + +flag { name: "adpf_gpu_report_actual_work_duration" is_exported: true namespace: "game" @@ -222,6 +231,15 @@ flag { } flag { + name: "message_queue_testability" + namespace: "system_performance" + is_exported: true + description: "Whether MessageQueue implements test APIs." + bug: "379472827" + is_fixed_read_only: true +} + +flag { name: "network_time_uses_shared_memory" namespace: "system_performance" description: "SystemClock.currentNetworkTimeMillis() reads network time offset from shared memory" diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java index 4db9bc333e2b..cd79e416531a 100644 --- a/core/java/android/os/health/SystemHealthManager.java +++ b/core/java/android/os/health/SystemHealthManager.java @@ -23,6 +23,8 @@ import android.annotation.Nullable; import android.annotation.SystemService; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.hardware.power.CpuHeadroomResult; +import android.hardware.power.GpuHeadroomResult; import android.os.BatteryStats; import android.os.Build; import android.os.Bundle; @@ -110,15 +112,16 @@ public class SystemHealthManager { } /** - * Provides an estimate of global available CPU headroom of the calling thread. + * Provides an estimate of global available CPU headroom. * <p> * * @param params params to customize the CPU headroom calculation, null to use default params. - * @return a single value a {@code Float.NaN} if it's temporarily unavailable. + * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable. * A valid value is ranged from [0, 100], where 0 indicates no more CPU resources can be * granted. - * @throws UnsupportedOperationException if the API is unsupported or the request params can't - * be served. + * @throws UnsupportedOperationException if the API is unsupported. + * @throws SecurityException if the TIDs of the params don't belong to the same process. + * @throws IllegalStateException if the TIDs of the params don't have the same affinity setting. */ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS) public @FloatRange(from = 0f, to = 100f) float getCpuHeadroom( @@ -127,8 +130,12 @@ public class SystemHealthManager { throw new UnsupportedOperationException(); } try { - return mHintManager.getCpuHeadroom( - params != null ? params.getInternal() : new CpuHeadroomParamsInternal())[0]; + final CpuHeadroomResult ret = mHintManager.getCpuHeadroom( + params != null ? params.getInternal() : new CpuHeadroomParamsInternal()); + if (ret == null || ret.getTag() != CpuHeadroomResult.globalHeadroom) { + return Float.NaN; + } + return ret.getGlobalHeadroom(); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -144,8 +151,7 @@ public class SystemHealthManager { * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable. * A valid value is ranged from [0, 100], where 0 indicates no more GPU resources can be * granted. - * @throws UnsupportedOperationException if the API is unsupported or the request params can't - * be served. + * @throws UnsupportedOperationException if the API is unsupported. */ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS) public @FloatRange(from = 0f, to = 100f) float getGpuHeadroom( @@ -154,8 +160,12 @@ public class SystemHealthManager { throw new UnsupportedOperationException(); } try { - return mHintManager.getGpuHeadroom( + final GpuHeadroomResult ret = mHintManager.getGpuHeadroom( params != null ? params.getInternal() : new GpuHeadroomParamsInternal()); + if (ret == null || ret.getTag() != GpuHeadroomResult.globalHeadroom) { + return Float.NaN; + } + return ret.getGlobalHeadroom(); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index 55ba4afc9c68..a7195834e6bb 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -58,7 +58,7 @@ flag { is_fixed_read_only: true namespace: "permissions" description: "enable enhanced confirmation incall apis" - bug: "310220212" + bug: "364535720" } flag { @@ -67,7 +67,7 @@ flag { is_fixed_read_only: true namespace: "permissions" description: "enable the blocking of certain app installs during an unknown call" - bug: "310220212" + bug: "364535720" } flag { @@ -436,3 +436,12 @@ flag { description: "Use profile labels from UserManager for default app section titles to allow partner customization" bug: "358369931" } + +flag { + name: "wallet_role_cross_user_enabled" + is_exported: true + is_fixed_read_only: true + namespace: "wallet_integration" + description: "Enable the Wallet role within profiles" + bug: "356107987" +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 23f76299d8c4..d5b525884ac1 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10999,6 +10999,25 @@ public final class Settings { "emergency_gesture_ui_last_started_millis"; /** + * Whether double tap the power button gesture is enabled. + * + * @hide + */ + @Readable + public static final String DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED = + "double_tap_power_button_gesture_enabled"; + + /** + * Double tap power button gesture behavior. + * 0 = Camera launch + * 1 = Wallet launch + * @hide + */ + @Readable + public static final String DOUBLE_TAP_POWER_BUTTON_GESTURE = + "double_tap_power_button_gesture"; + + /** * Whether the camera launch gesture to double tap the power button when the screen is off * should be disabled. * diff --git a/core/java/android/security/intrusiondetection/IIntrusionDetectionEventTransport.aidl b/core/java/android/security/intrusiondetection/IIntrusionDetectionEventTransport.aidl index 8759f72ca08a..eab1c156b6be 100644 --- a/core/java/android/security/intrusiondetection/IIntrusionDetectionEventTransport.aidl +++ b/core/java/android/security/intrusiondetection/IIntrusionDetectionEventTransport.aidl @@ -15,6 +15,7 @@ */ package android.security.intrusiondetection; + import android.security.intrusiondetection.IntrusionDetectionEvent; import com.android.internal.infra.AndroidFuture; @@ -24,18 +25,20 @@ oneway interface IIntrusionDetectionEventTransport { /** * Initialize the server side. */ - void initialize(in AndroidFuture<int> resultFuture); + void initialize(in AndroidFuture<boolean> resultFuture); /** - * Send intrusiondetection logging data to the backup destination. + * Send intrusiondetection logging data to the transport destination. * The data is a list of IntrusionDetectionEvent. * The IntrusionDetectionEvent is an abstract class that represents - * different type of events. + * different types of events. */ - void addData(in List<IntrusionDetectionEvent> events, in AndroidFuture<int> resultFuture); + void addData( + in List<IntrusionDetectionEvent> events, + in AndroidFuture<boolean> resultFuture); /** * Release the binder to the server. */ - void release(in AndroidFuture<int> resultFuture); + void release(in AndroidFuture<boolean> resultFuture); } diff --git a/core/java/android/security/intrusiondetection/IntrusionDetectionEvent.java b/core/java/android/security/intrusiondetection/IntrusionDetectionEvent.java index 538acf99c9fe..b479ca7a0b6f 100644 --- a/core/java/android/security/intrusiondetection/IntrusionDetectionEvent.java +++ b/core/java/android/security/intrusiondetection/IntrusionDetectionEvent.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.app.admin.ConnectEvent; import android.app.admin.DnsEvent; import android.app.admin.SecurityLog.SecurityEvent; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.security.Flags; @@ -31,14 +32,36 @@ import java.lang.annotation.RetentionPolicy; /** * A class that represents a intrusiondetection event. + * * @hide */ +@SystemApi @FlaggedApi(Flags.FLAG_AFL_API) public final class IntrusionDetectionEvent implements Parcelable { private static final String TAG = "IntrusionDetectionEvent"; + /** + * Event type representing a security-related event. + * This type is associated with a {@link SecurityEvent} object. + * + * @see SecurityEvent + */ public static final int SECURITY_EVENT = 0; + + /** + * Event type representing a network DNS event. + * This type is associated with a {@link DnsEvent} object. + * + * @see DnsEvent + */ public static final int NETWORK_EVENT_DNS = 1; + + /** + * Event type representing a network connection event. + * This type is associated with a {@link ConnectEvent} object. + * + * @see ConnectEvent + */ public static final int NETWORK_EVENT_CONNECT = 2; /** @hide */ @@ -67,6 +90,12 @@ public final class IntrusionDetectionEvent implements Parcelable { } }; + /** + * Creates an IntrusionDetectionEvent object with a + * {@link SecurityEvent} object as the event source. + * + * @param securityEvent The SecurityEvent object. + */ public IntrusionDetectionEvent(@NonNull SecurityEvent securityEvent) { mType = SECURITY_EVENT; mSecurityEvent = securityEvent; @@ -74,6 +103,12 @@ public final class IntrusionDetectionEvent implements Parcelable { mNetworkEventConnect = null; } + /** + * Creates an IntrusionDetectionEvent object with a + * {@link DnsEvent} object as the event source. + * + * @param dnsEvent The DnsEvent object. + */ public IntrusionDetectionEvent(@NonNull DnsEvent dnsEvent) { mType = NETWORK_EVENT_DNS; mNetworkEventDns = dnsEvent; @@ -81,6 +116,12 @@ public final class IntrusionDetectionEvent implements Parcelable { mNetworkEventConnect = null; } + /** + * Creates an IntrusionDetectionEvent object with a + * {@link ConnectEvent} object as the event source. + * + * @param connectEvent The ConnectEvent object. + */ public IntrusionDetectionEvent(@NonNull ConnectEvent connectEvent) { mType = NETWORK_EVENT_CONNECT; mNetworkEventConnect = connectEvent; diff --git a/core/java/android/security/intrusiondetection/IntrusionDetectionEventTransport.java b/core/java/android/security/intrusiondetection/IntrusionDetectionEventTransport.java new file mode 100644 index 000000000000..2e2d0f7f2dd2 --- /dev/null +++ b/core/java/android/security/intrusiondetection/IntrusionDetectionEventTransport.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 android.security.intrusiondetection; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.SuppressLint; +import android.content.ComponentName; +import android.content.Context; +import android.os.IBinder; +import android.util.CloseGuard; + +import android.security.Flags; +import android.security.intrusiondetection.IntrusionDetectionEvent; +import android.security.intrusiondetection.IIntrusionDetectionEventTransport; + +import com.android.internal.infra.AndroidFuture; + +import java.lang.AutoCloseable; +import java.util.List; + +/** + * A class that provides a stable API for transporting intrusion detection events + * to a transport location, such as a file or a network endpoint. + * + * This class acts as a bridge between the {@link IIntrusionDetectionEventTransport} + * interface and its implementations. It allows system components to add intrusion + * detection events ({@link IntrusionDetectionEvent}) to a transport queue, + * which will then be delivered to the specified location. + * + * Usage: + * 1. Obtain an instance of {@link IntrusionDetectionEventTransport} using the constructor. + * 2. Initialize the transport by calling {@link #initialize()}. + * 3. Add events to the transport queue using {@link #addData(List)}. + * 4. Release the transport when finished by calling {@link #release()}. + * + * Key Components: + * - {@link IIntrusionDetectionEventTransport}: The underlying AIDL interface + * for interacting with transport implementations. + * - {@link IntrusionDetectionEvent}: Represents a single event. + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_AFL_API) +@SuppressLint("NotCloseable") +public class IntrusionDetectionEventTransport { + IIntrusionDetectionEventTransport mBinderImpl = new TransportImpl(); + + /** + * Returns the binder interface for this transport. + */ + @NonNull + public IBinder getBinder() { + return mBinderImpl.asBinder(); + } + + /** + * Initializes the transport. + * + * @return whether the initialization was successful. + */ + public boolean initialize() { + return false; + } + + /** + * Adds data to the transport. + * + * @param events the events to add. + * @return whether the addition was successful. + */ + public boolean addData(@NonNull List<IntrusionDetectionEvent> events) { + return false; + } + + /** + * Releases the transport. + * + * The release() method is a callback implemented by the concrete transport + * endpoint. + * The "SuppressLint" annotation is used to allow the release() method to be + * included in the API without requiring the class to implement AutoCloseable. + * + * @return whether the release was successful. + */ + public boolean release() { + return false; + } + + /** + * Bridge between the actual IIntrusionDetectionEventTransport implementation + * and the stable API. If the binder interface needs to change, we use this + * layer to translate so that we can decouple those framework-side changes + * from the IntrusionDetectionEventTransport implementations. + */ + class TransportImpl extends IIntrusionDetectionEventTransport.Stub { + @Override + public void initialize(AndroidFuture<Boolean> resultFuture) { + try { + boolean result = IntrusionDetectionEventTransport.this.initialize(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } + } + + @Override + public void addData( + List<IntrusionDetectionEvent> events, + AndroidFuture<Boolean> resultFuture) { + try { + boolean result = IntrusionDetectionEventTransport.this.addData(events); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } + } + + @Override + public void release(AndroidFuture<Boolean> resultFuture) { + try { + boolean result = IntrusionDetectionEventTransport.this.release(); + resultFuture.complete(result); + } catch (RuntimeException e) { + resultFuture.cancel(/* mayInterruptIfRunning */ true); + } + } + } +}
\ No newline at end of file diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig index 6c92991ceff6..a5c837b88fa4 100644 --- a/core/java/android/security/responsible_apis_flags.aconfig +++ b/core/java/android/security/responsible_apis_flags.aconfig @@ -96,6 +96,21 @@ flag { } flag { + name: "prevent_intent_redirect_throw_exception_if_nested_keys_not_collected" + namespace: "responsible_apis" + description: "Prevent intent redirect attacks by throwing exception if the intent does not collect nested keys" + bug: "361143368" +} + +flag { + name: "prevent_intent_redirect_collect_nested_keys_on_server_if_not_collected" + namespace: "responsible_apis" + description: "Prevent intent redirect attacks by collecting nested keys on server if not yet collected" + bug: "361143368" + is_fixed_read_only: true +} + +flag { name: "enable_intent_matching_flags" is_exported: true namespace: "permissions" @@ -110,3 +125,16 @@ flag { description: "Android Advanced Protection Mode Feature: Disable Install Unknown Sources" bug: "369361373" } + +flag { + name: "aapm_feature_memory_tagging_extension" + namespace: "responsible_apis" + description: "Android Advanced Protection Mode Feature: Memory Tagging Extension" + bug: "378931989" +} +flag { + name: "aapm_feature_disable_cellular_2g" + namespace: "responsible_apis" + description: "Android Advanced Protection Mode Feature: Disable Cellular 2G" + bug: "377748286" +} diff --git a/core/java/android/service/notification/SystemZenRules.java b/core/java/android/service/notification/SystemZenRules.java index ebb8569ead49..f11ce1621f93 100644 --- a/core/java/android/service/notification/SystemZenRules.java +++ b/core/java/android/service/notification/SystemZenRules.java @@ -122,17 +122,16 @@ public final class SystemZenRules { @Nullable public static String getTriggerDescriptionForScheduleTime(Context context, @NonNull ScheduleInfo schedule) { - final StringBuilder sb = new StringBuilder(); String daysSummary = getDaysOfWeekShort(context, schedule); if (daysSummary == null) { // no use outputting times without dates return null; } - sb.append(daysSummary); - sb.append(context.getString(R.string.zen_mode_trigger_summary_divider_text)); - sb.append(getTimeSummary(context, schedule)); - - return sb.toString(); + return context.getString( + R.string.zen_mode_trigger_summary_combined, + daysSummary, + getTimeSummary(context, schedule) + ); } /** diff --git a/core/java/android/service/quickaccesswallet/flags.aconfig b/core/java/android/service/quickaccesswallet/flags.aconfig index 75a93091eec3..7225f27c4555 100644 --- a/core/java/android/service/quickaccesswallet/flags.aconfig +++ b/core/java/android/service/quickaccesswallet/flags.aconfig @@ -6,4 +6,11 @@ flag { namespace: "wallet_integration" description: "Option to launch the Wallet app on double-tap of the power button" bug: "378469025" +} + +flag { + name: "launch_selected_card_from_qs_tile" + namespace: "wallet_integration" + description: "When the wallet QS tile is tapped, launch the selected card pending intent instead of the home screen pending intent." + bug: "378469025" }
\ No newline at end of file diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java index 47ec0801f551..e20d5e996e66 100644 --- a/core/java/android/service/wearable/WearableSensingService.java +++ b/core/java/android/service/wearable/WearableSensingService.java @@ -27,6 +27,7 @@ import android.app.ambientcontext.AmbientContextEvent; import android.app.ambientcontext.AmbientContextEventRequest; import android.app.wearable.Flags; import android.app.wearable.IWearableSensingCallback; +import android.app.wearable.WearableConnection; import android.app.wearable.WearableSensingDataRequest; import android.app.wearable.WearableSensingManager; import android.content.Context; @@ -380,7 +381,11 @@ public abstract class WearableSensingService extends Service { * * @param secureWearableConnection The secure connection to the wearable. * @param statusConsumer The consumer for the service status. + * @deprecated Use {@link #onSecureConnectionProvided(ParcelFileDescriptor, PersistableBundle, + * Consumer)} instead to receive a remote wearable device connection. */ + @FlaggedApi(Flags.FLAG_ENABLE_CONCURRENT_WEARABLE_CONNECTIONS) + @Deprecated @BinderThread public void onSecureConnectionProvided( @NonNull ParcelFileDescriptor secureWearableConnection, @@ -389,12 +394,20 @@ public abstract class WearableSensingService extends Service { } /** - * Called when a secure connection to the wearable is available. + * Called when a secure connection to the wearable is available. See {@link + * WearableSensingManager#provideConnection(WearableConnection, Executor)} for details about the + * secure connection. + * + * <p>When the {@code secureWearableConnection} is closed, the system will send a {@link + * WearableSensingManager#STATUS_CHANNEL_ERROR} status code to the error callback provided by + * the caller of {@link WearableSensingManager#provideConnection(WearableConnection, Executor)}. + * + * <p>The implementing class should override this method. It should return an appropriate status + * code via {@code statusConsumer} after receiving the {@code secureWearableConnection}. * * @param secureWearableConnection The secure connection to the wearable. * @param metadata Metadata related to the provided connection. * @param statusConsumer The consumer for the service status. - * @see #onSecureConnectionProvided(ParcelFileDescriptor, Consumer) */ @FlaggedApi(Flags.FLAG_ENABLE_CONCURRENT_WEARABLE_CONNECTIONS) @BinderThread diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java index e0d4ec1ca826..a337ba2a57fb 100644 --- a/core/java/android/text/style/TtsSpan.java +++ b/core/java/android/text/style/TtsSpan.java @@ -108,13 +108,11 @@ public class TtsSpan implements ParcelableSpan { /** * The text associated with this span is a time, consisting of a number of - * hours, minutes, and seconds specified with {@link #ARG_HOURS}, {@link #ARG_MINUTES}, and - * {@link #ARG_SECONDS}. + * hours and minutes, specified with {@link #ARG_HOURS} and + * {@link #ARG_MINUTES}. * Also accepts the arguments {@link #ARG_GENDER}, * {@link #ARG_ANIMACY}, {@link #ARG_MULTIPLICITY} and - * {@link #ARG_CASE}. This is different from {@link #TYPE_DURATION}. This should be used to - * convey a particular moment in time, such as a clock time, while {@link #TYPE_DURATION} should - * be used to convey an interval of time. + * {@link #ARG_CASE}. */ public static final String TYPE_TIME = "android.type.time"; @@ -312,18 +310,16 @@ public class TtsSpan implements ParcelableSpan { public static final String ARG_UNIT = "android.arg.unit"; /** - * Argument used to specify the hours of a time or duration. The hours should be - * provided as an integer in the range from 0 up to and including 24 for - * {@link #TYPE_TIME}. - * Can be used with {@link #TYPE_TIME} or {@link #TYPE_DURATION}. + * Argument used to specify the hours of a time. The hours should be + * provided as an integer in the range from 0 up to and including 24. + * Can be used with {@link #TYPE_TIME}. */ public static final String ARG_HOURS = "android.arg.hours"; /** - * Argument used to specify the minutes of a time or duration. The minutes should be - * provided as an integer in the range from 0 up to and including 59 for - * {@link #TYPE_TIME}. - * Can be used with {@link #TYPE_TIME} or {@link #TYPE_DURATION}. + * Argument used to specify the minutes of a time. The minutes should be + * provided as an integer in the range from 0 up to and including 59. + * Can be used with {@link #TYPE_TIME}. */ public static final String ARG_MINUTES = "android.arg.minutes"; diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index a1a9fc697271..c9d560c3424b 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE; import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS; import static android.hardware.flags.Flags.FLAG_OVERLAYPROPERTIES_CLASS_API; +import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_GET_SUPPORTED_REFRESH_RATES; import static com.android.server.display.feature.flags.Flags.FLAG_HIGHEST_HDR_SDR_RATIO_API; import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_HAS_ARR_SUPPORT; import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE; @@ -63,6 +64,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -1207,17 +1209,36 @@ public final class Display { /** * Get the supported refresh rates of this display in frames per second. - * <p> - * This method only returns refresh rates for the display's default modes. For more options, use - * {@link #getSupportedModes()}. * - * @deprecated use {@link #getSupportedModes()} instead + * <ul> + * <li> Android version {@link Build.VERSION_CODES#BAKLAVA} and above: + * returns display supported render rates. + * <li> Android version {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and below: + * This method only returns refresh rates for the display's default modes. For more options, + * use {@link #getSupportedModes()}. + * </ul> */ - @Deprecated - public float[] getSupportedRefreshRates() { + @FlaggedApi(FLAG_ENABLE_GET_SUPPORTED_REFRESH_RATES) + public @NonNull float[] getSupportedRefreshRates() { + synchronized (mLock) { + updateDisplayInfoLocked(); + final float[] refreshRates = mDisplayInfo.getDefaultRefreshRates(); + Objects.requireNonNull(refreshRates); + return refreshRates; + } + } + + /** + * @hide + */ + @TestApi + @SuppressLint({"UnflaggedApi"}) // Usage in the CTS to test backward compatibility. + public @NonNull float[] getSupportedRefreshRatesLegacy() { synchronized (mLock) { updateDisplayInfoLocked(); - return mDisplayInfo.getDefaultRefreshRates(); + final float[] refreshRates = mDisplayInfo.getDefaultRefreshRatesLegacy(); + Objects.requireNonNull(refreshRates); + return refreshRates; } } @@ -1279,8 +1300,19 @@ public final class Display { } } + /** + * Represents the {@link FrameRateCategory} for the Normal frame rate + * + * @see FrameRateCategory + */ @FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE) public static final int FRAME_RATE_CATEGORY_NORMAL = 0; + + /** + * Represents the {@link FrameRateCategory} for the High frame rate + * + * @see FrameRateCategory + */ @FlaggedApi(FLAG_ENABLE_GET_SUGGESTED_FRAME_RATE) public static final int FRAME_RATE_CATEGORY_HIGH = 1; @@ -2438,6 +2470,8 @@ public final class Display { * constrained by the system. * @hide */ + @SuppressWarnings("UnflaggedApi") // For testing only + @TestApi public float getVsyncRate() { return mVsyncRate; } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 4ff04d5c1fa6..8b6458a54c43 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -210,6 +210,11 @@ public final class DisplayInfo implements Parcelable { public FrameRateCategoryRate frameRateCategoryRate; /** + * All the refresh rates supported in the active mode. + */ + public float[] supportedRefreshRates = new float[0]; + + /** * The default display mode. */ public int defaultModeId; @@ -449,6 +454,7 @@ public final class DisplayInfo implements Parcelable { && modeId == other.modeId && hasArrSupport == other.hasArrSupport && Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate) + && Arrays.equals(supportedRefreshRates, other.supportedRefreshRates) && defaultModeId == other.defaultModeId && userPreferredModeId == other.userPreferredModeId && Arrays.equals(supportedModes, other.supportedModes) @@ -512,6 +518,8 @@ public final class DisplayInfo implements Parcelable { renderFrameRate = other.renderFrameRate; hasArrSupport = other.hasArrSupport; frameRateCategoryRate = other.frameRateCategoryRate; + supportedRefreshRates = Arrays.copyOf( + other.supportedRefreshRates, other.supportedRefreshRates.length); defaultModeId = other.defaultModeId; userPreferredModeId = other.userPreferredModeId; supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length); @@ -571,6 +579,11 @@ public final class DisplayInfo implements Parcelable { hasArrSupport = source.readBoolean(); frameRateCategoryRate = source.readParcelable(null, android.view.FrameRateCategoryRate.class); + int numOfSupportedRefreshRates = source.readInt(); + supportedRefreshRates = new float[numOfSupportedRefreshRates]; + for (int i = 0; i < numOfSupportedRefreshRates; i++) { + supportedRefreshRates[i] = source.readFloat(); + } defaultModeId = source.readInt(); userPreferredModeId = source.readInt(); int nModes = source.readInt(); @@ -646,6 +659,10 @@ public final class DisplayInfo implements Parcelable { dest.writeFloat(renderFrameRate); dest.writeBoolean(hasArrSupport); dest.writeParcelable(frameRateCategoryRate, flags); + dest.writeInt(supportedRefreshRates.length); + for (float supportedRefreshRate : supportedRefreshRates) { + dest.writeFloat(supportedRefreshRate); + } dest.writeInt(defaultModeId); dest.writeInt(userPreferredModeId); dest.writeInt(supportedModes.length); @@ -750,9 +767,19 @@ public final class DisplayInfo implements Parcelable { } /** - * Returns the list of supported refresh rates in the default mode. + * Returns the list of supported refresh rates in the active mode. */ public float[] getDefaultRefreshRates() { + if (supportedRefreshRates.length == 0) { + return getDefaultRefreshRatesLegacy(); + } + return Arrays.copyOf(supportedRefreshRates, supportedRefreshRates.length); + } + + /** + * Returns the list of supported refresh rates in the default mode. + */ + public float[] getDefaultRefreshRatesLegacy() { Display.Mode[] modes = appsSupportedModes; ArraySet<Float> rates = new ArraySet<>(); Display.Mode defaultMode = getDefaultMode(); @@ -898,6 +925,8 @@ public final class DisplayInfo implements Parcelable { sb.append(hasArrSupport); sb.append(", frameRateCategoryRate "); sb.append(frameRateCategoryRate); + sb.append(", supportedRefreshRates "); + sb.append(Arrays.toString(supportedRefreshRates)); sb.append(", defaultMode "); sb.append(defaultModeId); sb.append(", userPreferredModeId "); diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index 58ef5efe846f..6cd4a4033adf 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -260,6 +260,15 @@ public final class InputWindowHandle { touchableRegionSurfaceControl = new WeakReference<>(bounds); } + /** + * Resize the window touchable region. + * @param rect new touchable region rectangle. + */ + public void setTouchableRegion(Rect rect) { + touchableRegion.set(rect); + } + + public void setWindowToken(IBinder iwindow) { windowToken = iwindow; } diff --git a/core/java/android/view/ScrollCaptureSearchResults.java b/core/java/android/view/ScrollCaptureSearchResults.java index 3469b9dc7103..b1ce949f9919 100644 --- a/core/java/android/view/ScrollCaptureSearchResults.java +++ b/core/java/android/view/ScrollCaptureSearchResults.java @@ -16,10 +16,16 @@ package android.view; +import static android.view.flags.Flags.scrollCaptureTargetZOrderFix; + +import static java.util.Comparator.comparing; import static java.util.Objects.requireNonNull; +import static java.util.Objects.requireNonNullElse; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UiThread; +import android.graphics.Point; import android.graphics.Rect; import android.os.CancellationSignal; import android.util.IndentingPrintWriter; @@ -113,7 +119,9 @@ public final class ScrollCaptureSearchResults { private void signalComplete() { mComplete = true; - mTargets.sort(PRIORITY_ORDER); + if (!scrollCaptureTargetZOrderFix()) { + mTargets.sort(PRIORITY_ORDER); + } if (mOnCompleteListener != null) { mOnCompleteListener.run(); mOnCompleteListener = null; @@ -125,14 +133,73 @@ public final class ScrollCaptureSearchResults { return new ArrayList<>(mTargets); } + private Rect getScrollBoundsInWindow(@Nullable ScrollCaptureTarget target) { + if (target == null || target.getScrollBounds() == null) { + return new Rect(); + } + Rect windowRect = new Rect(target.getScrollBounds()); + Point windowPosition = target.getPositionInWindow(); + windowRect.offset(windowPosition.x, windowPosition.y); + return windowRect; + } + /** * Get the top ranked result out of all completed requests. * * @return the top ranked result */ + @Nullable public ScrollCaptureTarget getTopResult() { - ScrollCaptureTarget target = mTargets.isEmpty() ? null : mTargets.get(0); - return target != null && target.getScrollBounds() != null ? target : null; + if (!scrollCaptureTargetZOrderFix()) { + ScrollCaptureTarget target = mTargets.isEmpty() ? null : mTargets.get(0); + return target != null && target.getScrollBounds() != null ? target : null; + } + List<ScrollCaptureTarget> filtered = new ArrayList<>(); + + mTargets.removeIf(a -> nullOrEmpty(a.getScrollBounds())); + + // Remove scroll targets obscured or covered by other scrolling views. + nextTarget: + for (int i = 0; i < mTargets.size(); i++) { + ScrollCaptureTarget current = mTargets.get(i); + + View currentView = current.getContainingView(); + + // Nested scroll containers: + // Check if the next view is a child of the current. If so, skip the current. + if (i + 1 < mTargets.size()) { + ScrollCaptureTarget next = mTargets.get(i + 1); + View nextView = next.getContainingView(); + // Honor explicit include hint on parent as escape hatch (unless both have it) + if (isDescendant(currentView, nextView) + && (!hasIncludeHint(currentView) || hasIncludeHint(nextView))) { + continue; + } + } + + // Check if any views will be drawn partially or fully over this one. + for (int j = i + 1; j < mTargets.size(); j++) { + ScrollCaptureTarget above = mTargets.get(j); + if (Rect.intersects(getScrollBoundsInWindow(current), + getScrollBoundsInWindow(above))) { + continue nextTarget; + } + } + + filtered.add(current); + } + + // natural order, false->true + Comparator<ScrollCaptureTarget> byIncludeHintPresence = comparing( + ScrollCaptureSearchResults::hasIncludeHint); + + // natural order, smallest->largest area + Comparator<ScrollCaptureTarget> byArea = comparing( + target -> area(requireNonNullElse(target.getScrollBounds(), new Rect()))); + + // The top result is the last one (with include hint if present, then by largest area) + filtered.sort(byIncludeHintPresence.thenComparing(byArea)); + return filtered.isEmpty() ? null : filtered.getLast(); } private class SearchRequest implements Consumer<Rect> { @@ -226,6 +293,10 @@ public final class ScrollCaptureSearchResults { return r == null || r.isEmpty(); } + private static boolean hasIncludeHint(ScrollCaptureTarget target) { + return hasIncludeHint(target.getContainingView()); + } + private static boolean hasIncludeHint(View view) { return (view.getScrollCaptureHint() & View.SCROLL_CAPTURE_HINT_INCLUDE) != 0; } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 68674dd402b4..dd9a95e58bd1 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -1932,6 +1932,7 @@ public final class SurfaceControl implements Parcelable { public float renderFrameRate; public boolean hasArrSupport; public FrameRateCategoryRate frameRateCategoryRate; + public float[] supportedRefreshRates; public int[] supportedColorModes; public int activeColorMode; @@ -1951,6 +1952,7 @@ public final class SurfaceControl implements Parcelable { + ", renderFrameRate=" + renderFrameRate + ", hasArrSupport=" + hasArrSupport + ", frameRateCategoryRate=" + frameRateCategoryRate + + ", supportedRefreshRates=" + Arrays.toString(supportedRefreshRates) + ", supportedColorModes=" + Arrays.toString(supportedColorModes) + ", activeColorMode=" + activeColorMode + ", hdrCapabilities=" + hdrCapabilities @@ -1972,14 +1974,15 @@ public final class SurfaceControl implements Parcelable { && Objects.equals(hdrCapabilities, that.hdrCapabilities) && preferredBootDisplayMode == that.preferredBootDisplayMode && hasArrSupport == that.hasArrSupport - && Objects.equals(frameRateCategoryRate, that.frameRateCategoryRate); + && Objects.equals(frameRateCategoryRate, that.frameRateCategoryRate) + && Arrays.equals(supportedRefreshRates, that.supportedRefreshRates); } @Override public int hashCode() { return Objects.hash(Arrays.hashCode(supportedDisplayModes), activeDisplayModeId, renderFrameRate, activeColorMode, hdrCapabilities, hasArrSupport, - frameRateCategoryRate); + frameRateCategoryRate, Arrays.hashCode(supportedRefreshRates)); } } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 4df7649e1c9b..b0051cefb21b 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -932,7 +932,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall /** * Sets the desired amount of HDR headroom to be used when HDR content is presented on this - * SurfaceView. + * SurfaceView. This is expressed as the ratio of maximum HDR white point over the SDR + * white point, not as absolute nits. * * <p>By default the system will choose an amount of HDR headroom that is appropriate * for the underlying device capabilities & bit-depth of the panel. However, for some types @@ -946,6 +947,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall * See {@link Display#getHdrSdrRatio()} for more information as well as how to query the * current value.</p> * + * <p>Note: This API operates independently of both the + * {@link Window#setColorMode Widow color mode} and the + * {@link Window#setDesiredHdrHeadroom Window desiredHdrHeadroom}</p> + * * @param desiredHeadroom The amount of HDR headroom that is desired. Must be >= 1.0 (no HDR) * and <= 10,000.0. Passing 0.0 will reset to the default, automatically * chosen value. diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 4a9916c007c4..949b667f0b7a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -20,6 +20,7 @@ import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static android.view.flags.Flags.FLAG_TOOLKIT_VIEWGROUP_SET_REQUESTED_FRAME_RATE_API; import static android.view.flags.Flags.toolkitViewgroupSetRequestedFrameRateApi; +import static android.view.flags.Flags.scrollCaptureTargetZOrderFix; import android.animation.LayoutTransition; import android.annotation.CallSuper; @@ -7657,6 +7658,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @NonNull Rect localVisibleRect, @NonNull Point windowOffset, @NonNull Consumer<ScrollCaptureTarget> targets) { + // Only visible views can be captured. + if (getVisibility() != View.VISIBLE) { + return; + } + if (getClipToPadding() && !localVisibleRect.intersect(mPaddingLeft, mPaddingTop, (mRight - mLeft) - mPaddingRight, (mBottom - mTop) - mPaddingBottom)) { return; @@ -7665,19 +7671,39 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Dispatch to self first. super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets); + final int childrenCount = mChildrenCount; + if (childrenCount == 0) { + return; + } + // Skip children if descendants excluded. if ((getScrollCaptureHint() & SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) != 0) { return; } - final Rect tmpRect = getTempRect(); - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); + + ArrayList<View> preorderedList = null; + boolean customOrder = false; + if (scrollCaptureTargetZOrderFix()) { + preorderedList = buildOrderedChildList(); + customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); + } + final View[] children = mChildren; + for (int i = 0; i < childrenCount; i++) { + View child; + if (scrollCaptureTargetZOrderFix()) { + // Traverse children in the same order they will be drawn (honors Z if set) + final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); + child = getAndVerifyPreorderedView(preorderedList, children, childIndex); + } else { + child = children[i]; + } + // Only visible views can be captured. if (child.getVisibility() != View.VISIBLE) { continue; } + // Offset the given rectangle (in parent's local coordinates) into child's coordinate // space and clip the result to the child View's bounds, padding and clipRect as needed. // If the resulting rectangle is not empty, the request is forwarded to the child. @@ -7706,6 +7732,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager child.dispatchScrollCaptureSearch(tmpRect, childWindowOffset, targets); } } + if (preorderedList != null) { + preorderedList.clear(); + } } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 072a835eb664..a0feccd87a81 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1236,6 +1236,8 @@ public final class ViewRootImpl implements ViewParent, private @ActivityInfo.ColorMode int mCurrentColorMode = ActivityInfo.COLOR_MODE_DEFAULT; private long mColorModeLastSetMillis = -1; + private final boolean mIsSubscribeGranularDisplayEventsEnabled; + public ViewRootImpl(Context context, Display display) { this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout()); } @@ -1333,6 +1335,8 @@ public final class ViewRootImpl implements ViewParent, // Disable DRAW_WAKE_LOCK starting U. mDisableDrawWakeLock = CompatChanges.isChangeEnabled(DISABLE_DRAW_WAKE_LOCK) && disableDrawWakeLock(); + mIsSubscribeGranularDisplayEventsEnabled = + com.android.server.display.feature.flags.Flags.subscribeGranularDisplayEvents(); } public static void addFirstDrawHandler(Runnable callback) { @@ -1810,14 +1814,22 @@ public final class ViewRootImpl implements ViewParent, mAccessibilityInteractionConnectionManager, mHandler); mAccessibilityManager.addHighContrastTextStateChangeListener( mExecutor, mHighContrastTextManager); + + + long eventsToBeRegistered = + (mIsSubscribeGranularDisplayEventsEnabled) + ? DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED + | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_STATE + | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED + : DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED + | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED + | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED; DisplayManagerGlobal .getInstance() .registerDisplayListener( mDisplayListener, mHandler, - DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED - | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_CHANGED - | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED, + eventsToBeRegistered, mBasePackageName); if (forceInvertColor()) { @@ -8876,11 +8888,6 @@ public final class ViewRootImpl implements ViewParent, SyntheticTouchNavigationHandler() { super(true); - int gestureDetectorVelocityStrategy = - android.companion.virtual.flags.Flags - .impulseVelocityStrategyForTouchNavigation() - ? VelocityTracker.VELOCITY_TRACKER_STRATEGY_IMPULSE - : VelocityTracker.VELOCITY_TRACKER_STRATEGY_DEFAULT; mGestureDetector = new GestureDetector(mContext, new GestureDetector.OnGestureListener() { @Override @@ -8920,7 +8927,7 @@ public final class ViewRootImpl implements ViewParent, } }, /* handler= */ null, - gestureDetectorVelocityStrategy); + VelocityTracker.VELOCITY_TRACKER_STRATEGY_IMPULSE); } public void process(MotionEvent event) { diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 381006c8a21b..39533344173b 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1334,6 +1334,9 @@ public abstract class Window { * <p>The requested color mode is not guaranteed to be honored. Please refer to * {@link #getColorMode()} for more information.</p> * + * <p>Note: This does not impact SurfaceViews or SurfaceControls, as those have their own + * independent color mode and HDR parameters.</p> + * * @see #getColorMode() * @see Display#isWideColorGamut() * @see Configuration#isScreenWideColorGamut() @@ -1361,6 +1364,9 @@ public abstract class Window { * See {@link Display#getHdrSdrRatio()} for more information as well as how to query the * current value.</p> * + * <p>Note: This does not impact SurfaceViews or SurfaceControls, as those have their own + * independent desired HDR headroom and HDR capabilities.</p> + * * @param desiredHeadroom The amount of HDR headroom that is desired. Must be >= 1.0 (no HDR) * and <= 10,000.0. Passing 0.0 will reset to the default, automatically * chosen value. diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index b4b0687eb498..1e8cad61381c 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1426,8 +1426,9 @@ public interface WindowManager extends ViewManager { "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE"; /** - * Activity-level {@link android.content.pm.PackageManager.Property PackageManager.Property} - * that specifies whether this activity can declare or request + * Application or Activity level + * {@link android.content.pm.PackageManager.Property PackageManager.Property} + * that specifies whether this package or activity can declare or request * {@link android.R.attr#screenOrientation fixed orientation}, * {@link android.R.attr#minAspectRatio max aspect ratio}, * {@link android.R.attr#maxAspectRatio min aspect ratio} @@ -1438,6 +1439,13 @@ public interface WindowManager extends ViewManager { * * <p><b>Syntax:</b> * <pre> + * <application> + * <property + * android:name="android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE" + * android:value="false"/> + * </application> + * </pre>or + * <pre> * <activity> * <property * android:name="android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY" @@ -1446,7 +1454,7 @@ public interface WindowManager extends ViewManager { * </pre> * @hide */ - // TODO(b/357141415): Make this public API. + // TODO(b/357141415): Remove this from sdk 37 String PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY = "android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY"; diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 0dfaf4149ce5..88ccf88d40f6 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -959,6 +959,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par CONTENT_CHANGE_TYPE_CONTENT_INVALID, CONTENT_CHANGE_TYPE_ERROR, CONTENT_CHANGE_TYPE_ENABLED, + CONTENT_CHANGE_TYPE_CHECKED, + CONTENT_CHANGE_TYPE_EXPANDED, CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION, }) public @interface ContentChangeTypes {} @@ -1241,6 +1243,16 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par case CONTENT_CHANGE_TYPE_ERROR: return "CONTENT_CHANGE_TYPE_ERROR"; case CONTENT_CHANGE_TYPE_ENABLED: return "CONTENT_CHANGE_TYPE_ENABLED"; default: { + if (Flags.triStateChecked()) { + if (type == CONTENT_CHANGE_TYPE_CHECKED) { + return "CONTENT_CHANGE_TYPE_CHECKED"; + } + } + if (Flags.a11yExpansionStateApi()) { + if (type == CONTENT_CHANGE_TYPE_EXPANDED) { + return "CONTENT_CHANGE_TYPE_EXPANDED"; + } + } if (Flags.supplementalDescription()) { if (type == CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION) { return "CONTENT_CHANGE_TYPE_SUPPLEMENTAL_DESCRIPTION"; diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 0204517e869a..df0c5a34e992 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -6551,15 +6551,23 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Range type: indeterminate. * + * When using this type, the {@code min}, {@code max}, and {@code current} values used to + * construct an instance may be ignored. + * + * @see #INDETERMINATE + */ + @FlaggedApi(Flags.FLAG_INDETERMINATE_RANGE_INFO) + public static final int RANGE_TYPE_INDETERMINATE = 3; + + /** * A {@link RangeInfo} type used to represent a node which may typically expose range * information but is presently in an indeterminate state, such as a {@link * android.widget.ProgressBar} representing a loading operation of unknown duration. - * When using this type, the {@code min}, {@code max}, and {@code current} values used to - * construct an instance may be ignored. It is recommended to use {@code Float.NaN} for - * these values. */ + @NonNull @FlaggedApi(Flags.FLAG_INDETERMINATE_RANGE_INFO) - public static final int RANGE_TYPE_INDETERMINATE = 3; + public static final RangeInfo INDETERMINATE = new RangeInfo(RANGE_TYPE_INDETERMINATE, 0.0f, + 0.0f, 0.0f); private int mType; private float mMin; diff --git a/core/java/android/view/flags/scroll_capture.aconfig b/core/java/android/view/flags/scroll_capture.aconfig new file mode 100644 index 000000000000..fdf9c1ed8ad2 --- /dev/null +++ b/core/java/android/view/flags/scroll_capture.aconfig @@ -0,0 +1,13 @@ +package: "android.view.flags" +container: "system" + +flag { + name: "scroll_capture_target_z_order_fix" + namespace: "system_ui" + description: "Always prefer targets with higher z-order" + bug: "365969802" + metadata { + purpose: PURPOSE_BUGFIX + } +} + diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 6e43d0f1c6cd..2a8a92882310 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -16,6 +16,8 @@ package android.widget; +import static android.view.accessibility.Flags.indeterminateRangeInfo; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -2364,15 +2366,22 @@ public class ProgressBar extends View { public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfoInternal(info); - if (!isIndeterminate()) { - AccessibilityNodeInfo.RangeInfo rangeInfo = AccessibilityNodeInfo.RangeInfo.obtain( + AccessibilityNodeInfo.RangeInfo rangeInfo = null; + if (isIndeterminate()) { + if (indeterminateRangeInfo()) { + rangeInfo = AccessibilityNodeInfo.RangeInfo.INDETERMINATE; + } + } else { + rangeInfo = new AccessibilityNodeInfo.RangeInfo( AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(), getProgress()); - info.setRangeInfo(rangeInfo); } - // Only set the default state description when custom state descripton is null. + info.setRangeInfo(rangeInfo); + + // Only set the default state description when custom state description is null. if (getStateDescription() == null) { + // TODO(b/380340432): Remove after accessibility services stop relying on this. if (isIndeterminate()) { info.setStateDescription(getResources().getString(R.string.in_progress)); } else { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index d85f5266b3fb..7e3b90444429 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1298,7 +1298,13 @@ public class RemoteViews implements Parcelable, Filter { @Override public void visitUris(@NonNull Consumer<Uri> visitor) { - if (mIntentId != -1 || mItems == null) { + if (mItems == null) { + // Null item indicates adapter conversion took place, so the URIs in cached items + // need to be validated. + RemoteCollectionItems cachedItems = mCollectionCache.getItemsForId(mIntentId); + if (cachedItems != null) { + cachedItems.visitUris(visitor); + } return; } @@ -8483,8 +8489,11 @@ public class RemoteViews implements Parcelable, Filter { } try { LoadedApk.checkAndUpdateApkPaths(mApplication); - return context.createApplicationContext(mApplication, + Context applicationContext = context.createApplicationContext(mApplication, Context.CONTEXT_RESTRICTED); + // Get the correct apk paths while maintaining the current context's configuration. + return applicationContext.createConfigurationContext( + context.getResources().getConfiguration()); } catch (NameNotFoundException e) { Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found"); } @@ -9330,7 +9339,11 @@ public class RemoteViews implements Parcelable, Filter { Set<Integer> bitmapIdSet = getBitmapIdsUsedByActions(new HashSet<>()); int result = 0; for (int bitmapId: bitmapIdSet) { - result += mBitmapCache.getBitmapForId(bitmapId).getAllocationByteCount(); + Bitmap currentBitmap = mBitmapCache.getBitmapForId(bitmapId); + if (currentBitmap == null) { + continue; + } + result += currentBitmap.getAllocationByteCount(); } return result; diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java index fdaab66f2de3..2b370b9797e5 100644 --- a/core/java/android/window/WindowContext.java +++ b/core/java/android/window/WindowContext.java @@ -94,6 +94,23 @@ public class WindowContext extends ContextWrapper implements WindowProvider { mController.attachToDisplayArea(mType, getDisplayId(), mOptions); } + /** + * Updates this context to a new displayId. + * <p> + * Note that this doesn't re-parent previously attached windows (they should be removed and + * re-added manually after this is called). Resources associated with this context will have + * the correct value and configuration for the new display after this is called. + */ + @Override + public void updateDisplay(int displayId) { + if (displayId == getDisplayId()) { + return; + } + super.updateDisplay(displayId); + mController.detachIfNeeded(); + mController.attachToDisplayArea(mType, displayId, mOptions); + } + @Override public Object getSystemService(String name) { if (WINDOW_SERVICE.equals(name)) { diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java index 15adc80add01..6e76d8d345b2 100644 --- a/core/java/android/window/WindowTokenClient.java +++ b/core/java/android/window/WindowTokenClient.java @@ -19,6 +19,8 @@ import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded; import static android.window.ConfigurationHelper.isDifferentDisplay; import static android.window.ConfigurationHelper.shouldUpdateResources; +import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; + import android.annotation.AnyThread; import android.annotation.MainThread; import android.annotation.NonNull; @@ -104,7 +106,7 @@ public class WindowTokenClient extends Binder { * @param newConfig the updated {@link Configuration} * @param newDisplayId the updated {@link android.view.Display} ID */ - @VisibleForTesting + @VisibleForTesting(visibility = PACKAGE) @MainThread public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */); @@ -113,7 +115,7 @@ public class WindowTokenClient extends Binder { /** * Posts an {@link #onConfigurationChanged} to the main thread. */ - @VisibleForTesting + @VisibleForTesting(visibility = PACKAGE) public void postOnConfigurationChanged(@NonNull Configuration newConfig, int newDisplayId) { mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig, newDisplayId, true /* shouldReportConfigChange */).recycleOnUse()); @@ -232,7 +234,7 @@ public class WindowTokenClient extends Binder { /** * Called when the attached window is removed from the display. */ - @VisibleForTesting + @VisibleForTesting(visibility = PACKAGE) @MainThread public void onWindowTokenRemoved() { final Context context = mContextRef.get(); diff --git a/core/java/android/window/WindowTokenClientController.java b/core/java/android/window/WindowTokenClientController.java index abf7bb15b3d8..fa345956ec4d 100644 --- a/core/java/android/window/WindowTokenClientController.java +++ b/core/java/android/window/WindowTokenClientController.java @@ -28,7 +28,7 @@ import android.content.Context; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; -import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.view.IWindowManager; import android.view.WindowManagerGlobal; @@ -50,9 +50,9 @@ public class WindowTokenClientController { private final IApplicationThread mAppThread = ActivityThread.currentActivityThread() .getApplicationThread(); - /** Mapping from a client defined token to the {@link WindowTokenClient} it represents. */ + /** Attached {@link WindowTokenClient}. */ @GuardedBy("mLock") - private final ArrayMap<IBinder, WindowTokenClient> mWindowTokenClientMap = new ArrayMap<>(); + private final ArraySet<WindowTokenClient> mWindowTokenClients = new ArraySet<>(); /** Gets the singleton controller. */ @NonNull @@ -85,11 +85,15 @@ public class WindowTokenClientController { /** Gets the {@link WindowContext} instance for the token. */ @Nullable public Context getWindowContext(@NonNull IBinder clientToken) { - final WindowTokenClient windowTokenClient; + if (!(clientToken instanceof WindowTokenClient windowTokenClient)) { + return null; + } synchronized (mLock) { - windowTokenClient = mWindowTokenClientMap.get(clientToken); + if (!mWindowTokenClients.contains(windowTokenClient)) { + return null; + } } - return windowTokenClient != null ? windowTokenClient.getContext() : null; + return windowTokenClient.getContext(); } /** @@ -126,8 +130,14 @@ public class WindowTokenClientController { */ public boolean attachToDisplayContent(@NonNull WindowTokenClient client, int displayId) { final IWindowManager wms = getWindowManagerService(); - // #createSystemUiContext may call this method before WindowManagerService is initialized. if (wms == null) { + // #createSystemUiContext may call this method before WindowManagerService is + // initialized. + // Regardless of whether or not it is ready, keep track of the token so that when WMS + // is initialized later, the SystemUiContext will start reporting from + // DisplayContent#registerSystemUiContext, and WindowTokenClientController can report + // the Configuration to the correct client. + recordWindowContextToken(client); return false; } final WindowContextInfo info; @@ -170,12 +180,18 @@ public class WindowTokenClientController { /** Detaches a {@link WindowTokenClient} from associated WindowContainer if there's one. */ public void detachIfNeeded(@NonNull WindowTokenClient client) { synchronized (mLock) { - if (mWindowTokenClientMap.remove(client) == null) { + if (!mWindowTokenClients.remove(client)) { return; } } + final IWindowManager wms = getWindowManagerService(); + if (wms == null) { + // #createSystemUiContext may call this method before WindowManagerService is + // initialized. If it is GC'ed before WMS is initialized, skip calling into WMS. + return; + } try { - getWindowManagerService().detachWindowContext(client); + wms.detachWindowContext(client); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -183,9 +199,7 @@ public class WindowTokenClientController { private void onWindowContextTokenAttached(@NonNull WindowTokenClient client, @NonNull WindowContextInfo info, boolean shouldReportConfigChange) { - synchronized (mLock) { - mWindowTokenClientMap.put(client, client); - } + recordWindowContextToken(client); if (shouldReportConfigChange) { // Should trigger an #onConfigurationChanged callback to the WindowContext. Post the // dispatch in the next loop to prevent the callback from being dispatched before @@ -199,10 +213,16 @@ public class WindowTokenClientController { } } + private void recordWindowContextToken(@NonNull WindowTokenClient client) { + synchronized (mLock) { + mWindowTokenClients.add(client); + } + } + /** Called when receives {@link WindowContextInfoChangeItem}. */ public void onWindowContextInfoChanged(@NonNull IBinder clientToken, @NonNull WindowContextInfo info) { - final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken); + final WindowTokenClient windowTokenClient = getWindowTokenClientIfAttached(clientToken); if (windowTokenClient != null) { windowTokenClient.onConfigurationChanged(info.getConfiguration(), info.getDisplayId()); } @@ -210,20 +230,23 @@ public class WindowTokenClientController { /** Called when receives {@link WindowContextWindowRemovalItem}. */ public void onWindowContextWindowRemoved(@NonNull IBinder clientToken) { - final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken); + final WindowTokenClient windowTokenClient = getWindowTokenClientIfAttached(clientToken); if (windowTokenClient != null) { windowTokenClient.onWindowTokenRemoved(); } } @Nullable - private WindowTokenClient getWindowTokenClient(@NonNull IBinder clientToken) { - final WindowTokenClient windowTokenClient; - synchronized (mLock) { - windowTokenClient = mWindowTokenClientMap.get(clientToken); + private WindowTokenClient getWindowTokenClientIfAttached(@NonNull IBinder clientToken) { + if (!(clientToken instanceof WindowTokenClient windowTokenClient)) { + Log.e(TAG, "getWindowTokenClient failed for non-window token " + clientToken); + return null; } - if (windowTokenClient == null) { - Log.w(TAG, "Can't find attached WindowTokenClient for " + clientToken); + synchronized (mLock) { + if (!mWindowTokenClients.contains(windowTokenClient)) { + Log.w(TAG, "Can't find attached WindowTokenClient for " + clientToken); + return null; + } } return windowTokenClient; } diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index eebdeadcdeb2..8019e6791cf1 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -242,6 +242,13 @@ flag { } flag { + name: "enable_desktop_windowing_app_handle_education_integration" + namespace: "lse_desktop_experience" + description: "Enables desktop windowing app handle education and integrates new APIs" + bug: "380272815" +} + +flag { name: "enable_desktop_windowing_transitions" namespace: "lse_desktop_experience" description: "Enables desktop windowing transition & motion polish changes" @@ -256,6 +263,16 @@ flag { } flag { + name: "enable_desktop_windowing_enter_transition_bugfix" + namespace: "lse_desktop_experience" + description: "Enables enter desktop windowing transition & motion polish changes" + bug: "380224875" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "enable_desktop_windowing_exit_transitions" namespace: "lse_desktop_experience" description: "Enables exit desktop windowing transition & motion polish changes" @@ -263,6 +280,16 @@ flag { } flag { + name: "enable_desktop_windowing_exit_transitions_bugfix" + namespace: "lse_desktop_experience" + description: "Enables exit desktop windowing transition & motion polish changes" + bug: "380224768" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "enable_compat_ui_visibility_status" namespace: "lse_desktop_experience" description: "Enables the tracking of the status for compat ui elements." @@ -346,6 +373,16 @@ flag { } flag { + name: "enable_desktop_app_launch_alttab_transitions_bugfix" + namespace: "lse_desktop_experience" + description: "Enables custom transitions for alt-tab app launches in Desktop Mode." + bug: "380225486" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "enable_desktop_app_launch_transitions" namespace: "lse_desktop_experience" description: "Enables custom transitions for app launches in Desktop Mode." @@ -353,6 +390,16 @@ flag { } flag { + name: "enable_desktop_app_launch_transitions_bugfix" + namespace: "lse_desktop_experience" + description: "Enables custom transitions for app launches in Desktop Mode." + bug: "380224832" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "enable_desktop_system_dialogs_transitions" namespace: "lse_desktop_experience" description: "Enables custom transitions for system dialogs in Desktop Mode." diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 86bbeb827703..ebbe4830009c 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -441,3 +441,11 @@ flag { description: "Enable Predictive Back Animation for 3-button-nav" bug: "373544911" } + +flag { + name: "predictive_back_default_enable_sdk_36" + namespace: "systemui" + description: "Enable Predictive Back by default with targetSdk>=36" + is_fixed_read_only: true + bug: "376407910" +} diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java index 8a6e6be1abbf..5fc1276dd9f9 100644 --- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java +++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java @@ -2377,6 +2377,7 @@ public class ParsingPackageUtils { * Flags are separated by type and by default value. They are sorted alphabetically within each * section. */ + @SuppressWarnings("AndroidFrameworkCompatChange") private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) { int targetSdk = pkg.getTargetSdkVersion(); //@formatter:off @@ -2414,12 +2415,20 @@ public class ParsingPackageUtils { .setResetEnabledSettingsOnAppDataCleared(bool(false, R.styleable.AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared, sa)) - .setOnBackInvokedCallbackEnabled(bool(false, R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback, sa)) // targetSdkVersion gated .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa)) .setHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa)) .setRequestLegacyExternalStorage(bool(targetSdk < Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, sa)) .setCleartextTrafficAllowed(bool(targetSdk < Build.VERSION_CODES.P, R.styleable.AndroidManifestApplication_usesCleartextTraffic, sa)) + // CompatChange.isChangeEnabled() can't be used here because this is called during + // PackageManagerService initialization. PlatformCompat can't be used because this + // code is not guaranteed to be called from the system_server process. Therefore + // accessing Build.VERSION_CODES directly and suppressing + // AndroidFrameworkCompatChange warning + .setOnBackInvokedCallbackEnabled(bool( + com.android.window.flags.Flags.predictiveBackDefaultEnableSdk36() + && targetSdk > Build.VERSION_CODES.VANILLA_ICE_CREAM, + R.styleable.AndroidManifestApplication_enableOnBackInvokedCallback, sa)) // Ints Default 0 .setUiOptions(anInt(R.styleable.AndroidManifestApplication_uiOptions, sa)) // Ints diff --git a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java index 8771cde1de46..be3bbb2d1d94 100644 --- a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java @@ -70,7 +70,7 @@ public class LegacyProtoLogImpl implements IProtoLog { private final TraceBuffer mBuffer; private final LegacyProtoLogViewerConfigReader mViewerConfig; private final Map<String, IProtoLogGroup> mLogGroups = new TreeMap<>(); - private final Runnable mCacheUpdater; + private final ProtoLogCacheUpdater mCacheUpdater; private final int mPerChunkSize; private boolean mProtoLogEnabled; @@ -78,14 +78,14 @@ public class LegacyProtoLogImpl implements IProtoLog { private final Object mProtoLogEnabledLock = new Object(); public LegacyProtoLogImpl(String outputFile, String viewerConfigFilename, - Runnable cacheUpdater) { + ProtoLogCacheUpdater cacheUpdater) { this(new File(outputFile), viewerConfigFilename, BUFFER_CAPACITY, new LegacyProtoLogViewerConfigReader(), PER_CHUNK_SIZE, cacheUpdater); } public LegacyProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity, LegacyProtoLogViewerConfigReader viewerConfig, int perChunkSize, - Runnable cacheUpdater) { + ProtoLogCacheUpdater cacheUpdater) { mLogFile = file; mBuffer = new TraceBuffer(bufferCapacity); mLegacyViewerConfigFilename = viewerConfigFilename; @@ -298,7 +298,7 @@ public class LegacyProtoLogImpl implements IProtoLog { } } - mCacheUpdater.run(); + mCacheUpdater.update(this); return 0; } diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java index eb682dff14de..05a33fe830e8 100644 --- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java @@ -51,7 +51,6 @@ import android.os.ServiceManager; import android.os.ShellCommand; import android.os.SystemClock; import android.text.TextUtils; -import android.tracing.perfetto.DataSourceParams; import android.tracing.perfetto.InitArguments; import android.tracing.perfetto.Producer; import android.tracing.perfetto.TracingContext; @@ -86,7 +85,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import java.util.stream.Stream; /** * A service for the ProtoLog logging system. @@ -98,10 +96,12 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen @NonNull protected final ProtoLogDataSource mDataSource; + @Nullable + protected final IProtoLogConfigurationService mConfigurationService; @NonNull protected final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>(); @NonNull - private final Runnable mCacheUpdater; + private final ProtoLogCacheUpdater mCacheUpdater; @NonNull private final int[] mDefaultLogLevelCounts = new int[LogLevel.values().length]; @@ -117,10 +117,10 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen private boolean mLogcatReady = false; protected PerfettoProtoLogImpl( - @NonNull Runnable cacheUpdater, + @NonNull ProtoLogDataSource dataSource, + @NonNull ProtoLogCacheUpdater cacheUpdater, @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { - this(cacheUpdater, groups, - ProtoLogDataSource::new, + this(dataSource, cacheUpdater, groups, android.tracing.Flags.clientSideProtoLogging() ? IProtoLogConfigurationService.Stub.asInterface( ServiceManager.getServiceOrThrow(PROTOLOG_CONFIGURATION_SERVICE) @@ -129,49 +129,62 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen } protected PerfettoProtoLogImpl( - @NonNull Runnable cacheUpdater, + @NonNull ProtoLogDataSource dataSource, + @NonNull ProtoLogCacheUpdater cacheUpdater, @NonNull IProtoLogGroup[] groups, - @NonNull ProtoLogDataSourceBuilder dataSourceBuilder, @Nullable IProtoLogConfigurationService configurationService) { - mDataSource = dataSourceBuilder.build( - this::onTracingInstanceStart, - this::onTracingFlush, - this::onTracingInstanceStop); - Producer.init(InitArguments.DEFAULTS); - DataSourceParams params = - new DataSourceParams.Builder() - .setBufferExhaustedPolicy( - DataSourceParams - .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP) - .build(); - // NOTE: Registering that datasource is an async operation, so there may be no data traced - // for some messages logged right after the construction of this class. - mDataSource.register(params); - - this.mCacheUpdater = cacheUpdater; + mDataSource = dataSource; + mCacheUpdater = cacheUpdater; + mConfigurationService = configurationService; registerGroupsLocally(groups); + } + + /** + * To be called to enable the ProtoLogImpl to start tracing to ProtoLog and register with all + * the expected ProtoLog components. + */ + public void enable() { + Producer.init(InitArguments.DEFAULTS); if (android.tracing.Flags.clientSideProtoLogging()) { - Objects.requireNonNull(configurationService, - "A null ProtoLog Configuration Service was provided!"); + connectToConfigurationService(); + } - try { - var args = createConfigurationServiceRegisterClientArgs(); + mDataSource.registerOnStartCallback(this::onTracingInstanceStart); + mDataSource.registerOnFlushCallback(this::onTracingFlush); + mDataSource.registerOnStopCallback(this::onTracingInstanceStop); + } - final var groupArgs = Stream.of(groups) - .map(group -> new RegisterClientArgs - .GroupConfig(group.name(), group.isLogToLogcat())) - .toArray(RegisterClientArgs.GroupConfig[]::new); - args.setGroups(groupArgs); + private void connectToConfigurationService() { + Objects.requireNonNull(mConfigurationService, + "A null ProtoLog Configuration Service was provided!"); - configurationService.registerClient(this, args); - } catch (RemoteException e) { - throw new RuntimeException("Failed to register ProtoLog client"); - } + try { + var args = createConfigurationServiceRegisterClientArgs(); + + final var groupArgs = mLogGroups.values().stream() + .map(group -> new RegisterClientArgs + .GroupConfig(group.name(), group.isLogToLogcat())) + .toArray(RegisterClientArgs.GroupConfig[]::new); + args.setGroups(groupArgs); + + mConfigurationService.registerClient(this, args); + } catch (RemoteException e) { + throw new RuntimeException("Failed to register ProtoLog client"); } } + /** + * Should be called when we no longer want to use the ProtoLog logger to unlink ourselves from + * the datasource and the configuration service to ensure we no longer receive the callback. + */ + public void disable() { + mDataSource.unregisterOnStartCallback(this::onTracingInstanceStart); + mDataSource.unregisterOnFlushCallback(this::onTracingFlush); + mDataSource.unregisterOnStopCallback(this::onTracingInstanceStop); + } + @NonNull protected abstract RegisterClientArgs createConfigurationServiceRegisterClientArgs(); @@ -703,7 +716,7 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen } } - mCacheUpdater.run(); + mCacheUpdater.update(this); return 0; } @@ -746,7 +759,7 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen } } - mCacheUpdater.run(); + mCacheUpdater.update(this); this.mTracingInstances.incrementAndGet(); @@ -786,7 +799,7 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen } } - mCacheUpdater.run(); + mCacheUpdater.update(this); Log.d(LOG_TAG, "Finished onTracingInstanceStop"); } diff --git a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java index 967a5ed1744d..e0a77d2be724 100644 --- a/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/ProcessedPerfettoProtoLogImpl.java @@ -42,10 +42,11 @@ public class ProcessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl { private final String mViewerConfigFilePath; public ProcessedPerfettoProtoLogImpl( + @NonNull ProtoLogDataSource datasource, @NonNull String viewerConfigFilePath, - @NonNull Runnable cacheUpdater, + @NonNull ProtoLogCacheUpdater cacheUpdater, @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { - this(viewerConfigFilePath, new ViewerConfigInputStreamProvider() { + this(datasource, viewerConfigFilePath, new ViewerConfigInputStreamProvider() { @NonNull @Override public AutoClosableProtoInputStream getInputStream() { @@ -64,11 +65,12 @@ public class ProcessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl { @VisibleForTesting public ProcessedPerfettoProtoLogImpl( + @NonNull ProtoLogDataSource datasource, @NonNull String viewerConfigFilePath, @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, - @NonNull Runnable cacheUpdater, + @NonNull ProtoLogCacheUpdater cacheUpdater, @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { - super(cacheUpdater, groups); + super(datasource, cacheUpdater, groups); this.mViewerConfigFilePath = viewerConfigFilePath; @@ -80,15 +82,15 @@ public class ProcessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl { @VisibleForTesting public ProcessedPerfettoProtoLogImpl( + @NonNull ProtoLogDataSource datasource, @NonNull String viewerConfigFilePath, @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider, @NonNull ProtoLogViewerConfigReader viewerConfigReader, - @NonNull Runnable cacheUpdater, + @NonNull ProtoLogCacheUpdater cacheUpdater, @NonNull IProtoLogGroup[] groups, - @NonNull ProtoLogDataSourceBuilder dataSourceBuilder, @Nullable IProtoLogConfigurationService configurationService) throws ServiceManager.ServiceNotFoundException { - super(cacheUpdater, groups, dataSourceBuilder, configurationService); + super(datasource, cacheUpdater, groups, configurationService); this.mViewerConfigFilePath = viewerConfigFilePath; diff --git a/core/java/com/android/internal/protolog/ProtoLog.java b/core/java/com/android/internal/protolog/ProtoLog.java index d117e93d7de7..c81af959f36c 100644 --- a/core/java/com/android/internal/protolog/ProtoLog.java +++ b/core/java/com/android/internal/protolog/ProtoLog.java @@ -17,6 +17,9 @@ package com.android.internal.protolog; import android.os.ServiceManager; +import android.tracing.perfetto.DataSourceParams; +import android.tracing.perfetto.InitArguments; +import android.tracing.perfetto.Producer; import com.android.internal.protolog.common.IProtoLog; import com.android.internal.protolog.common.IProtoLogGroup; @@ -54,6 +57,8 @@ public class ProtoLog { private static IProtoLog sProtoLogInstance; + private static ProtoLogDataSource sDataSource; + private static final Object sInitLock = new Object(); /** @@ -69,26 +74,45 @@ public class ProtoLog { // files to extract out the log strings. Otherwise, the trace calls are replaced with calls // directly to the generated tracing implementations. if (android.tracing.Flags.perfettoProtologTracing()) { - synchronized (sInitLock) { - final var allGroups = new HashSet<>(Arrays.stream(groups).toList()); - if (sProtoLogInstance != null) { - // The ProtoLog instance has already been initialized in this process - final var alreadyRegisteredGroups = sProtoLogInstance.getRegisteredGroups(); - allGroups.addAll(alreadyRegisteredGroups); - } - - try { - sProtoLogInstance = new UnprocessedPerfettoProtoLogImpl( - allGroups.toArray(new IProtoLogGroup[0])); - } catch (ServiceManager.ServiceNotFoundException e) { - throw new RuntimeException(e); - } - } + initializePerfettoProtoLog(groups); } else { sProtoLogInstance = new LogcatOnlyProtoLogImpl(); } } + private static void initializePerfettoProtoLog(IProtoLogGroup... groups) { + var datasource = getSharedSingleInstanceDataSource(); + + synchronized (sInitLock) { + final var allGroups = new HashSet<>(Arrays.stream(groups).toList()); + final var previousProtoLogImpl = sProtoLogInstance; + if (previousProtoLogImpl != null) { + // The ProtoLog instance has already been initialized in this process + final var alreadyRegisteredGroups = previousProtoLogImpl.getRegisteredGroups(); + allGroups.addAll(alreadyRegisteredGroups); + } + + sProtoLogInstance = createAndEnableNewPerfettoProtoLogImpl( + datasource, allGroups.toArray(new IProtoLogGroup[0])); + if (previousProtoLogImpl instanceof PerfettoProtoLogImpl) { + ((PerfettoProtoLogImpl) previousProtoLogImpl).disable(); + } + } + } + + private static PerfettoProtoLogImpl createAndEnableNewPerfettoProtoLogImpl( + ProtoLogDataSource datasource, IProtoLogGroup[] groups) { + try { + var unprocessedPerfettoProtoLogImpl = + new UnprocessedPerfettoProtoLogImpl(datasource, groups); + unprocessedPerfettoProtoLogImpl.enable(); + + return unprocessedPerfettoProtoLogImpl; + } catch (ServiceManager.ServiceNotFoundException e) { + throw new RuntimeException(e); + } + } + /** * DEBUG level log. * @@ -190,6 +214,32 @@ public class ProtoLog { return sProtoLogInstance; } + /** + * Gets or creates if it doesn't exist yet the protolog datasource to use in this process. + * We should re-use the same datasource to avoid registering the datasource multiple times in + * the same process, since there is no way to unregister the datasource after registration. + * + * @return The single ProtoLog datasource instance to be shared across all ProtoLog tracing + * objects. + */ + public static synchronized ProtoLogDataSource getSharedSingleInstanceDataSource() { + if (sDataSource == null) { + Producer.init(InitArguments.DEFAULTS); + sDataSource = new ProtoLogDataSource(); + DataSourceParams params = + new DataSourceParams.Builder() + .setBufferExhaustedPolicy( + DataSourceParams + .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP) + .build(); + // NOTE: Registering that datasource is an async operation, so there may be no data + // traced for some messages logged right after the construction of this class. + sDataSource.register(params); + } + + return sDataSource; + } + private static void logStringMessage(LogLevel logLevel, IProtoLogGroup group, String stringMessage, Object... args) { if (sProtoLogInstance == null) { diff --git a/core/java/com/android/internal/protolog/ProtoLogCacheUpdater.java b/core/java/com/android/internal/protolog/ProtoLogCacheUpdater.java new file mode 100644 index 000000000000..4f8655c6377b --- /dev/null +++ b/core/java/com/android/internal/protolog/ProtoLogCacheUpdater.java @@ -0,0 +1,28 @@ +/* + * 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.internal.protolog; + +import com.android.internal.protolog.common.IProtoLog; + +public interface ProtoLogCacheUpdater { + /** + * Update the cache based on the latest state of the active tracing configurations. + * + * @param protoLogInstance the instance to use to query the latest state of tracing. + */ + void update(IProtoLog protoLogInstance); +} diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java index e9a8770deb73..23f7c2a3d987 100644 --- a/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java +++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationServiceImpl.java @@ -34,9 +34,6 @@ import android.content.Context; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; -import android.tracing.perfetto.DataSourceParams; -import android.tracing.perfetto.InitArguments; -import android.tracing.perfetto.Producer; import android.util.Log; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; @@ -110,39 +107,31 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ private final ViewerConfigFileTracer mViewerConfigFileTracer; public ProtoLogConfigurationServiceImpl() { - this(ProtoLogDataSource::new, ProtoLogConfigurationServiceImpl::dumpViewerConfig); + this(ProtoLog.getSharedSingleInstanceDataSource(), + ProtoLogConfigurationServiceImpl::dumpViewerConfig); } @VisibleForTesting - public ProtoLogConfigurationServiceImpl(@NonNull ProtoLogDataSourceBuilder dataSourceBuilder) { - this(dataSourceBuilder, ProtoLogConfigurationServiceImpl::dumpViewerConfig); + public ProtoLogConfigurationServiceImpl(@NonNull ProtoLogDataSource datasource) { + this(datasource, ProtoLogConfigurationServiceImpl::dumpViewerConfig); } @VisibleForTesting public ProtoLogConfigurationServiceImpl(@NonNull ViewerConfigFileTracer tracer) { - this(ProtoLogDataSource::new, tracer); + this(ProtoLog.getSharedSingleInstanceDataSource(), tracer); } @VisibleForTesting public ProtoLogConfigurationServiceImpl( - @NonNull ProtoLogDataSourceBuilder dataSourceBuilder, + @NonNull ProtoLogDataSource datasource, @NonNull ViewerConfigFileTracer tracer) { - mDataSource = dataSourceBuilder.build( - this::onTracingInstanceStart, - this::onTracingInstanceFlush, - this::onTracingInstanceStop - ); - - // Initialize the Perfetto producer and register the Perfetto ProtoLog datasource to be - // receive the lifecycle callbacks of the datasource and write the viewer configs if and - // when required to the datasource. - Producer.init(InitArguments.DEFAULTS); - final var params = new DataSourceParams.Builder() - .setBufferExhaustedPolicy(DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP) - .build(); - mDataSource.register(params); - mViewerConfigFileTracer = tracer; + + datasource.registerOnStartCallback(this::onTracingInstanceStart); + datasource.registerOnFlushCallback(this::onTracingInstanceFlush); + datasource.registerOnStopCallback(this::onTracingInstanceStop); + + mDataSource = datasource; } public static class RegisterClientArgs extends IRegisterClientArgs.Stub { diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSource.java b/core/java/com/android/internal/protolog/ProtoLogDataSource.java index 0afb135ac6d9..ea452494b88d 100644 --- a/core/java/com/android/internal/protolog/ProtoLogDataSource.java +++ b/core/java/com/android/internal/protolog/ProtoLogDataSource.java @@ -46,36 +46,30 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.TreeMap; public class ProtoLogDataSource extends DataSource<ProtoLogDataSource.Instance, ProtoLogDataSource.TlsState, ProtoLogDataSource.IncrementalState> { private static final String DATASOURCE_NAME = "android.protolog"; + private final Map<Integer, ProtoLogConfig> mRunningInstances = new TreeMap<>(); + @NonNull - private final Instance.TracingInstanceStartCallback mOnStart; + private final Set<Instance.TracingInstanceStartCallback> mOnStartCallbacks = new HashSet<>(); @NonNull - private final Runnable mOnFlush; + private final Set<Runnable> mOnFlushCallbacks = new HashSet<>(); @NonNull - private final Instance.TracingInstanceStopCallback mOnStop; + private final Set<Instance.TracingInstanceStopCallback> mOnStopCallbacks = new HashSet<>(); - public ProtoLogDataSource( - @NonNull Instance.TracingInstanceStartCallback onStart, - @NonNull Runnable onFlush, - @NonNull Instance.TracingInstanceStopCallback onStop) { - this(onStart, onFlush, onStop, DATASOURCE_NAME); + public ProtoLogDataSource() { + this(DATASOURCE_NAME); } @VisibleForTesting public ProtoLogDataSource( - @NonNull Instance.TracingInstanceStartCallback onStart, - @NonNull Runnable onFlush, - @NonNull Instance.TracingInstanceStopCallback onStop, @NonNull String dataSourceName) { super(dataSourceName); - this.mOnStart = onStart; - this.mOnFlush = onFlush; - this.mOnStop = onStop; } @Override @@ -106,7 +100,8 @@ public class ProtoLogDataSource extends DataSource<ProtoLogDataSource.Instance, } return new Instance( - this, instanceIndex, config, mOnStart, mOnFlush, mOnStop); + this, instanceIndex, config, this::executeOnStartCallbacks, + this::executeOnFlushCallbacks, this::executeOnStopCallbacks); } @Override @@ -128,6 +123,84 @@ public class ProtoLogDataSource extends DataSource<ProtoLogDataSource.Instance, return new IncrementalState(); } + /** + * Register an onStart callback that will be called when a tracing instance is started. + * The callback will be called immediately for all currently running tracing instances on + * registration. + * @param onStartCallback The callback to call on starting a tracing instance. + */ + public synchronized void registerOnStartCallback( + Instance.TracingInstanceStartCallback onStartCallback) { + mOnStartCallbacks.add(onStartCallback); + + for (var instanceIndex : mRunningInstances.keySet()) { + var config = mRunningInstances.get(instanceIndex); + onStartCallback.run(instanceIndex, config); + } + } + + /** + * Register an onFlush callback that will be called when a tracing instance is about to flush. + * @param onFlushCallback The callback to call on flushing a tracing instance + */ + public void registerOnFlushCallback(Runnable onFlushCallback) { + mOnFlushCallbacks.add(onFlushCallback); + } + + /** + * Register an onStop callback that will be called when a tracing instance is being stopped. + * @param onStopCallback The callback to call on stopping a tracing instance. + */ + public void registerOnStopCallback(Instance.TracingInstanceStopCallback onStopCallback) { + mOnStopCallbacks.add(onStopCallback); + } + + /** + * Unregister an onStart callback. + * @param onStartCallback The callback object to unregister. + */ + public void unregisterOnStartCallback(Instance.TracingInstanceStartCallback onStartCallback) { + mOnStartCallbacks.add(onStartCallback); + } + + /** + * Unregister an onFlush callback. + * @param onFlushCallback The callback object to unregister. + */ + public void unregisterOnFlushCallback(Runnable onFlushCallback) { + mOnFlushCallbacks.add(onFlushCallback); + } + + /** + * Unregister an onStop callback. + * @param onStopCallback The callback object to unregister. + */ + public void unregisterOnStopCallback(Instance.TracingInstanceStopCallback onStopCallback) { + mOnStopCallbacks.add(onStopCallback); + } + + private synchronized void executeOnStartCallbacks(int instanceIdx, ProtoLogConfig config) { + mRunningInstances.put(instanceIdx, config); + + for (var onStart : mOnStartCallbacks) { + onStart.run(instanceIdx, config); + } + } + + private void executeOnFlushCallbacks() { + for (var onFlush : mOnFlushCallbacks) { + onFlush.run(); + } + } + + private synchronized void executeOnStopCallbacks(int instanceIdx, ProtoLogConfig config) { + mRunningInstances.remove(instanceIdx, config); + + for (var onStop : mOnStopCallbacks) { + onStop.run(instanceIdx, config); + } + } + public static class TlsState { @NonNull private final ProtoLogConfig mConfig; diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java index 3378d08e7761..c2d4b21181b7 100644 --- a/core/java/com/android/internal/protolog/ProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java @@ -56,7 +56,7 @@ public class ProtoLogImpl { private static TreeMap<String, IProtoLogGroup> sLogGroups; @ProtoLogToolInjected(CACHE_UPDATER) - private static Runnable sCacheUpdater; + private static ProtoLogCacheUpdater sCacheUpdater; /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */ public static void d(IProtoLogGroup group, long messageHash, int paramsMask, Object... args) { @@ -93,7 +93,12 @@ public class ProtoLogImpl { * and log level. */ public static boolean isEnabled(IProtoLogGroup group, LogLevel level) { - return getSingleInstance().isEnabled(group, level); + return isEnabled(getSingleInstance(), group, level); + } + + private static boolean isEnabled( + IProtoLog protoLogInstance, IProtoLogGroup group, LogLevel level) { + return protoLogInstance.isEnabled(group, level); } /** @@ -106,36 +111,37 @@ public class ProtoLogImpl { final var groups = sLogGroups.values().toArray(new IProtoLogGroup[0]); if (android.tracing.Flags.perfettoProtologTracing()) { - sServiceInstance = createProtoLogImpl(groups); + var viewerConfigFile = new File(sViewerConfigPath); + if (!viewerConfigFile.exists()) { + // TODO(b/353530422): Remove - temporary fix to unblock b/352290057 + // In robolectric tests the viewer config file isn't current available, so we + // cannot use the ProcessedPerfettoProtoLogImpl. + Log.e(LOG_TAG, "Failed to find viewer config file " + sViewerConfigPath + + " when setting up " + ProtoLogImpl.class.getSimpleName() + ". " + + "ProtoLog will not work here!"); + + sServiceInstance = new NoViewerConfigProtoLogImpl(); + } else { + var datasource = ProtoLog.getSharedSingleInstanceDataSource(); + try { + var processedProtoLogImpl = + new ProcessedPerfettoProtoLogImpl(datasource, sViewerConfigPath, + sCacheUpdater, groups); + sServiceInstance = processedProtoLogImpl; + processedProtoLogImpl.enable(); + } catch (ServiceManager.ServiceNotFoundException e) { + throw new RuntimeException(e); + } + } } else { sServiceInstance = createLegacyProtoLogImpl(groups); } - sCacheUpdater.run(); + sCacheUpdater.update(sServiceInstance); } return sServiceInstance; } - private static IProtoLog createProtoLogImpl(IProtoLogGroup[] groups) { - try { - File f = new File(sViewerConfigPath); - if (!f.exists()) { - // TODO(b/353530422): Remove - temporary fix to unblock b/352290057 - // In robolectric tests the viewer config file isn't current available, so we cannot - // use the ProcessedPerfettoProtoLogImpl. - Log.e(LOG_TAG, "Failed to find viewer config file " + sViewerConfigPath - + " when setting up " + ProtoLogImpl.class.getSimpleName() + ". " - + "ProtoLog will not work here!"); - - return new NoViewerConfigProtoLogImpl(); - } else { - return new ProcessedPerfettoProtoLogImpl(sViewerConfigPath, sCacheUpdater, groups); - } - } catch (ServiceManager.ServiceNotFoundException e) { - throw new RuntimeException(e); - } - } - private static LegacyProtoLogImpl createLegacyProtoLogImpl(IProtoLogGroup[] groups) { var protologImpl = new LegacyProtoLogImpl( sLegacyOutputFilePath, sLegacyViewerConfigPath, sCacheUpdater); diff --git a/core/java/com/android/internal/protolog/UnprocessedPerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/UnprocessedPerfettoProtoLogImpl.java index f3fe58070fa9..ebb07a04270d 100644 --- a/core/java/com/android/internal/protolog/UnprocessedPerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/UnprocessedPerfettoProtoLogImpl.java @@ -23,14 +23,10 @@ import com.android.internal.protolog.ProtoLogConfigurationServiceImpl.RegisterCl import com.android.internal.protolog.common.IProtoLogGroup; public class UnprocessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl { - public UnprocessedPerfettoProtoLogImpl(@NonNull IProtoLogGroup[] groups) + public UnprocessedPerfettoProtoLogImpl( + @NonNull ProtoLogDataSource dataSource, @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { - this(() -> {}, groups); - } - - public UnprocessedPerfettoProtoLogImpl(@NonNull Runnable cacheUpdater, - @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException { - super(cacheUpdater, groups); + super(dataSource, (instance) -> {}, groups); readyToLogToLogcat(); } diff --git a/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java b/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java index 862f7cb1d476..5eedec6a63e4 100644 --- a/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java +++ b/core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java @@ -17,6 +17,7 @@ package com.android.internal.vibrator.persistence; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_DELAY_MS; +import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_DELAY_TYPE; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_NAME; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_SCALE; import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE; @@ -25,9 +26,11 @@ import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PRIMITI import android.annotation.NonNull; import android.annotation.Nullable; import android.os.VibrationEffect; +import android.os.vibrator.Flags; import android.os.vibrator.PrimitiveSegment; import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment; +import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveDelayType; import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveEffectName; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -46,17 +49,26 @@ final class SerializedCompositionPrimitive implements SerializedSegment { private final PrimitiveEffectName mPrimitiveName; private final float mPrimitiveScale; private final int mPrimitiveDelayMs; + @Nullable + private final PrimitiveDelayType mDelayType; - SerializedCompositionPrimitive(PrimitiveEffectName primitiveName, float scale, int delayMs) { + SerializedCompositionPrimitive(PrimitiveEffectName primitiveName, float scale, int delayMs, + @Nullable PrimitiveDelayType delayType) { mPrimitiveName = primitiveName; mPrimitiveScale = scale; mPrimitiveDelayMs = delayMs; + mDelayType = delayType; } @Override public void deserializeIntoComposition(@NonNull VibrationEffect.Composition composition) { - composition.addPrimitive(mPrimitiveName.getPrimitiveId(), mPrimitiveScale, - mPrimitiveDelayMs); + if (Flags.primitiveCompositionAbsoluteDelay() && mDelayType != null) { + composition.addPrimitive(mPrimitiveName.getPrimitiveId(), mPrimitiveScale, + mPrimitiveDelayMs, mDelayType.getDelayType()); + } else { + composition.addPrimitive(mPrimitiveName.getPrimitiveId(), mPrimitiveScale, + mPrimitiveDelayMs); + } } @Override @@ -72,6 +84,12 @@ final class SerializedCompositionPrimitive implements SerializedSegment { serializer.attributeInt(NAMESPACE, ATTRIBUTE_DELAY_MS, mPrimitiveDelayMs); } + if (Flags.primitiveCompositionAbsoluteDelay() && mDelayType != null) { + if (mDelayType.getDelayType() != PrimitiveSegment.DEFAULT_DELAY_TYPE) { + serializer.attribute(NAMESPACE, ATTRIBUTE_DELAY_TYPE, mDelayType.toString()); + } + } + serializer.endTag(NAMESPACE, TAG_PRIMITIVE_EFFECT); } @@ -81,6 +99,7 @@ final class SerializedCompositionPrimitive implements SerializedSegment { + "name=" + mPrimitiveName + ", scale=" + mPrimitiveScale + ", delayMs=" + mPrimitiveDelayMs + + ", delayType=" + mDelayType + '}'; } @@ -91,8 +110,14 @@ final class SerializedCompositionPrimitive implements SerializedSegment { static SerializedCompositionPrimitive parseNext(@NonNull TypedXmlPullParser parser) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, TAG_PRIMITIVE_EFFECT); - XmlValidator.checkTagHasNoUnexpectedAttributes(parser, - ATTRIBUTE_NAME, ATTRIBUTE_DELAY_MS, ATTRIBUTE_SCALE); + + if (Flags.primitiveCompositionAbsoluteDelay()) { + XmlValidator.checkTagHasNoUnexpectedAttributes(parser, + ATTRIBUTE_NAME, ATTRIBUTE_DELAY_MS, ATTRIBUTE_SCALE, ATTRIBUTE_DELAY_TYPE); + } else { + XmlValidator.checkTagHasNoUnexpectedAttributes(parser, + ATTRIBUTE_NAME, ATTRIBUTE_DELAY_MS, ATTRIBUTE_SCALE); + } PrimitiveEffectName primitiveName = parsePrimitiveName( parser.getAttributeValue(NAMESPACE, ATTRIBUTE_NAME)); @@ -100,11 +125,13 @@ final class SerializedCompositionPrimitive implements SerializedSegment { parser, ATTRIBUTE_SCALE, 0, 1, PrimitiveSegment.DEFAULT_SCALE); int delayMs = XmlReader.readAttributeIntNonNegative( parser, ATTRIBUTE_DELAY_MS, PrimitiveSegment.DEFAULT_DELAY_MILLIS); + PrimitiveDelayType delayType = parseDelayType( + parser.getAttributeValue(NAMESPACE, ATTRIBUTE_DELAY_TYPE)); // Consume tag XmlReader.readEndTag(parser); - return new SerializedCompositionPrimitive(primitiveName, scale, delayMs); + return new SerializedCompositionPrimitive(primitiveName, scale, delayMs, delayType); } @NonNull @@ -119,5 +146,21 @@ final class SerializedCompositionPrimitive implements SerializedSegment { } return effectName; } + + @Nullable + private static PrimitiveDelayType parseDelayType(@Nullable String name) + throws XmlParserException { + if (name == null) { + return null; + } + if (!Flags.primitiveCompositionAbsoluteDelay()) { + throw new XmlParserException("Unexpected primitive delay type " + name); + } + PrimitiveDelayType delayType = PrimitiveDelayType.findByName(name); + if (delayType == null) { + throw new XmlParserException("Unexpected primitive delay type " + name); + } + return delayType; + } } } diff --git a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java index d74a23d47f4a..cb834a5eac7e 100644 --- a/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java +++ b/core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java @@ -27,6 +27,7 @@ import android.os.vibrator.VibrationEffectSegment; import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment; import com.android.internal.vibrator.persistence.XmlConstants.PredefinedEffectName; +import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveDelayType; import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveEffectName; import java.util.List; @@ -170,8 +171,20 @@ public final class VibrationEffectXmlSerializer { XmlValidator.checkSerializerCondition(primitiveName != null, "Unsupported primitive effect id %s", primitive.getPrimitiveId()); + PrimitiveDelayType delayType = null; + + if (Flags.primitiveCompositionAbsoluteDelay()) { + delayType = PrimitiveDelayType.findByType(primitive.getDelayType()); + XmlValidator.checkSerializerCondition(delayType != null, + "Unsupported primitive delay type %s", primitive.getDelayType()); + } else { + XmlValidator.checkSerializerCondition( + primitive.getDelayType() == PrimitiveSegment.DEFAULT_DELAY_TYPE, + "Unsupported primitive delay type %s", primitive.getDelayType()); + } + return new SerializedCompositionPrimitive( - primitiveName, primitive.getScale(), primitive.getDelay()); + primitiveName, primitive.getScale(), primitive.getDelay(), delayType); } private static int toAmplitudeInt(float amplitude) { diff --git a/core/java/com/android/internal/vibrator/persistence/XmlConstants.java b/core/java/com/android/internal/vibrator/persistence/XmlConstants.java index 2a55d999bc0f..4122215a2b04 100644 --- a/core/java/com/android/internal/vibrator/persistence/XmlConstants.java +++ b/core/java/com/android/internal/vibrator/persistence/XmlConstants.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.VibrationEffect; +import android.os.VibrationEffect.Composition.DelayType; import android.os.VibrationEffect.Composition.PrimitiveType; import java.lang.annotation.Retention; @@ -51,6 +52,7 @@ public final class XmlConstants { public static final String ATTRIBUTE_AMPLITUDE = "amplitude"; public static final String ATTRIBUTE_SCALE = "scale"; public static final String ATTRIBUTE_DELAY_MS = "delayMs"; + public static final String ATTRIBUTE_DELAY_TYPE = "delayType"; public static final String VALUE_AMPLITUDE_DEFAULT = "default"; @@ -87,7 +89,7 @@ public final class XmlConstants { /** * Return the {@link PrimitiveEffectName} that represents given primitive id, or null if - * none of the available names maps to the given id. + * none of the available names map to the given id. */ @Nullable public static PrimitiveEffectName findById(int primitiveId) { @@ -200,4 +202,53 @@ public final class XmlConstants { return name().toLowerCase(Locale.ROOT); } } + + /** Represent supported values for attribute delay type in {@link #TAG_PRIMITIVE_EFFECT} */ + public enum PrimitiveDelayType { + PAUSE(VibrationEffect.Composition.DELAY_TYPE_PAUSE), + RELATIVE_START_OFFSET(VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET); + + @DelayType private final int mDelayType; + + PrimitiveDelayType(@DelayType int type) { + mDelayType = type; + } + + /** + * Return the {@link PrimitiveEffectName} that represents given primitive id, or null if + * none of the available names maps to the given id. + */ + @Nullable + public static PrimitiveDelayType findByType(int delayType) { + for (PrimitiveDelayType type : PrimitiveDelayType.values()) { + if (type.mDelayType == delayType) { + return type; + } + } + return null; + } + + /** + * Return the {@link PrimitiveEffectName} that represents given primitive name, or null if + * none of the available names maps to the given name. + */ + @Nullable + public static PrimitiveDelayType findByName(@NonNull String delayType) { + try { + return PrimitiveDelayType.valueOf(delayType.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException e) { + return null; + } + } + + @DelayType + public int getDelayType() { + return mDelayType; + } + + @Override + public String toString() { + return name().toLowerCase(Locale.ROOT); + } + } } diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java index 7a21275d611e..8cd7843fe1d9 100644 --- a/core/java/com/android/internal/widget/NotificationProgressBar.java +++ b/core/java/com/android/internal/widget/NotificationProgressBar.java @@ -68,12 +68,18 @@ public final class NotificationProgressBar extends ProgressBar { @Nullable private Drawable mTracker = null; + + /** @see R.styleable#NotificationProgressBar_trackerHeight */ private final int mTrackerHeight; private int mTrackerWidth; private int mTrackerPos; private final Matrix mMatrix = new Matrix(); private Matrix mTrackerDrawMatrix = null; + private float mScale = 0; + /** Indicates whether mTrackerPos needs to be recalculated before the tracker is drawn. */ + private boolean mTrackerPosIsDirty = false; + public NotificationProgressBar(Context context) { this(context, null); } @@ -107,8 +113,8 @@ public final class NotificationProgressBar extends ProgressBar { final Drawable tracker = a.getDrawable(R.styleable.NotificationProgressBar_tracker); setTracker(tracker); - // If this is configured to be non-zero, will scale the tracker drawable and ensure its - // aspect ration is between 2:1 to 1:2. + // If this is configured to be a non-zero size, will scale and crop the tracker drawable to + // ensure its aspect ratio is between 2:1 to 1:2. mTrackerHeight = a.getDimensionPixelSize(R.styleable.NotificationProgressBar_trackerHeight, 0); } @@ -200,8 +206,9 @@ public final class NotificationProgressBar extends ProgressBar { } private void setTracker(@Nullable Drawable tracker) { - final boolean needUpdate = mTracker != null && tracker != mTracker; - if (needUpdate) { + if (tracker == mTracker) return; + + if (mTracker != null) { mTracker.setCallback(null); } @@ -214,33 +221,41 @@ public final class NotificationProgressBar extends ProgressBar { if (canResolveLayoutDirection()) { tracker.setLayoutDirection(getLayoutDirection()); } - - // If we're updating get the new states - if (needUpdate && (tracker.getIntrinsicWidth() != mTracker.getIntrinsicWidth() - || tracker.getIntrinsicHeight() != mTracker.getIntrinsicHeight())) { - requestLayout(); - } } + final boolean trackerSizeChanged = trackerSizeChanged(tracker, mTracker); + mTracker = tracker; if (mNotificationProgressDrawable != null) { mNotificationProgressDrawable.setHasTrackerIcon(mTracker != null); } configureTrackerBounds(); + updateTrackerAndBarPos(getWidth(), getHeight()); + + // Change in tracker size may lead to change in measured view size. + // @see #onMeasure. + if (trackerSizeChanged) requestLayout(); invalidate(); - if (needUpdate) { - updateTrackerAndBarPos(getWidth(), getHeight()); - if (tracker != null && tracker.isStateful()) { - // Note that if the states are different this won't work. - // For now, let's consider that an app bug. - tracker.setState(getDrawableState()); - } + if (tracker != null && tracker.isStateful()) { + // Note that if the states are different this won't work. + // For now, let's consider that an app bug. + tracker.setState(getDrawableState()); } } + private static boolean trackerSizeChanged(@Nullable Drawable newTracker, + @Nullable Drawable oldTracker) { + if (newTracker == null && oldTracker == null) return false; + if (newTracker == null && oldTracker != null) return true; + if (newTracker != null && oldTracker == null) return true; + + return newTracker.getIntrinsicWidth() != oldTracker.getIntrinsicWidth() + || newTracker.getIntrinsicHeight() != oldTracker.getIntrinsicHeight(); + } + private void configureTrackerBounds() { // Reset the tracker draw matrix to null mTrackerDrawMatrix = null; @@ -279,6 +294,44 @@ public final class NotificationProgressBar extends ProgressBar { } @Override + public synchronized void setProgress(int progress) { + super.setProgress(progress); + + onMaybeVisualProgressChanged(); + } + + @Override + public void setProgress(int progress, boolean animate) { + // Animation isn't supported by NotificationProgressBar. + super.setProgress(progress, false); + + onMaybeVisualProgressChanged(); + } + + @Override + public synchronized void setMin(int min) { + super.setMin(min); + + onMaybeVisualProgressChanged(); + } + + @Override + public synchronized void setMax(int max) { + super.setMax(max); + + onMaybeVisualProgressChanged(); + } + + private void onMaybeVisualProgressChanged() { + float scale = getScale(); + if (mScale == scale) return; + + mScale = scale; + mTrackerPosIsDirty = true; + invalidate(); + } + + @Override protected boolean verifyDrawable(@NonNull Drawable who) { return who == mTracker || super.verifyDrawable(who); } @@ -328,7 +381,7 @@ public final class NotificationProgressBar extends ProgressBar { // parameter does. final int barHeight = Math.min(getMaxHeight(), paddedHeight); final int trackerHeight = tracker == null ? 0 - : ((mTrackerHeight == 0) ? tracker.getIntrinsicHeight() : mTrackerHeight); + : ((mTrackerHeight <= 0) ? tracker.getIntrinsicHeight() : mTrackerHeight); // Apply offset to whichever item is taller. final int barOffsetY; @@ -349,7 +402,7 @@ public final class NotificationProgressBar extends ProgressBar { } if (tracker != null) { - setTrackerPos(w, tracker, getScale(), trackerOffsetY); + setTrackerPos(w, tracker, mScale, trackerOffsetY); } } @@ -373,7 +426,7 @@ public final class NotificationProgressBar extends ProgressBar { int available = w - mPaddingLeft - mPaddingRight; final int trackerWidth = tracker.getIntrinsicWidth(); final int trackerHeight = tracker.getIntrinsicHeight(); - available -= ((mTrackerHeight == 0) ? trackerWidth : mTrackerWidth); + available -= ((mTrackerHeight <= 0) ? trackerWidth : mTrackerWidth); final int trackerPos = (int) (scale * available + 0.5f); @@ -401,6 +454,8 @@ public final class NotificationProgressBar extends ProgressBar { // Canvas will be translated, so 0,0 is where we start drawing tracker.setBounds(left, top, right, bottom); + + mTrackerPosIsDirty = false; } @Override @@ -424,18 +479,26 @@ public final class NotificationProgressBar extends ProgressBar { * Draw the tracker. */ private void drawTracker(Canvas canvas) { - if (mTracker != null) { - final int saveCount = canvas.save(); - // Translate the canvas origin to tracker position to make the draw matrix and the RtL - // transformations work. - canvas.translate(mPaddingLeft + mTrackerPos, mPaddingTop); + if (mTracker == null) return; + + if (mTrackerPosIsDirty) { + setTrackerPos(getWidth(), mTracker, mScale, Integer.MIN_VALUE); + } + + final int saveCount = canvas.save(); + // Translate the canvas origin to tracker position to make the draw matrix and the RtL + // transformations work. + canvas.translate(mPaddingLeft + mTrackerPos, mPaddingTop); + + if (mTrackerHeight > 0) { canvas.clipRect(0, 0, mTrackerWidth, mTrackerHeight); - if (mTrackerDrawMatrix != null) { - canvas.concat(mTrackerDrawMatrix); - } - mTracker.draw(canvas); - canvas.restoreToCount(saveCount); } + + if (mTrackerDrawMatrix != null) { + canvas.concat(mTrackerDrawMatrix); + } + mTracker.draw(canvas); + canvas.restoreToCount(saveCount); } @Override @@ -468,7 +531,7 @@ public final class NotificationProgressBar extends ProgressBar { final Drawable tracker = mTracker; if (tracker != null) { - setTrackerPos(getWidth(), tracker, getScale(), Integer.MIN_VALUE); + setTrackerPos(getWidth(), tracker, mScale, Integer.MIN_VALUE); // Since we draw translated, the drawable's bounds that it signals // for invalidation won't be the actual bounds we want invalidated, diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index ded1a9949ef8..1e7bfe32ba79 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -36,6 +36,8 @@ using ::android::base::unique_fd; namespace android { +static constexpr bool kLogWeakReachableDeletedAssets = false; + static struct overlayableinfo_offsets_t { jclass classObject; jmethodID constructor; @@ -97,7 +99,7 @@ static void DeleteGuardedApkAssets(Guarded<AssetManager2::ApkAssetsPtr>& apk_ass if (useCount > 1) { ALOGW("ApkAssets: Deleting an object '%s' with %d > 1 strong and %d weak references", (*assets)->GetDebugName().c_str(), int(useCount), int(weakCount)); - } else if (weakCount > 0) { + } else if constexpr (kLogWeakReachableDeletedAssets) if (weakCount > 0) { ALOGW("ApkAssets: Deleting an ApkAssets object '%s' with %d weak references", (*assets)->GetDebugName().c_str(), int(weakCount)); } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 68e642086636..0c243d1dc185 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -119,6 +119,7 @@ static struct { jfieldID renderFrameRate; jfieldID hasArrSupport; jfieldID frameRateCategoryRate; + jfieldID supportedRefreshRates; jfieldID supportedColorModes; jfieldID activeColorMode; jfieldID hdrCapabilities; @@ -1508,6 +1509,21 @@ static jobject nativeGetDynamicDisplayInfo(JNIEnv* env, jclass clazz, jlong disp env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.hasArrSupport, info.hasArrSupport); env->SetObjectField(object, gDynamicDisplayInfoClassInfo.frameRateCategoryRate, convertFrameRateCategoryRateToJavaObject(env, info.frameRateCategoryRate)); + + jfloatArray supportedRefreshRatesArray = env->NewFloatArray(info.supportedRefreshRates.size()); + if (supportedRefreshRatesArray == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return NULL; + } + jfloat* supportedRefreshRatesArrayValues = + env->GetFloatArrayElements(supportedRefreshRatesArray, 0); + for (size_t i = 0; i < info.supportedRefreshRates.size(); i++) { + supportedRefreshRatesArrayValues[i] = static_cast<jfloat>(info.supportedRefreshRates[i]); + } + env->ReleaseFloatArrayElements(supportedRefreshRatesArray, supportedRefreshRatesArrayValues, 0); + env->SetObjectField(object, gDynamicDisplayInfoClassInfo.supportedRefreshRates, + supportedRefreshRatesArray); + jintArray colorModesArray = env->NewIntArray(info.supportedColorModes.size()); if (colorModesArray == NULL) { jniThrowException(env, "java/lang/OutOfMemoryError", NULL); @@ -2766,6 +2782,8 @@ int register_android_view_SurfaceControl(JNIEnv* env) gFrameRateCategoryRateClassInfo.ctor = GetMethodIDOrDie(env, frameRateCategoryRateClazz, "<init>", "(FF)V"); + gDynamicDisplayInfoClassInfo.supportedRefreshRates = + GetFieldIDOrDie(env, dynamicInfoClazz, "supportedRefreshRates", "[F"); gDynamicDisplayInfoClassInfo.supportedColorModes = GetFieldIDOrDie(env, dynamicInfoClazz, "supportedColorModes", "[I"); gDynamicDisplayInfoClassInfo.activeColorMode = diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 6af742fb23f4..2e0fe9eb13d9 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -256,6 +256,12 @@ message SecureSettingsProto { } optional Display display = 100; + message DoubleTapPowerButton { + optional SettingProto gesture_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto gesture = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + } + optional DoubleTapPowerButton double_tap_power_button = 103; + message Doze { option (android.msg_privacy).dest = DEST_EXPLICIT; @@ -737,5 +743,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 103; + // Next tag = 104; } diff --git a/core/res/Android.bp b/core/res/Android.bp index 8042b30df4dc..aacd8699c202 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -160,6 +160,7 @@ android_app { "android.app.contextualsearch.flags-aconfig", "android.app.flags-aconfig", "android.appwidget.flags-aconfig", + "android.companion.virtualdevice.flags-aconfig", "android.content.pm.flags-aconfig", "android.media.audio-aconfig", "android.provider.flags-aconfig", diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index cedcd2feafc3..e2f3d2a32d0b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -848,10 +848,10 @@ <protected-broadcast android:name="android.app.action.CONSOLIDATED_NOTIFICATION_POLICY_CHANGED" /> <protected-broadcast android:name="android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED" /> <protected-broadcast android:name="com.android.uwb.uwbcountrycode.GEOCODE_RETRY" /> - <protected-broadcast android:name="android.telephony.action.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED" /> + <protected-broadcast android:name="android.telephony.satellite.action.SATELLITE_SUBSCRIBER_ID_LIST_CHANGED" /> <protected-broadcast android:name="android.service.ondeviceintelligence.MODEL_LOADED" /> <protected-broadcast android:name="android.service.ondeviceintelligence.MODEL_UNLOADED" /> - <protected-broadcast android:name="android.telephony.action.ACTION_SATELLITE_START_NON_EMERGENCY_SESSION" /> + <protected-broadcast android:name="android.telephony.satellite.action.SATELLITE_START_NON_EMERGENCY_SESSION" /> <!-- ====================================================================== --> @@ -6516,7 +6516,7 @@ @hide --> <permission android:name="android.permission.BYPASS_CONCURRENT_RECORD_AUDIO_RESTRICTION" android:featureFlag="android.media.audio.concurrent_audio_record_bypass_permission" - android:protectionLevel="signature|privileged|role" /> + android:protectionLevel="signature|privileged" /> <!-- @SystemApi Allows an application to capture audio for hotword detection. <p>Not for use by third-party applications.</p> @@ -8060,6 +8060,13 @@ <permission android:name="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY" android:protectionLevel="signature|role"/> + <!-- Allows an application to create displays that mirror other displays' content. + @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE) + @hide @SystemApi --> + <permission android:name="android.permission.ADD_MIRROR_DISPLAY" + android:protectionLevel="internal|role" + android:featureFlag="android.companion.virtualdevice.flags.enable_limited_vdm_role" /> + <!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. --> <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS" android:protectionLevel="signature|role" /> @@ -9344,6 +9351,17 @@ </intent-filter> </service> + <service android:name="com.android.ecm.EnhancedConfirmationCallTrackerService" + android:permission="android.permission.BIND_INCALL_SERVICE" + android:featureFlag="android.permission.flags.enhanced_confirmation_in_call_apis_enabled" + android:exported="true"> + <meta-data android:name="android.telecom.INCLUDE_SELF_MANAGED_CALLS" + android:value="true" /> + <intent-filter> + <action android:name="android.telecom.InCallService"/> + </intent-filter> + </service> + <service android:name="com.android.server.companion.datatransfer.contextsync.CallMetadataSyncConnectionService" android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" android:exported="true"> diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml index b6b8eac3a547..0314bbec2b55 100644 --- a/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml +++ b/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml @@ -18,7 +18,7 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="@color/btn_material_filled_tonal_background_color"/> - <corners android:radius="@dimen/config_bottomDialogCornerRadius" /> + <corners android:radius="@dimen/config_wearMaterial3_bottomDialogCornerRadius" /> <size android:width="@dimen/dialog_btn_negative_width" android:height="@dimen/dialog_btn_negative_height" /> diff --git a/core/res/res/layout-sw600dp/preference_list_content_single.xml b/core/res/res/layout-sw600dp/preference_list_content_single.xml index 88b1aa8d4d00..ebe4eca1b156 100644 --- a/core/res/res/layout-sw600dp/preference_list_content_single.xml +++ b/core/res/res/layout-sw600dp/preference_list_content_single.xml @@ -19,6 +19,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" + android:fitsSystemWindows="true" android:layout_height="match_parent" android:layout_width="match_parent"> diff --git a/core/res/res/layout-watch-v36/alert_dialog_icon_button_wear_material3.xml b/core/res/res/layout-watch-v36/alert_dialog_icon_button_wear_material3.xml new file mode 100644 index 000000000000..407ec7a42740 --- /dev/null +++ b/core/res/res/layout-watch-v36/alert_dialog_icon_button_wear_material3.xml @@ -0,0 +1,123 @@ +<!-- + ~ 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. + --> + +<!-- This layout is the AlertDialog template. It overrides the system layout with the same name. + Make sure to include all the existing id of the overridden alert_dialog_material.--> +<com.android.internal.widget.WatchListDecorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/parentPanel" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <ScrollView + android:id="@+id/scrollView" + android:fillViewport="true" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <!-- Top Panel --> + <FrameLayout + android:paddingLeft="?dialogPreferredPadding" + android:paddingRight="?dialogPreferredPadding" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/topPanel" + android:minHeight="@dimen/dialog_list_padding_top_no_title"> + <include android:id="@+id/title_template" + android:layout_width="match_parent" + android:layout_height="wrap_content" + layout="@layout/alert_dialog_title_material"/> + </FrameLayout> + + <!-- Content Panel --> + <FrameLayout android:id="@+id/contentPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipToPadding="false"> + <TextView android:id="@+id/message" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_horizontal|top" + android:textAppearance="@style/TextAppearance.DeviceDefault.Body1" + android:paddingStart="?dialogPreferredPadding" + android:paddingEnd="?dialogPreferredPadding" + android:paddingTop="8dip" + android:paddingBottom="8dip"/> + </FrameLayout> + + <!-- Custom Panel, to replace content panel if needed --> + <FrameLayout android:id="@+id/customPanel" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:minHeight="64dp"> + <FrameLayout android:id="@+android:id/custom" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </FrameLayout> + + <!-- Button Panel --> + <FrameLayout + android:id="@+id/buttonPanel" + android:minHeight="@dimen/dialog_list_padding_bottom_no_buttons" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center"> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:orientation="horizontal" + android:paddingBottom="?dialogPreferredPadding" + style="?android:attr/buttonBarStyle" + android:measureWithLargestChild="true"> + <Button android:id="@+id/button2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center" + android:layout_weight="1" + style="@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Negative" /> + <Button android:id="@+id/button3" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center" + android:layout_weight="1" + style="?android:attr/buttonBarButtonStyle"/> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + <Button android:id="@+id/button1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:gravity="center" + android:layout_weight="1" + style="@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Confirm" /> + <!-- This works as background. --> + <ImageView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:src="@drawable/dialog_alert_button_positive"/> + </FrameLayout> + </LinearLayout> + </FrameLayout> + </LinearLayout> + </ScrollView> +</com.android.internal.widget.WatchListDecorLayout> diff --git a/core/res/res/layout-watch-v36/alert_dialog_material.xml b/core/res/res/layout-watch-v36/alert_dialog_material.xml index 900102f379d9..8f7545690142 100644 --- a/core/res/res/layout-watch-v36/alert_dialog_material.xml +++ b/core/res/res/layout-watch-v36/alert_dialog_material.xml @@ -82,9 +82,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom" - android:orientation="horizontal" + android:orientation="vertical" android:paddingBottom="?dialogPreferredPadding" - style="?android:attr/buttonBarStyle" android:measureWithLargestChild="true"> <Button android:id="@+id/button2" android:layout_width="wrap_content" @@ -92,7 +91,7 @@ android:layout_gravity="center" android:gravity="center" android:layout_weight="1" - style="@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Negative" /> + style="@*android:style/Widget.DeviceDefault.Button.WearMaterial3"/> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -100,22 +99,13 @@ android:gravity="center" android:layout_weight="1" style="?android:attr/buttonBarButtonStyle"/> - <FrameLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - <Button android:id="@+id/button1" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center" - android:gravity="center" - android:layout_weight="1" - style="@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Confirm"/> - <!-- This works as background. --> - <ImageView + <Button android:id="@+id/button1" android:layout_width="match_parent" android:layout_height="match_parent" - android:src="@drawable/dialog_alert_button_positive"/> - </FrameLayout> + android:layout_gravity="center" + android:gravity="center" + android:layout_weight="1" + style="@*android:style/Widget.DeviceDefault.Button.Filled"/> </LinearLayout> </FrameLayout> </LinearLayout> diff --git a/core/res/res/layout/notification_2025_reply_container.xml b/core/res/res/layout/notification_2025_reply_container.xml new file mode 100644 index 000000000000..6923b59f34dc --- /dev/null +++ b/core/res/res/layout/notification_2025_reply_container.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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 + --> + +<!-- Note: this layout is included from a view stub; layout attributes will be overridden. --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/notification_material_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingStart="@dimen/notification_2025_content_margin_start"> + + <ImageView + android:layout_width="match_parent" + android:layout_height="1dip" + android:id="@+id/action_divider" + android:layout_marginTop="@dimen/notification_content_margin" + android:layout_marginBottom="@dimen/notification_content_margin" + android:layout_marginEnd="@dimen/notification_content_margin_end" + android:background="@drawable/notification_template_divider" /> + + <TextView + android:id="@+id/notification_material_reply_text_3" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/notification_content_margin_end" + android:visibility="gone" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply" + android:singleLine="true" /> + + <TextView + android:id="@+id/notification_material_reply_text_2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/notification_content_margin_end" + android:visibility="gone" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply" + android:singleLine="true" /> + + <LinearLayout + android:id="@+id/notification_material_reply_text_1_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginEnd="@dimen/notification_content_margin_end"> + <TextView + android:id="@+id/notification_material_reply_text_1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginEnd="@dimen/notification_content_margin_end" + android:layout_gravity="center" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Reply" + android:singleLine="true" /> + <ProgressBar + android:id="@+id/notification_material_reply_progress" + android:layout_height="@dimen/messaging_group_sending_progress_size" + android:layout_width="@dimen/messaging_group_sending_progress_size" + android:layout_marginStart="@dimen/notification_2025_content_margin_start" + android:layout_gravity="center" + android:indeterminate="true" + style="?android:attr/progressBarStyleSmall" /> + </LinearLayout> + +</LinearLayout> diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml index c003820a247f..a790e5da20de 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_base.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml @@ -123,7 +123,7 @@ <!-- This is the simplest way to keep this text vertically centered without gravity="center_vertical" which causes jumpiness in expansion animations. --> <include - layout="@layout/notification_template_text" + layout="@layout/notification_2025_text" android:layout_width="match_parent" android:layout_height="@dimen/notification_text_height" android:layout_gravity="center_vertical" diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml new file mode 100644 index 000000000000..427c4e42e40c --- /dev/null +++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml @@ -0,0 +1,197 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2014 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 + --> + +<!-- Note: This is the old media style notification (different from UMO). --> + +<!-- extends FrameLayout --> +<com.android.internal.widget.MediaNotificationView + android:id="@+id/status_bar_latest_event_content" + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_min_height" + android:tag="media" + > + + + <ImageView + android:id="@+id/left_icon" + android:layout_width="@dimen/notification_2025_left_icon_size" + android:layout_height="@dimen/notification_2025_left_icon_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_left_icon_start" + android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + android:visibility="gone" + /> + + <com.android.internal.widget.NotificationRowIconView + android:id="@+id/icon" + android:layout_width="@dimen/notification_2025_icon_circle_size" + android:layout_height="@dimen/notification_2025_icon_circle_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_icon_circle_start" + android:background="@drawable/notification_icon_circle" + android:padding="@dimen/notification_2025_icon_circle_padding" + /> + + <FrameLayout + android:id="@+id/alternate_expand_target" + android:layout_width="@dimen/notification_2025_content_margin_start" + android:layout_height="match_parent" + android:layout_gravity="start" + android:importantForAccessibility="no" + android:focusable="false" + /> + + <LinearLayout + android:id="@+id/notification_headerless_view_row" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginStart="@dimen/notification_2025_content_margin_start" + android:orientation="horizontal" + > + + <LinearLayout + android:id="@+id/notification_headerless_view_column" + android:layout_width="0px" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_weight="1" + android:layout_marginBottom="@dimen/notification_headerless_margin_twoline" + android:layout_marginTop="@dimen/notification_headerless_margin_twoline" + android:orientation="vertical" + > + + <NotificationTopLineView + android:id="@+id/notification_top_line" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_headerless_line_height" + android:clipChildren="false" + android:theme="@style/Theme.DeviceDefault.Notification" + > + + <!-- + NOTE: The notification_top_line_views layout contains the app_name_text. + In order to include the title view at the beginning, the Notification.Builder + has logic to hide that view whenever this title view is to be visible. + --> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/notification_header_separating_margin" + android:ellipsize="end" + android:fadingEdge="horizontal" + android:singleLine="true" + android:textAlignment="viewStart" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" + /> + + <include layout="@layout/notification_top_line_views" /> + + </NotificationTopLineView> + + <LinearLayout + android:id="@+id/notification_main_column" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + > + + <com.android.internal.widget.NotificationVanishingFrameLayout + android:layout_width="match_parent" + android:layout_height="@dimen/notification_headerless_line_height" + > + <!-- This is the simplest way to keep this text vertically centered without + gravity="center_vertical" which causes jumpiness in expansion animations. --> + <include + layout="@layout/notification_template_text" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_text_height" + android:layout_gravity="center_vertical" + android:layout_marginTop="0dp" + /> + </com.android.internal.widget.NotificationVanishingFrameLayout> + + <include + layout="@layout/notification_template_progress" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_headerless_line_height" + /> + + </LinearLayout> + + </LinearLayout> + + <ImageView + android:id="@+id/right_icon" + android:layout_width="@dimen/notification_right_icon_size" + android:layout_height="@dimen/notification_right_icon_size" + android:layout_gravity="center_vertical|end" + android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" + android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" + android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + /> + + <LinearLayout + android:id="@+id/media_actions" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layoutDirection="ltr" + android:orientation="horizontal" + > + <include + layout="@layout/notification_material_media_action" + android:id="@+id/action0" + /> + <include + layout="@layout/notification_material_media_action" + android:id="@+id/action1" + /> + <include + layout="@layout/notification_material_media_action" + android:id="@+id/action2" + /> + </LinearLayout> + + <FrameLayout + android:id="@+id/expand_button_touch_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="@dimen/notification_content_margin_end" + > + + <include layout="@layout/notification_expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|end" + /> + + </FrameLayout> + + </LinearLayout> +</com.android.internal.widget.MediaNotificationView> diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml new file mode 100644 index 000000000000..f0e4c0f272fb --- /dev/null +++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml @@ -0,0 +1,220 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2016 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 + --> + +<!-- Note: This is the old "Messaging Style" notification (not a conversation). --> + +<!-- extends FrameLayout --> +<com.android.internal.widget.MessagingLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:tag="messaging" + > + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:orientation="vertical" + > + + + <com.android.internal.widget.NotificationMaxHeightFrameLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_min_height" + android:clipChildren="false" + > + + <ImageView + android:id="@+id/left_icon" + android:layout_width="@dimen/notification_2025_left_icon_size" + android:layout_height="@dimen/notification_2025_left_icon_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_left_icon_start" + android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + android:visibility="gone" + /> + + <com.android.internal.widget.NotificationRowIconView + android:id="@+id/icon" + android:layout_width="@dimen/notification_2025_icon_circle_size" + android:layout_height="@dimen/notification_2025_icon_circle_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_icon_circle_start" + android:background="@drawable/notification_icon_circle" + android:padding="@dimen/notification_2025_icon_circle_padding" + /> + + <FrameLayout + android:id="@+id/alternate_expand_target" + android:layout_width="@dimen/notification_2025_content_margin_start" + android:layout_height="match_parent" + android:layout_gravity="start" + android:importantForAccessibility="no" + android:focusable="false" + /> + + <!-- + NOTE: to make the expansion animation of id/notification_messaging happen vertically, + its X positioning must be the left edge of the notification, so instead of putting the + layout_marginStart on the id/notification_headerless_view_row, we put it on + id/notification_top_line, making the layout here just a bit different from the base. + --> + <LinearLayout + android:id="@+id/notification_headerless_view_row" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipChildren="false" + > + + <!-- + NOTE: because messaging will always have 2 lines, this LinearLayout should NOT + have the id/notification_headerless_view_column, as that is used for modifying + vertical margins to accommodate the single-line state that base supports + --> + <LinearLayout + android:layout_width="0px" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_weight="1" + android:layout_marginBottom="@dimen/notification_headerless_margin_twoline" + android:layout_marginTop="@dimen/notification_headerless_margin_twoline" + android:layout_marginStart="@dimen/notification_2025_content_margin_start" + android:clipChildren="false" + android:orientation="vertical" + > + + <NotificationTopLineView + android:id="@+id/notification_top_line" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_headerless_line_height" + android:clipChildren="false" + android:theme="@style/Theme.DeviceDefault.Notification" + > + + <!-- + NOTE: The notification_top_line_views layout contains the app_name_text. + In order to include the title view at the beginning, the Notification.Builder + has logic to hide that view whenever this title view is to be visible. + --> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/notification_header_separating_margin" + android:ellipsize="end" + android:fadingEdge="horizontal" + android:singleLine="true" + android:textAlignment="viewStart" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" + /> + + <include layout="@layout/notification_top_line_views" /> + + </NotificationTopLineView> + + <LinearLayout + android:id="@+id/notification_main_column" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:clipChildren="false" + > + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/notification_messaging" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:spacing="@dimen/notification_messaging_spacing" /> + </LinearLayout> + + </LinearLayout> + + <!-- Images --> + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/conversation_image_message_container" + android:layout_width="@dimen/notification_right_icon_size" + android:layout_height="@dimen/notification_right_icon_size" + android:layout_gravity="center_vertical|end" + android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" + android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" + android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:forceHasOverlappingRendering="false" + android:spacing="0dp" + android:clipChildren="false" + android:visibility="gone" + /> + + <ImageView + android:id="@+id/right_icon" + android:layout_width="@dimen/notification_right_icon_size" + android:layout_height="@dimen/notification_right_icon_size" + android:layout_gravity="center_vertical|end" + android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" + android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" + android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + /> + + <FrameLayout + android:id="@+id/expand_button_touch_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="@dimen/notification_content_margin_end" + > + + <include layout="@layout/notification_expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|end" + /> + + </FrameLayout> + + </LinearLayout> + + </com.android.internal.widget.NotificationMaxHeightFrameLayout> + + <LinearLayout + android:id="@+id/notification_action_list_margin_target" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="-20dp" + android:clipChildren="false" + android:orientation="vertical"> + <include layout="@layout/notification_template_smart_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_content_margin" + android:layout_marginStart="@dimen/notification_2025_content_margin_start" + android:layout_marginEnd="@dimen/notification_content_margin_end" /> + <include layout="@layout/notification_material_action_list" /> + </LinearLayout> +</LinearLayout> +</com.android.internal.widget.MessagingLayout> diff --git a/core/res/res/layout/notification_2025_template_expanded_base.xml b/core/res/res/layout/notification_2025_template_expanded_base.xml new file mode 100644 index 000000000000..e480fe5eef1e --- /dev/null +++ b/core/res/res/layout/notification_2025_template_expanded_base.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ 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 + --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:clipChildren="false" + android:tag="big" + > + + <LinearLayout + android:id="@+id/notification_action_list_margin_target" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/notification_content_margin" + android:orientation="vertical" + > + + <FrameLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_gravity="top" + > + + <include layout="@layout/notification_2025_template_header" /> + + <LinearLayout + android:id="@+id/notification_main_column" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/notification_2025_content_margin_start" + android:layout_marginEnd="@dimen/notification_content_margin_end" + android:layout_marginTop="@dimen/notification_content_margin_top" + android:orientation="vertical" + > + + <include layout="@layout/notification_template_part_line1" /> + + <include layout="@layout/notification_template_text_multiline" /> + + <include + android:layout_width="match_parent" + android:layout_height="@dimen/notification_progress_bar_height" + android:layout_marginTop="@dimen/notification_progress_margin_top" + layout="@layout/notification_template_progress" + /> + </LinearLayout> + + <include layout="@layout/notification_template_right_icon" /> + </FrameLayout> + + <ViewStub + android:layout="@layout/notification_2025_reply_container" + android:id="@+id/notification_material_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + /> + + <include + layout="@layout/notification_template_smart_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/notification_2025_content_margin_start" + android:layout_marginEnd="@dimen/notification_content_margin_end" + android:layout_marginTop="@dimen/notification_content_margin" + /> + + <include layout="@layout/notification_material_action_list" /> + </LinearLayout> +</FrameLayout> diff --git a/core/res/res/layout/notification_2025_text.xml b/core/res/res/layout/notification_2025_text.xml new file mode 100644 index 000000000000..48b1083a5e53 --- /dev/null +++ b/core/res/res/layout/notification_2025_text.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ 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 + --> +<com.android.internal.widget.ImageFloatingTextView + xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/Widget.DeviceDefault.Notification.Text" + android:id="@+id/text" + android:layout_width="match_parent" + android:layout_height="@dimen/notification_text_height" + android:layout_gravity="top" + android:layout_marginTop="@dimen/notification_text_margin_top" + android:fadingEdge="horizontal" + android:gravity="top" + android:maxLines="1" + android:textAlignment="viewStart" + /> diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 565d584875ac..57959361bd48 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -13,7 +13,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<!-- extends FrameLayout --> +<!-- extends RelativeLayout --> <NotificationHeaderView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/notification_header" @@ -62,7 +62,7 @@ android:layout_height="match_parent" android:layout_alignParentStart="true" android:layout_centerVertical="true" - android:layout_toStartOf="@id/notification_buttons_column" + android:layout_toStartOf="@id/expand_button" android:layout_alignWithParentIfMissing="true" android:clipChildren="false" android:gravity="center_vertical" @@ -83,28 +83,17 @@ android:focusable="false" /> - <LinearLayout - android:id="@+id/notification_buttons_column" + <include layout="@layout/notification_expand_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentEnd="true" - android:orientation="vertical" - > - - <include layout="@layout/notification_close_button" - android:layout_width="@dimen/notification_close_button_size" - android:layout_height="@dimen/notification_close_button_size" - android:layout_gravity="end" - android:layout_marginEnd="20dp" - /> - - <include layout="@layout/notification_expand_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentEnd="true" - android:layout_centerVertical="true" - /> + android:layout_centerVertical="true" + android:layout_alignParentEnd="true" /> - </LinearLayout> + <include layout="@layout/notification_close_button" + android:id="@+id/close_button" + android:layout_width="@dimen/notification_close_button_size" + android:layout_height="@dimen/notification_close_button_size" + android:layout_alignParentTop="true" + android:layout_alignParentEnd="true" /> </NotificationHeaderView> diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml index 29f14a4a93fc..227f84bb2eed 100644 --- a/core/res/res/layout/notification_template_material_base.xml +++ b/core/res/res/layout/notification_template_material_base.xml @@ -157,39 +157,27 @@ android:maxDrawableHeight="@dimen/notification_right_icon_size" /> - <LinearLayout - android:id="@+id/notification_buttons_column" + <FrameLayout + android:id="@+id/expand_button_touch_container" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_alignParentEnd="true" - android:orientation="vertical" + android:minWidth="@dimen/notification_content_margin_end" > - <include layout="@layout/notification_close_button" - android:layout_width="@dimen/notification_close_button_size" - android:layout_height="@dimen/notification_close_button_size" - android:layout_gravity="end" - android:layout_marginEnd="20dp" - /> - - <FrameLayout - android:id="@+id/expand_button_touch_container" + <include layout="@layout/notification_expand_button" android:layout_width="wrap_content" - android:layout_height="0dp" - android:layout_weight="1" - android:minWidth="@dimen/notification_content_margin_end" - > - - <include layout="@layout/notification_expand_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical|end" - /> - - </FrameLayout> + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|end" + /> - </LinearLayout> + </FrameLayout> </LinearLayout> + <include layout="@layout/notification_close_button" + android:id="@+id/close_button" + android:layout_width="@dimen/notification_close_button_size" + android:layout_height="@dimen/notification_close_button_size" + android:layout_gravity="top|end" /> + </FrameLayout> diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml index 6e9d17fc31d4..5459fa895f82 100644 --- a/core/res/res/layout/notification_template_material_media.xml +++ b/core/res/res/layout/notification_template_material_media.xml @@ -15,6 +15,7 @@ ~ limitations under the License --> +<!-- extends FrameLayout --> <com.android.internal.widget.MediaNotificationView android:id="@+id/status_bar_latest_event_content" xmlns:android="http://schemas.android.com/apk/res/android" @@ -191,4 +192,11 @@ </FrameLayout> </LinearLayout> + + <include layout="@layout/notification_close_button" + android:id="@+id/close_button" + android:layout_width="@dimen/notification_close_button_size" + android:layout_height="@dimen/notification_close_button_size" + android:layout_gravity="top|end" /> + </com.android.internal.widget.MediaNotificationView> diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml index 1eae41da1e81..2b3b7d873f4f 100644 --- a/core/res/res/layout/notification_template_material_messaging.xml +++ b/core/res/res/layout/notification_template_material_messaging.xml @@ -195,6 +195,12 @@ </LinearLayout> + <include layout="@layout/notification_close_button" + android:id="@+id/close_button" + android:layout_width="@dimen/notification_close_button_size" + android:layout_height="@dimen/notification_close_button_size" + android:layout_gravity="top|end" /> + </com.android.internal.widget.NotificationMaxHeightFrameLayout> <LinearLayout diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml index bed80edf0ee7..7a2fb0b3b822 100644 --- a/core/res/res/layout/preference_list_content.xml +++ b/core/res/res/layout/preference_list_content.xml @@ -20,6 +20,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" + android:fitsSystemWindows="true" android:layout_height="match_parent" android:layout_width="match_parent"> diff --git a/core/res/res/layout/preference_list_content_material.xml b/core/res/res/layout/preference_list_content_material.xml index 37b411918ef6..23c82509b6b0 100644 --- a/core/res/res/layout/preference_list_content_material.xml +++ b/core/res/res/layout/preference_list_content_material.xml @@ -20,6 +20,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" + android:fitsSystemWindows="true" android:layout_height="match_parent" android:layout_width="match_parent"> diff --git a/core/res/res/layout/preference_list_content_single.xml b/core/res/res/layout/preference_list_content_single.xml index 726ce78b281f..4f072da99546 100644 --- a/core/res/res/layout/preference_list_content_single.xml +++ b/core/res/res/layout/preference_list_content_single.xml @@ -19,6 +19,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" + android:fitsSystemWindows="true" android:layout_height="match_parent" android:layout_width="match_parent"> diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml index c43975e4ad3c..44a5df9b60be 100644 --- a/core/res/res/layout/preference_list_fragment.xml +++ b/core/res/res/layout/preference_list_fragment.xml @@ -19,6 +19,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" + android:fitsSystemWindows="true" android:layout_height="match_parent" android:layout_width="match_parent" android:background="@android:color/transparent" diff --git a/core/res/res/layout/preference_list_fragment_material.xml b/core/res/res/layout/preference_list_fragment_material.xml index db2fe7d038e0..4df76029e606 100644 --- a/core/res/res/layout/preference_list_fragment_material.xml +++ b/core/res/res/layout/preference_list_fragment_material.xml @@ -19,6 +19,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" + android:fitsSystemWindows="true" android:layout_height="match_parent" android:layout_width="match_parent" android:background="@android:color/transparent" diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values-watch-v36/config.xml index 1143ae30fe9d..679dc709ec35 100644 --- a/core/res/res/values-watch-v36/config.xml +++ b/core/res/res/values-watch-v36/config.xml @@ -16,5 +16,5 @@ <resources> <dimen name="config_wearMaterial3_buttonCornerRadius">26dp</dimen> - <dimen name="config_bottomDialogCornerRadius">18dp</dimen> + <dimen name="config_wearMaterial3_bottomDialogCornerRadius">18dp</dimen> </resources> diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml index 00f3f092b768..7da7435930b1 100644 --- a/core/res/res/values-watch-v36/styles_material.xml +++ b/core/res/res/values-watch-v36/styles_material.xml @@ -57,7 +57,7 @@ </style> <!-- AlertDialog Styles --> - <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog" parent="Widget.DeviceDefault.Button"> + <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3" parent="Widget.DeviceDefault.Button"> <item name="android:textSize">0sp</item> <item name="android:gravity">center</item> <item name="android:paddingStart">0dp</item> @@ -65,14 +65,14 @@ <item name="android:drawablePadding">0dp</item> </style> - <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Confirm" parent="Widget.DeviceDefault.Button.ButtonBar.AlertDialog"> + <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Confirm"> <!-- Use a ImageView as background --> <item name="background">@android:color/transparent</item> <item name="minWidth">@dimen/dialog_btn_confirm_width</item> <item name="minHeight">@dimen/dialog_btn_confirm_height</item> </style> - <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Negative" parent="Widget.DeviceDefault.Button.ButtonBar.AlertDialog"> + <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3.Negative"> <item name="background">@drawable/dialog_alert_button_negative</item> <item name="minWidth">@dimen/dialog_btn_negative_width</item> <item name="minHeight">@dimen/dialog_btn_negative_height</item> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index f6590b1360f8..8c46ccc84f39 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4808,6 +4808,12 @@ <!-- Whether the device should default to observe mode when this service is default or in the foreground. --> <attr name="shouldDefaultToObserveMode" format="boolean"/> + <!-- Whether this service should share the same AID routing priority as the role + owner. This package and the role owner must have the same signature, and the + role owner must opt into this behavior by using the property named by + {@link android.nfc.cardemulation.CardEmulation.PROPERTY_ALLOW_SHARED_ROLE_PRIORITY } + in the <code><application&rt;</code> tag. --> + <attr name="shareRolePriority" format="boolean"/> </declare-styleable> <!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that @@ -4835,6 +4841,7 @@ <!-- Whether the device should default to observe mode when this service is default or in the foreground. --> <attr name="shouldDefaultToObserveMode"/> + <attr name="shareRolePriority"/> </declare-styleable> <!-- Specify one or more <code>aid-group</code> elements inside a diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7f2c816e5654..73d696ba3567 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2731,6 +2731,8 @@ </string-array> <!-- The list of supported dream complications --> <integer-array name="config_supportedDreamComplications"> + <!-- COMPLICATION_TYPE_TIME --> + <item>1</item> </integer-array> <!-- Are we allowed to dream while not plugged in? --> @@ -6001,6 +6003,12 @@ <!-- If true, show multiuser switcher by default unless the user specifically disables it. --> <bool name="config_showUserSwitcherByDefault">false</bool> + <!-- If true, user can change state of multiuser switcher. --> + <bool name="config_allowChangeUserSwitcherEnabled">true</bool> + + <!-- If true, multiuser switcher would be automatically enabled when second user is created on the device. --> + <bool name="config_enableUserSwitcherUponUserCreation">true</bool> + <!-- Set to true to make assistant show in front of the dream/screensaver. --> <bool name="config_assistantOnTopOfDream">false</bool> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index e71277dc0414..3b39a65b6795 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -290,4 +290,7 @@ <!-- View tag associating a view with its overridden id, to ensure valid recycling only. --> <item type="id" name="remote_views_override_id" /> + + <!-- View tag associating a view with its id for widget metrics. --> + <item type="id" name="remoteViewsMetricsId" /> </resources> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index b0b87d1dead6..e75371d6bf46 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -133,9 +133,13 @@ <public name="alternateLauncherLabels"/> <!-- @FlaggedApi(android.content.pm.Flags.FLAG_APP_COMPAT_OPTION_16KB) --> <public name="pageSizeCompat" /> + <!-- @FlaggedApi(android.nfc.Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) --> + <public name="shareRolePriority"/> </staging-public-group> <staging-public-group type="id" first-id="0x01b60000"> + <!-- @FlaggedApi(android.appwidget.flags.Flags.FLAG_ENGAGEMENT_METRICS) --> + <public name="remoteViewsMetricsId"/> </staging-public-group> <staging-public-group type="style" first-id="0x01b50000"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index c13fdb17dfe3..413f0c3e0c58 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5357,6 +5357,8 @@ <string name="zen_mode_trigger_summary_range_symbol_combination"><xliff:g id="start" example="Sun">%1$s</xliff:g> - <xliff:g id="end" example="Thu">%2$s</xliff:g></string> <!-- [CHAR LIMIT=40] General template for a start - end range in a text summary, used for the trigger description of a Zen mode --> <string name="zen_mode_trigger_summary_range_words"><xliff:g id="start" example="Sunday">%1$s</xliff:g> to <xliff:g id="end" example="Thursday">%2$s</xliff:g></string> + <!-- [CHAR LIMIT=NONE] General template for combining a days of week start-end range with a time-based start-end range, for example: "Sun-Thurs, 10:00 PM - 7:00 AM" --> + <string name="zen_mode_trigger_summary_combined"><xliff:g id="days" example="Sat-Thurs">%1$s</xliff:g>,\u0020<xliff:g id="times" example="10:00 PM - 7:00 AM">%2$s</xliff:g></string> <!-- [CHAR LIMIT=40] Event-based rule calendar option value for any calendar, used for the trigger description of a Zen mode --> <string name="zen_mode_trigger_event_calendar_any">Any calendar</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b49e142e4cee..0622d7224411 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2390,8 +2390,11 @@ <java-symbol type="layout" name="notification_material_action_list" /> <java-symbol type="layout" name="notification_material_action_tombstone" /> <java-symbol type="layout" name="notification_2025_template_collapsed_base" /> + <java-symbol type="layout" name="notification_2025_template_expanded_base" /> <java-symbol type="layout" name="notification_2025_template_heads_up_base" /> <java-symbol type="layout" name="notification_2025_template_header" /> + <java-symbol type="layout" name="notification_2025_template_collapsed_messaging" /> + <java-symbol type="layout" name="notification_2025_template_collapsed_media" /> <java-symbol type="layout" name="notification_template_material_base" /> <java-symbol type="layout" name="notification_template_material_heads_up_base" /> <java-symbol type="layout" name="notification_template_material_compact_heads_up_base" /> @@ -2666,6 +2669,7 @@ <java-symbol type="string" name="zen_mode_trigger_summary_divider_text" /> <java-symbol type="string" name="zen_mode_trigger_summary_range_symbol_combination" /> <java-symbol type="string" name="zen_mode_trigger_summary_range_words" /> + <java-symbol type="string" name="zen_mode_trigger_summary_combined" /> <java-symbol type="string" name="zen_mode_trigger_event_calendar_any" /> <java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" /> @@ -4693,6 +4697,8 @@ <!-- If true, show multiuser switcher by default unless the user specifically disables it. --> <java-symbol type="bool" name="config_showUserSwitcherByDefault" /> + <java-symbol type="bool" name="config_allowChangeUserSwitcherEnabled" /> + <java-symbol type="bool" name="config_enableUserSwitcherUponUserCreation" /> <!-- Set to true to make assistant show in front of the dream/screensaver. --> <java-symbol type="bool" name="config_assistantOnTopOfDream"/> @@ -5109,7 +5115,6 @@ <java-symbol type="layout" name="notification_expand_button"/> <java-symbol type="id" name="close_button" /> <java-symbol type="layout" name="notification_close_button"/> - <java-symbol type="id" name="notification_buttons_column" /> <java-symbol type="bool" name="config_supportsMicToggle" /> <java-symbol type="bool" name="config_supportsCamToggle" /> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_ltr.9.png b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_ltr.9.png Binary files differnew file mode 100644 index 000000000000..c7e937c6d77a --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_ltr.9.png diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_tr.9.png b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_tr.9.png Binary files differnew file mode 100644 index 000000000000..a3cff989bad8 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable-mdpi/border_tr.9.png diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/power_other_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/power_other_24.xml new file mode 100644 index 000000000000..c9fb53ee78da --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/power_other_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorControlNormal" + android:viewportHeight="960" + android:viewportWidth="960"> + <path + android:fillColor="@android:color/white" + android:pathData="M720,600L720,520L800,520Q817,520 828.5,531.5Q840,543 840,560Q840,577 828.5,588.5Q817,600 800,600L720,600ZM720,760L720,680L800,680Q817,680 828.5,691.5Q840,703 840,720Q840,737 828.5,748.5Q817,760 800,760L720,760ZM560,800Q527,800 503.5,776.5Q480,753 480,720L400,720L400,560L480,560Q480,527 503.5,503.5Q527,480 560,480L680,480L680,800L560,800ZM280,680Q214,680 167,633Q120,586 120,520Q120,454 167,407Q214,360 280,360L340,360Q365,360 382.5,342.5Q400,325 400,300Q400,275 382.5,257.5Q365,240 340,240L200,240Q183,240 171.5,228.5Q160,217 160,200Q160,183 171.5,171.5Q183,160 200,160L340,160Q398,160 439,201Q480,242 480,300Q480,358 439,399Q398,440 340,440L280,440Q247,440 223.5,463.5Q200,487 200,520Q200,553 223.5,576.5Q247,600 280,600L360,600L360,680L280,680Z" /> +</vector>
\ No newline at end of file diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_off_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_off_24.xml new file mode 100644 index 000000000000..ca9482503c92 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_off_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorControlNormal" + android:viewportHeight="960" + android:viewportWidth="960"> + <path + android:fillColor="@android:color/white" + android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,800L280,840Q280,840 280,840Q280,840 280,840L680,840Q680,840 680,840Q680,840 680,840L680,800L280,800ZM280,720L680,240L280,720ZM280,160L680,160L680,120Q680,120 680,120Q680,120 680,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,160L280,120Q280,120 280,120Q280,120 280,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,800L280,800L280,840Q280,840 280,840Q280,840 280,840L280,840Q280,840 280,840Q280,840 280,840L280,800Z" /> +</vector>
\ No newline at end of file diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_on_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_on_24.xml new file mode 100644 index 000000000000..48f990c3ba37 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/screen_on_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:tint="?attr/colorControlNormal" + android:viewportHeight="960" + android:viewportWidth="960"> + <path + android:fillColor="@android:color/white" + android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,800L280,840Q280,840 280,840Q280,840 280,840L680,840Q680,840 680,840Q680,840 680,840L680,800L280,800ZM280,720L680,720L680,240L280,240L280,720ZM280,160L680,160L680,120Q680,120 680,120Q680,120 680,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,160L280,120Q280,120 280,120Q280,120 280,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,800L280,800L280,840Q280,840 280,840Q280,840 280,840L280,840Q280,840 280,840Q280,840 280,840L280,800Z" /> +</vector>
\ No newline at end of file diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml index be0e135af23a..e1f46232488c 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!-- ~ Copyright (C) 2020 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,46 +13,127 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" - android:minHeight="?android:attr/listPreferredItemHeightSmall" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingTop="8dp" - android:paddingBottom="8dp"> - - <ImageView - android:id="@+id/icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical" - android:layout_marginEnd="8dp" - android:paddingBottom="8dp"/> - - <TextView - android:id="@+id/title" - android:layout_width="0dp" - android:layout_weight="1" - android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearanceBody"/> + android:orientation="vertical"> - <TextView - android:id="@+id/value1" - android:layout_width="wrap_content" + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:gravity="right" - android:maxLines="1" - android:textAppearance="@style/TextAppearanceBody"/> - - <TextView - android:id="@+id/value2" - android:layout_width="76dp" + android:minHeight="?android:attr/listPreferredItemHeightSmall" + android:orientation="horizontal" + android:paddingBottom="8dp" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingTop="8dp"> + + <ImageView + android:id="@+id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginEnd="8dp" + android:paddingBottom="8dp" /> + + <TextView + android:id="@+id/title" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:textAppearance="@style/TextAppearanceBody" /> + + <TextView + android:id="@+id/value1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:gravity="right" + android:maxLines="1" + android:textAppearance="@style/TextAppearanceBody" /> + + <TextView + android:id="@+id/value2" + android:layout_width="76dp" + android:layout_height="wrap_content" + android:gravity="right" + android:maxLines="1" + android:textAppearance="@style/TextAppearanceBody" /> + </LinearLayout> + + <TableLayout + android:id="@+id/table" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="right" - android:maxLines="1" - android:textAppearance="@style/TextAppearanceBody"/> + android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd" + android:layout_marginStart="50dp" + android:stretchColumns="1,2,3,4"> + + <TableRow android:background="#EEFFEE"> + <LinearLayout + style="@style/TableCell.Start" + android:layout_width="65dp"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="3dip" + android:text="State" + android:textStyle="bold" /> + </LinearLayout> + + <RelativeLayout style="@style/TableCell.Inner"> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:src="@drawable/screen_on_24" + android:tint="@color/battery_consumer_slice_icon" /> + </RelativeLayout> + + <RelativeLayout style="@style/TableCell.Inner"> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:src="@drawable/screen_off_24" + android:tint="@color/battery_consumer_slice_icon" /> + </RelativeLayout> + + <RelativeLayout style="@style/TableCell.Inner"> + <ImageView + android:id="@+id/screen_on_24_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:src="@drawable/screen_on_24" + android:tint="@color/battery_consumer_slice_icon" /> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/screen_on_24_icon" + android:src="@drawable/power_other_24" + android:tint="@color/battery_consumer_slice_icon" /> + </RelativeLayout> + + <RelativeLayout style="@style/TableCell.End"> + <ImageView + android:id="@+id/screen_off_24_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:src="@drawable/screen_off_24" + android:tint="@color/battery_consumer_slice_icon" /> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/screen_off_24_icon" + android:src="@drawable/power_other_24" + android:tint="@color/battery_consumer_slice_icon" /> + </RelativeLayout> + </TableRow> + + <View + android:layout_height="1dip" + android:background="#000000" /> + </TableLayout> </LinearLayout> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml index 987de6bcb4f4..b88425a57187 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml @@ -14,16 +14,30 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<androidx.swiperefreshlayout.widget.SwipeRefreshLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/swipe_refresh" - android:paddingTop="?attr/actionBarSize" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:orientation="vertical" + android:fitsSystemWindows="true"> - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/list_view" + <androidx.appcompat.widget.Toolbar + android:id="@+id/toolbar" android:layout_width="match_parent" - android:layout_height="match_parent"/> + android:layout_height="?attr/actionBarSize" + android:background="?attr/colorPrimary" + android:elevation="4dp" + android:theme="@style/ThemeOverlay.AppCompat.ActionBar" /> -</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> + <androidx.swiperefreshlayout.widget.SwipeRefreshLayout + android:id="@+id/swipe_refresh" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/list_view" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> +</LinearLayout> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_slices_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_slices_layout.xml new file mode 100644 index 000000000000..642c0deac6f4 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_slices_layout.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ 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. + --> + +<TableRow xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <LinearLayout style="@style/TableCell.Start"> + <TextView + android:id="@+id/procState" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </LinearLayout> + + <LinearLayout + style="@style/TableCell.Inner" + android:orientation="vertical"> + <TextView + android:id="@+id/power_b_on" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + <TextView + android:id="@+id/duration_b_on" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + </LinearLayout> + + <LinearLayout + style="@style/TableCell.Inner" + android:orientation="vertical"> + <TextView + android:id="@+id/power_b_off" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + <TextView + android:id="@+id/duration_b_off" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + </LinearLayout> + + <LinearLayout + style="@style/TableCell.Inner" + android:orientation="vertical"> + <TextView + android:id="@+id/power_c_on" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + <TextView + android:id="@+id/duration_c_on" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + </LinearLayout> + + <LinearLayout + style="@style/TableCell.End" + android:orientation="vertical"> + <TextView + android:id="@+id/power_c_off" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + <TextView + android:id="@+id/duration_c_off" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="right" /> + </LinearLayout> +</TableRow> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml index 2d276a51a1da..46d8f04f9eef 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml @@ -17,13 +17,12 @@ <androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/swipe_refresh" - android:paddingTop="?attr/actionBarSize" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:fitsSystemWindows="true"> <LinearLayout android:orientation="vertical" - android:paddingTop="?attr/actionBarSize" android:layout_width="match_parent" android:layout_height="match_parent"> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml index 6cc70bd1af61..1dc288af89b6 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml @@ -18,4 +18,5 @@ <resources> <color name="battery_consumer_bg_power_profile">#ffffff</color> <color name="battery_consumer_bg_energy_consumption">#fff5eb</color> + <color name="battery_consumer_slice_icon">#aaaaaa</color> </resources> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml index fa30b2c8dc6f..a298cc9f59e3 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml @@ -17,11 +17,9 @@ --> <resources> - <style name="Theme" parent="Theme.MaterialComponents.Light"> + <style name="Theme" parent="Theme.MaterialComponents.Light.NoActionBar"> <item name="colorPrimary">#34a853</item> - <item name="android:windowActionBar">true</item> - <item name="android:windowNoTitle">false</item> - <item name="android:windowDrawsSystemBarBackgrounds">false</item> + <item name="toolbarStyle">@style/Widget.AppCompat.Toolbar</item> </style> <style name="LoadTestCardView" parent="Widget.MaterialComponents.CardView"> @@ -32,4 +30,25 @@ <item name="android:textColor">#000000</item> <item name="android:textSize">18sp</item> </style> -</resources>
\ No newline at end of file + + <style name="TableCell"> + <item name="android:layout_height">match_parent</item> + <item name="android:padding">4dp</item> + </style> + + <style name="TableCell.Start" parent="TableCell"> + <item name="android:background">@drawable/border_ltr</item> + </style> + + <style name="TableCell.Inner" parent="TableCell"> + <item name="android:background">@drawable/border_tr</item> + <item name="android:layout_width">0dp</item> + <item name="android:layout_weight">1</item> + </style> + + <style name="TableCell.End" parent="TableCell"> + <item name="android:background">@drawable/border_tr</item> + <item name="android:layout_width">0dp</item> + <item name="android:layout_weight">1</item> + </style> +</resources> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java index f691a1b90934..35175a755e89 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java @@ -31,22 +31,16 @@ public class BatteryConsumerData { public static final String UID_BATTERY_CONSUMER_ID_PREFIX = "APP|"; public static final String AGGREGATE_BATTERY_CONSUMER_ID = "SYS|"; - enum EntryType { - UID_TOTAL_POWER, - UID_POWER_PROFILE, - UID_POWER_PROFILE_PROCESS_STATE, - UID_POWER_ENERGY_CONSUMPTION, - UID_POWER_ENERGY_PROCESS_STATE, - UID_POWER_CUSTOM, - UID_DURATION, + public enum EntryType { DEVICE_TOTAL_POWER, - DEVICE_POWER_MODELED, + DEVICE_POWER, DEVICE_POWER_ENERGY_CONSUMPTION, DEVICE_POWER_CUSTOM, DEVICE_DURATION, + UID, } - enum ConsumerType { + public enum ConsumerType { UID_BATTERY_CONSUMER, DEVICE_POWER_COMPONENT, } @@ -56,34 +50,38 @@ public class BatteryConsumerData { public String title; public double value1; public double value2; + public List<Slice> slices; + } + + public static class Slice { + public int powerState; + public int screenState; + public int processState; + public double powerMah; + public long durationMs; } private BatteryConsumerInfoHelper.BatteryConsumerInfo mBatteryConsumerInfo; private final List<Entry> mEntries = new ArrayList<>(); public BatteryConsumerData(Context context, - List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId) { + BatteryUsageStats batteryUsageStats, String batteryConsumerId) { switch (getConsumerType(batteryConsumerId)) { case UID_BATTERY_CONSUMER: - populateForUidBatteryConsumer(context, batteryUsageStatsList, batteryConsumerId); + populateForUidBatteryConsumer(context, batteryUsageStats, batteryConsumerId); break; case DEVICE_POWER_COMPONENT: - populateForAggregateBatteryConsumer(context, batteryUsageStatsList); + populateForAggregateBatteryConsumer(context, batteryUsageStats); break; } } - private void populateForUidBatteryConsumer( - Context context, List<BatteryUsageStats> batteryUsageStatsList, + private void populateForUidBatteryConsumer(Context context, BatteryUsageStats batteryUsageStats, String batteryConsumerId) { - BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0); - BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1); BatteryConsumer requestedBatteryConsumer = getRequestedBatteryConsumer(batteryUsageStats, batteryConsumerId); - BatteryConsumer requestedModeledBatteryConsumer = getRequestedBatteryConsumer( - modeledBatteryUsageStats, batteryConsumerId); - if (requestedBatteryConsumer == null || requestedModeledBatteryConsumer == null) { + if (requestedBatteryConsumer == null) { mBatteryConsumerInfo = null; return; } @@ -92,118 +90,95 @@ public class BatteryConsumerData { batteryUsageStats, batteryConsumerId, context.getPackageManager()); double[] totalPowerByComponentMah = new double[BatteryConsumer.POWER_COMPONENT_COUNT]; - double[] totalModeledPowerByComponentMah = - new double[BatteryConsumer.POWER_COMPONENT_COUNT]; long[] totalDurationByComponentMs = new long[BatteryConsumer.POWER_COMPONENT_COUNT]; - final int customComponentCount = - requestedBatteryConsumer.getCustomPowerComponentCount(); + final int customComponentCount = requestedBatteryConsumer.getCustomPowerComponentCount(); double[] totalCustomPowerByComponentMah = new double[customComponentCount]; computeTotalPower(batteryUsageStats, totalPowerByComponentMah); - computeTotalPower(modeledBatteryUsageStats, totalModeledPowerByComponentMah); computeTotalPowerForCustomComponent(batteryUsageStats, totalCustomPowerByComponentMah); computeTotalDuration(batteryUsageStats, totalDurationByComponentMs); - if (isPowerProfileModelsOnly(requestedBatteryConsumer)) { - addEntry("Consumed", EntryType.UID_TOTAL_POWER, - requestedBatteryConsumer.getConsumedPower(), - batteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) - .getConsumedPower()); - } else { - addEntry("Consumed (PowerStats)", EntryType.UID_TOTAL_POWER, - requestedBatteryConsumer.getConsumedPower(), - batteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) - .getConsumedPower()); - addEntry("Consumed (PowerProfile)", EntryType.UID_TOTAL_POWER, - requestedModeledBatteryConsumer.getConsumedPower(), - modeledBatteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) - .getConsumedPower()); + Entry totalsEntry = addEntry("Consumed", EntryType.UID, + requestedBatteryConsumer.getConsumedPower(), + batteryUsageStats.getAggregateBatteryConsumer( + BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) + .getConsumedPower()); + addSlices(totalsEntry, requestedBatteryConsumer, BatteryConsumer.POWER_COMPONENT_BASE); + for (Slice slice : totalsEntry.slices) { + slice.powerMah = requestedBatteryConsumer.getConsumedPower( + new BatteryConsumer.Dimensions(BatteryConsumer.POWER_COMPONENT_ANY, + slice.processState, slice.screenState, slice.powerState)); } for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { + if (component == BatteryConsumer.POWER_COMPONENT_BASE) { + continue; + } final String metricTitle = getPowerMetricTitle(component); - final int powerModel = requestedBatteryConsumer.getPowerModel(component); - if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE - || powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) { - addEntry(metricTitle, EntryType.UID_POWER_PROFILE, - requestedBatteryConsumer.getConsumedPower(component), + double consumedPower = requestedBatteryConsumer.getConsumedPower(component); + if (consumedPower != 0) { + Entry entry = addEntry(metricTitle, EntryType.UID, consumedPower, totalPowerByComponentMah[component]); - addProcessStateEntries(metricTitle, EntryType.UID_POWER_PROFILE_PROCESS_STATE, - requestedBatteryConsumer, component); - } else { - addEntry(metricTitle + " (PowerStats)", EntryType.UID_POWER_ENERGY_CONSUMPTION, - requestedBatteryConsumer.getConsumedPower(component), - totalPowerByComponentMah[component]); - addProcessStateEntries(metricTitle, EntryType.UID_POWER_ENERGY_PROCESS_STATE, - requestedBatteryConsumer, component); - addEntry(metricTitle + " (PowerProfile)", EntryType.UID_POWER_PROFILE, - requestedModeledBatteryConsumer.getConsumedPower(component), - totalModeledPowerByComponentMah[component]); - addProcessStateEntries(metricTitle, EntryType.UID_POWER_PROFILE_PROCESS_STATE, - requestedModeledBatteryConsumer, component); + addSlices(entry, requestedBatteryConsumer, component); } } for (int component = 0; component < customComponentCount; component++) { - final String name = requestedBatteryConsumer.getCustomPowerComponentName( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component); - addEntry(name + " (PowerStats)", EntryType.UID_POWER_CUSTOM, - requestedBatteryConsumer.getConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component), - totalCustomPowerByComponentMah[component] - ); - } - - for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { - final String metricTitle = getTimeMetricTitle(component); - addEntry(metricTitle, EntryType.UID_DURATION, - requestedBatteryConsumer.getUsageDurationMillis(component), - totalDurationByComponentMs[component] - ); + int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component; + final String name = requestedBatteryConsumer.getCustomPowerComponentName(componentId); + double consumedPower = requestedBatteryConsumer.getConsumedPower(componentId); + if (consumedPower != 0) { + Entry entry = addEntry(name, EntryType.UID, consumedPower, + totalCustomPowerByComponentMah[component]); + addSlices(entry, requestedBatteryConsumer, componentId); + } } mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(batteryUsageStats, batteryConsumerId, context.getPackageManager()); } - private void addProcessStateEntries(String metricTitle, EntryType entryType, - BatteryConsumer batteryConsumer, int component) { + private void addSlices(Entry entry, BatteryConsumer batteryConsumer, int component) { final BatteryConsumer.Key[] keys = batteryConsumer.getKeys(component); if (keys == null || keys.length <= 1) { return; } + boolean hasProcStateData = false; for (BatteryConsumer.Key key : keys) { - String label; - switch (key.processState) { - case BatteryConsumer.PROCESS_STATE_FOREGROUND: - label = "foreground"; - break; - case BatteryConsumer.PROCESS_STATE_BACKGROUND: - label = "background"; - break; - case BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE: - label = "FGS"; - break; - case BatteryConsumer.PROCESS_STATE_CACHED: - label = "cached"; - break; - default: - continue; + if (key.processState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) { + hasProcStateData = true; + break; } - addEntry(metricTitle + " \u2022 " + label, entryType, - batteryConsumer.getConsumedPower(key), 0); } + + ArrayList<Slice> slices = new ArrayList<>(); + for (BatteryConsumer.Key key : keys) { + if (hasProcStateData && key.processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) { + continue; + } + + double powerMah = batteryConsumer.getConsumedPower(key); + long durationMs = batteryConsumer.getUsageDurationMillis(key); + + if (powerMah == 0 && durationMs == 0) { + continue; + } + + Slice slice = new Slice(); + slice.powerState = key.powerState; + slice.screenState = key.screenState; + slice.processState = key.processState; + slice.powerMah = powerMah; + slice.durationMs = durationMs; + + slices.add(slice); + } + entry.slices = slices; } private void populateForAggregateBatteryConsumer(Context context, - List<BatteryUsageStats> batteryUsageStatsList) { - BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0); - BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1); - + BatteryUsageStats batteryUsageStats) { final BatteryConsumer deviceBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer( BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); @@ -211,46 +186,18 @@ public class BatteryConsumerData { batteryUsageStats.getAggregateBatteryConsumer( BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS); - BatteryConsumer modeledDeviceBatteryConsumer = - modeledBatteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); - BatteryConsumer modeledAppsBatteryConsumer = - modeledBatteryUsageStats.getAggregateBatteryConsumer( - BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS); - - if (isPowerProfileModelsOnly(deviceBatteryConsumer)) { - addEntry("Consumed", EntryType.DEVICE_TOTAL_POWER, - deviceBatteryConsumer.getConsumedPower(), - appsBatteryConsumer.getConsumedPower()); - } else { - addEntry("Consumed (PowerStats)", EntryType.DEVICE_TOTAL_POWER, - deviceBatteryConsumer.getConsumedPower(), - appsBatteryConsumer.getConsumedPower()); - addEntry("Consumed (PowerProfile)", EntryType.DEVICE_TOTAL_POWER, - modeledDeviceBatteryConsumer.getConsumedPower(), - modeledAppsBatteryConsumer.getConsumedPower()); - } + addEntry("Consumed", EntryType.DEVICE_TOTAL_POWER, + deviceBatteryConsumer.getConsumedPower(), + appsBatteryConsumer.getConsumedPower()); mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(batteryUsageStats, AGGREGATE_BATTERY_CONSUMER_ID, context.getPackageManager()); - for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { final String metricTitle = getPowerMetricTitle(component); - final int powerModel = deviceBatteryConsumer.getPowerModel(component); - if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE - || powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) { - addEntry(metricTitle, EntryType.DEVICE_POWER_MODELED, - deviceBatteryConsumer.getConsumedPower(component), - appsBatteryConsumer.getConsumedPower(component)); - } else { - addEntry(metricTitle + " (PowerStats)", EntryType.DEVICE_POWER_ENERGY_CONSUMPTION, - deviceBatteryConsumer.getConsumedPower(component), - appsBatteryConsumer.getConsumedPower(component)); - addEntry(metricTitle + " (PowerProfile)", EntryType.DEVICE_POWER_MODELED, - modeledDeviceBatteryConsumer.getConsumedPower(component), - modeledAppsBatteryConsumer.getConsumedPower(component)); - } + addEntry(metricTitle, EntryType.DEVICE_POWER, + deviceBatteryConsumer.getConsumedPower(component), + appsBatteryConsumer.getConsumedPower(component)); } final int customComponentCount = @@ -258,10 +205,10 @@ public class BatteryConsumerData { for (int component = 0; component < customComponentCount; component++) { final String name = deviceBatteryConsumer.getCustomPowerComponentName( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component); - addEntry(name + " (PowerStats)", EntryType.DEVICE_POWER_CUSTOM, - deviceBatteryConsumer.getConsumedPowerForCustomComponent( + addEntry(name, EntryType.DEVICE_POWER_CUSTOM, + deviceBatteryConsumer.getConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component), - appsBatteryConsumer.getConsumedPowerForCustomComponent( + appsBatteryConsumer.getConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component)); } @@ -272,17 +219,6 @@ public class BatteryConsumerData { } } - private boolean isPowerProfileModelsOnly(BatteryConsumer batteryConsumer) { - for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { - final int powerModel = batteryConsumer.getPowerModel(component); - if (powerModel != BatteryConsumer.POWER_MODEL_POWER_PROFILE - && powerModel != BatteryConsumer.POWER_MODEL_UNDEFINED) { - return false; - } - } - return true; - } - private BatteryConsumer getRequestedBatteryConsumer(BatteryUsageStats batteryUsageStats, String batteryConsumerId) { for (UidBatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) { @@ -352,13 +288,14 @@ public class BatteryConsumerData { } } - private void addEntry(String title, EntryType entryType, double value1, double value2) { + private Entry addEntry(String title, EntryType entryType, double value1, double value2) { Entry entry = new Entry(); entry.title = title; entry.entryType = entryType; entry.value1 = value1; entry.value2 = value2; mEntries.add(entry); + return entry; } public BatteryConsumerInfoHelper.BatteryConsumerInfo getBatteryConsumerInfo() { diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java index c6d71c3f573a..37d6b17a665c 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java @@ -24,6 +24,8 @@ import android.os.UidBatteryConsumer; import androidx.annotation.NonNull; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.List; class BatteryConsumerInfoHelper { @@ -76,6 +78,8 @@ class BatteryConsumerInfoHelper { String packageWithHighestDrain = uidBatteryConsumer.getPackageWithHighestDrain(); if (uid == Process.ROOT_UID) { info.label = "<root>"; + } else if (uid < Process.FIRST_APPLICATION_UID) { + info.label = makeSystemUidLabel(uid); } else { String[] packages = packageManager.getPackagesForUid(uid); String primaryPackageName = null; @@ -134,6 +138,23 @@ class BatteryConsumerInfoHelper { return info; } + private static CharSequence makeSystemUidLabel(int uid) { + for (Field field : Process.class.getDeclaredFields()) { + final int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) + && field.getType().equals(int.class) && field.getName().endsWith("_UID")) { + try { + if (uid == field.getInt(null)) { + String label = field.getName(); + return label.substring(0, label.lastIndexOf("_UID")); + } + } catch (IllegalAccessException ignored) { + } + } + } + return null; + } + private static BatteryConsumerInfo makeAggregateBatteryConsumerInfo( BatteryUsageStats batteryUsageStats) { BatteryConsumerInfo info = new BatteryConsumerInfo(); diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java index 4469168a77b4..3699690aca59 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerActivity.java @@ -30,8 +30,8 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import androidx.activity.ComponentActivity; import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; import androidx.recyclerview.widget.LinearLayoutManager; @@ -50,7 +50,7 @@ import java.util.Locale; * Picker, showing a sorted lists of applications and other types of entities consuming power. * Opens BatteryStatsViewerActivity upon item selection. */ -public class BatteryConsumerPickerActivity extends ComponentActivity { +public class BatteryConsumerPickerActivity extends AppCompatActivity { private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId"; private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000; private static final String FORCE_FRESH_STATS = "force_fresh_stats"; @@ -68,6 +68,7 @@ public class BatteryConsumerPickerActivity extends ComponentActivity { super.onCreate(icicle); setContentView(R.layout.battery_consumer_picker_layout); + setSupportActionBar(findViewById(R.id.toolbar)); mSwipeRefreshLayout = findViewById(R.id.swipe_refresh); mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_green_light); diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java index e165c49ff55d..350213161b7b 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java @@ -17,14 +17,18 @@ package com.android.frameworks.core.batterystatsviewer; import android.content.Context; +import android.os.BatteryConsumer; import android.os.BatteryStatsManager; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.Bundle; +import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.TableLayout; +import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; @@ -40,6 +44,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import com.android.settingslib.utils.AsyncLoaderCompat; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -63,7 +68,16 @@ public class BatteryStatsViewerActivity extends ComponentActivity { private SwipeRefreshLayout mSwipeRefreshLayout; private View mCardView; private View mEmptyView; - private List<BatteryUsageStats> mBatteryUsageStats; + private BatteryUsageStats mBatteryUsageStats; + + private static SparseArray<String> sProcStateNames = new SparseArray<>(); + static { + sProcStateNames.put(BatteryConsumer.PROCESS_STATE_UNSPECIFIED, "-"); + sProcStateNames.put(BatteryConsumer.PROCESS_STATE_FOREGROUND, "FG"); + sProcStateNames.put(BatteryConsumer.PROCESS_STATE_BACKGROUND, "BG"); + sProcStateNames.put(BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, "FGS"); + sProcStateNames.put(BatteryConsumer.PROCESS_STATE_CACHED, "Cached"); + } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -122,7 +136,7 @@ public class BatteryStatsViewerActivity extends ComponentActivity { } private static class BatteryUsageStatsLoader extends - AsyncLoaderCompat<List<BatteryUsageStats>> { + AsyncLoaderCompat<BatteryUsageStats> { private final BatteryStatsManager mBatteryStatsManager; private final boolean mForceFreshStats; @@ -133,51 +147,44 @@ public class BatteryStatsViewerActivity extends ComponentActivity { } @Override - public List<BatteryUsageStats> loadInBackground() { + public BatteryUsageStats loadInBackground() { final int maxStatsAgeMs = mForceFreshStats ? 0 : BATTERY_STATS_REFRESH_RATE_MILLIS; final BatteryUsageStatsQuery queryDefault = new BatteryUsageStatsQuery.Builder() - .includePowerModels() .includeProcessStateData() + .includeScreenStateData() + .includePowerStateData() .setMaxStatsAgeMs(maxStatsAgeMs) .build(); - final BatteryUsageStatsQuery queryPowerProfileModeledOnly = - new BatteryUsageStatsQuery.Builder() - .powerProfileModeledOnly() - .includePowerModels() - .includeProcessStateData() - .setMaxStatsAgeMs(maxStatsAgeMs) - .build(); - return mBatteryStatsManager.getBatteryUsageStats( - List.of(queryDefault, queryPowerProfileModeledOnly)); + return mBatteryStatsManager.getBatteryUsageStats(queryDefault); } @Override - protected void onDiscardResult(List<BatteryUsageStats> result) { + protected void onDiscardResult(BatteryUsageStats result) { } } private class BatteryUsageStatsLoaderCallbacks - implements LoaderCallbacks<List<BatteryUsageStats>> { + implements LoaderCallbacks<BatteryUsageStats> { @NonNull @Override - public Loader<List<BatteryUsageStats>> onCreateLoader(int id, Bundle args) { + public Loader<BatteryUsageStats> onCreateLoader(int id, Bundle args) { return new BatteryUsageStatsLoader(BatteryStatsViewerActivity.this, args.getBoolean(FORCE_FRESH_STATS)); } @Override - public void onLoadFinished(@NonNull Loader<List<BatteryUsageStats>> loader, - List<BatteryUsageStats> batteryUsageStats) { + public void onLoadFinished(@NonNull Loader<BatteryUsageStats> loader, + BatteryUsageStats batteryUsageStats) { onBatteryUsageStatsLoaded(batteryUsageStats); } @Override - public void onLoaderReset(@NonNull Loader<List<BatteryUsageStats>> loader) { + public void onLoaderReset(@NonNull Loader<BatteryUsageStats> loader) { } } - private void onBatteryUsageStatsLoaded(List<BatteryUsageStats> batteryUsageStats) { + private void onBatteryUsageStatsLoaded(BatteryUsageStats batteryUsageStats) { mBatteryUsageStats = batteryUsageStats; onBatteryStatsDataLoaded(); } @@ -238,10 +245,21 @@ public class BatteryStatsViewerActivity extends ComponentActivity { private static class BatteryStatsDataAdapter extends RecyclerView.Adapter<BatteryStatsDataAdapter.ViewHolder> { public static class ViewHolder extends RecyclerView.ViewHolder { + public static class SliceViewHolder { + public TableRow tableRow; + public int procState; + public int powerState; + public int screenState; + public TextView powerTextView; + public TextView durationTextView; + } + public ImageView iconImageView; public TextView titleTextView; public TextView value1TextView; public TextView value2TextView; + public TableLayout table; + public List<SliceViewHolder> slices = new ArrayList<>(); ViewHolder(View itemView) { super(itemView); @@ -250,6 +268,40 @@ public class BatteryStatsViewerActivity extends ComponentActivity { titleTextView = itemView.findViewById(R.id.title); value1TextView = itemView.findViewById(R.id.value1); value2TextView = itemView.findViewById(R.id.value2); + table = itemView.findViewById(R.id.table); + + for (int i = 0; i < sProcStateNames.size(); i++) { + int procState = sProcStateNames.keyAt(i); + slices.add(createSliceViewHolder(procState, + BatteryConsumer.POWER_STATE_BATTERY, + BatteryConsumer.SCREEN_STATE_ON, + R.id.power_b_on, R.id.duration_b_on)); + slices.add(createSliceViewHolder(procState, + BatteryConsumer.POWER_STATE_BATTERY, + BatteryConsumer.SCREEN_STATE_OTHER, + R.id.power_b_off, R.id.duration_b_off)); + slices.add(createSliceViewHolder(procState, + BatteryConsumer.POWER_STATE_OTHER, + BatteryConsumer.SCREEN_STATE_ON, + R.id.power_c_on, R.id.duration_c_on)); + slices.add(createSliceViewHolder(procState, + BatteryConsumer.POWER_STATE_OTHER, + BatteryConsumer.SCREEN_STATE_OTHER, + R.id.power_c_off, R.id.duration_c_off)); + } + } + + private SliceViewHolder createSliceViewHolder(int procState, int powerState, + int screenState, int powerTextViewResId, int durationTextViewResId) { + TableRow powerRow = table.findViewWithTag("procstate" + procState); + SliceViewHolder svh = new SliceViewHolder(); + svh.tableRow = powerRow; + svh.procState = procState; + svh.powerState = powerState; + svh.screenState = screenState; + svh.powerTextView = powerRow.findViewById(powerTextViewResId); + svh.durationTextView = powerRow.findViewById(durationTextViewResId); + return svh; } } @@ -269,62 +321,32 @@ public class BatteryStatsViewerActivity extends ComponentActivity { @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) { LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); - View itemView = layoutInflater.inflate(R.layout.battery_consumer_entry_layout, parent, - false); + ViewGroup itemView = (ViewGroup) layoutInflater.inflate( + R.layout.battery_consumer_entry_layout, parent, false); + TableLayout table = itemView.findViewById(R.id.table); + int offset = 1; // Skip header + for (int i = 0; i < sProcStateNames.size(); i++) { + View powerRow = layoutInflater.inflate(R.layout.battery_consumer_slices_layout, + itemView, false); + ((TextView) powerRow.findViewById(R.id.procState)) + .setText(sProcStateNames.valueAt(i)); + powerRow.setTag("procstate" + sProcStateNames.keyAt(i)); + table.addView(powerRow, offset++); + } + return new ViewHolder(itemView); } @Override public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) { BatteryConsumerData.Entry entry = mEntries.get(position); - switch (entry.entryType) { - case UID_TOTAL_POWER: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_sum_24, 0); - setPowerText(viewHolder.value1TextView, entry.value1); - setProportionText(viewHolder.value2TextView, entry); - break; - case UID_POWER_PROFILE: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_calculate_24, - R.color.battery_consumer_bg_power_profile); - setPowerText(viewHolder.value1TextView, entry.value1); - setProportionText(viewHolder.value2TextView, entry); - break; - case UID_POWER_PROFILE_PROCESS_STATE: - setTitleIconAndBackground(viewHolder, " " + entry.title, - R.drawable.gm_calculate_24, - R.color.battery_consumer_bg_power_profile); - setPowerText(viewHolder.value1TextView, entry.value1); - viewHolder.value2TextView.setVisibility(View.INVISIBLE); - break; - case UID_POWER_ENERGY_CONSUMPTION: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_energy_24, - R.color.battery_consumer_bg_energy_consumption); - setPowerText(viewHolder.value1TextView, entry.value1); - setProportionText(viewHolder.value2TextView, entry); - break; - case UID_POWER_ENERGY_PROCESS_STATE: - setTitleIconAndBackground(viewHolder, " " + entry.title, - R.drawable.gm_energy_24, - R.color.battery_consumer_bg_energy_consumption); - setPowerText(viewHolder.value1TextView, entry.value1); - viewHolder.value2TextView.setVisibility(View.INVISIBLE); - break; - case UID_POWER_CUSTOM: + case UID: setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_energy_24, - R.color.battery_consumer_bg_energy_consumption); + R.drawable.gm_energy_24, 0); setPowerText(viewHolder.value1TextView, entry.value1); setProportionText(viewHolder.value2TextView, entry); - break; - case UID_DURATION: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_timer_24, 0); - setDurationText(viewHolder.value1TextView, (long) entry.value1); - setProportionText(viewHolder.value2TextView, entry); + bindSlices(viewHolder, entry); break; case DEVICE_TOTAL_POWER: setTitleIconAndBackground(viewHolder, entry.title, @@ -332,27 +354,13 @@ public class BatteryStatsViewerActivity extends ComponentActivity { setPowerText(viewHolder.value1TextView, entry.value1); setPowerText(viewHolder.value2TextView, entry.value2); break; - case DEVICE_POWER_MODELED: + case DEVICE_POWER: setTitleIconAndBackground(viewHolder, entry.title, R.drawable.gm_calculate_24, R.color.battery_consumer_bg_power_profile); setPowerText(viewHolder.value1TextView, entry.value1); setPowerText(viewHolder.value2TextView, entry.value2); break; - case DEVICE_POWER_ENERGY_CONSUMPTION: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_energy_24, - R.color.battery_consumer_bg_energy_consumption); - setPowerText(viewHolder.value1TextView, entry.value1); - setPowerText(viewHolder.value2TextView, entry.value2); - break; - case DEVICE_POWER_CUSTOM: - setTitleIconAndBackground(viewHolder, entry.title, - R.drawable.gm_energy_24, - R.color.battery_consumer_bg_energy_consumption); - setPowerText(viewHolder.value1TextView, entry.value1); - setPowerText(viewHolder.value2TextView, entry.value2); - break; case DEVICE_DURATION: setTitleIconAndBackground(viewHolder, entry.title, R.drawable.gm_timer_24, 0); @@ -362,6 +370,65 @@ public class BatteryStatsViewerActivity extends ComponentActivity { } } + private void bindSlices(ViewHolder viewHolder, BatteryConsumerData.Entry entry) { + if (entry.slices == null || entry.slices.isEmpty()) { + viewHolder.table.setVisibility(View.GONE); + return; + } + viewHolder.table.setVisibility(View.VISIBLE); + + boolean[] procStateRowPopulated = + new boolean[BatteryConsumer.PROCESS_STATE_COUNT]; + for (BatteryConsumerData.Slice s : entry.slices) { + if (s.powerMah != 0 || s.durationMs != 0) { + procStateRowPopulated[s.processState] = true; + } + } + + for (ViewHolder.SliceViewHolder sliceViewHolder : viewHolder.slices) { + BatteryConsumerData.Slice slice = null; + for (BatteryConsumerData.Slice s : entry.slices) { + if (s.powerState == sliceViewHolder.powerState + && s.screenState == sliceViewHolder.screenState + && s.processState == sliceViewHolder.procState) { + slice = s; + break; + } + } + if (!procStateRowPopulated[sliceViewHolder.procState]) { + sliceViewHolder.tableRow.setVisibility(View.GONE); + } else { + sliceViewHolder.tableRow.setVisibility(View.VISIBLE); + + if (slice != null && (slice.powerMah != 0 || slice.durationMs != 0)) { + sliceViewHolder.powerTextView.setText( + String.format(Locale.getDefault(), "%.1f", slice.powerMah)); + } else { + sliceViewHolder.powerTextView.setText(null); + } + + if (slice != null && slice.durationMs != 0) { + sliceViewHolder.durationTextView.setVisibility(View.VISIBLE); + String timeString; + if (slice.durationMs < MILLIS_IN_MINUTE) { + timeString = String.format(Locale.getDefault(), "%ds", + slice.durationMs / 1000); + } else if (slice.durationMs < 60 * MILLIS_IN_MINUTE) { + timeString = String.format(Locale.getDefault(), "%dm %ds", + slice.durationMs / MILLIS_IN_MINUTE, + (slice.durationMs % MILLIS_IN_MINUTE) / 1000); + } else { + timeString = String.format(Locale.getDefault(), "%dm", + slice.durationMs / MILLIS_IN_MINUTE); + } + sliceViewHolder.durationTextView.setText(timeString); + } else { + sliceViewHolder.durationTextView.setVisibility(View.GONE); + } + } + } + } + private void setTitleIconAndBackground(ViewHolder viewHolder, String title, int icon, int background) { viewHolder.titleTextView.setText(title); 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 index b01648838695..412169e7a905 100644 --- 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 @@ -42,5 +42,6 @@ public class TrampolineActivity extends Activity { private void launchMainActivity() { startActivity(new Intent(this, BatteryConsumerPickerActivity.class)); + finish(); } } diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index f39508d6de15..49425572b256 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -152,6 +152,7 @@ android_test { ":HelloWorldUsingSdkMalformedNegativeVersion", ":CtsStaticSharedLibConsumerApp1", ":CtsStaticSharedLibConsumerApp3", + ":CtsStaticSharedLibProviderApp1", ], } diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml index 5d8ff87eca24..bada7512a8e9 100644 --- a/core/tests/coretests/AndroidTest.xml +++ b/core/tests/coretests/AndroidTest.xml @@ -43,6 +43,8 @@ value="/data/local/tmp/tests/coretests/pm/CtsStaticSharedLibConsumerApp1.apk"/> <option name="push-file" key="CtsStaticSharedLibConsumerApp3.apk" value="/data/local/tmp/tests/coretests/pm/CtsStaticSharedLibConsumerApp3.apk"/> + <option name="push-file" key="CtsStaticSharedLibProviderApp1.apk" + value="/data/local/tmp/tests/coretests/pm/CtsStaticSharedLibProviderApp1.apk"/> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index 23a09857032c..63e678d9ee53 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -467,7 +467,6 @@ public class NotificationTest { .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG")) .setColor(Color.WHITE) .setColorized(true) - .setFlag(FLAG_CAN_COLORIZE, true) .build(); assertThat(n.hasPromotableCharacteristics()).isTrue(); } @@ -481,7 +480,6 @@ public class NotificationTest { .setContentTitle("TITLE") .setColor(Color.WHITE) .setColorized(true) - .setFlag(FLAG_CAN_COLORIZE, true) .build(); assertThat(n.hasPromotableCharacteristics()).isFalse(); } @@ -505,7 +503,20 @@ public class NotificationTest { .setStyle(new Notification.BigTextStyle()) .setColor(Color.WHITE) .setColorized(true) - .setFlag(FLAG_CAN_COLORIZE, true) + .build(); + assertThat(n.hasPromotableCharacteristics()).isFalse(); + } + + @Test + @EnableFlags(Flags.FLAG_UI_RICH_ONGOING) + public void testHasPromotableCharacteristics_groupSummary() { + Notification n = new Notification.Builder(mContext, "test") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG")) + .setColor(Color.WHITE) + .setColorized(true) + .setGroup("someGroup") + .setGroupSummary(true) .build(); assertThat(n.hasPromotableCharacteristics()).isFalse(); } diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java index 2fc72e1d3994..177c7f0b2f27 100644 --- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java +++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java @@ -26,6 +26,7 @@ import static android.app.PropertyInvalidatedCache.NonceStore.INVALID_NONCE_INDE import static com.android.internal.os.Flags.FLAG_APPLICATION_SHARED_MEMORY_ENABLED; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; @@ -34,6 +35,8 @@ import static org.junit.Assert.fail; import android.annotation.SuppressLint; import android.app.PropertyInvalidatedCache.Args; +import android.app.PropertyInvalidatedCache.NonceWatcher; +import android.app.PropertyInvalidatedCache.NonceStore; import android.os.Binder; import com.android.internal.os.ApplicationSharedMemory; @@ -45,11 +48,15 @@ import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; +import com.android.internal.os.ApplicationSharedMemory; + import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import java.util.concurrent.TimeUnit; + /** * Test for verifying the behavior of {@link PropertyInvalidatedCache}. This test does * not use any actual binder calls - it is entirely self-contained. This test also relies @@ -490,6 +497,62 @@ public class PropertyInvalidatedCacheTests { } } + // Verify that NonceWatcher change reporting works properly + @Test + public void testNonceWatcherChanged() { + // Create a cache that will write a system nonce. + TestCache sysCache = new TestCache(MODULE_SYSTEM, "watcher1"); + sysCache.testPropertyName(); + + try (NonceWatcher watcher1 = sysCache.getNonceWatcher()) { + + // The property has never been invalidated so it is still unset. + assertFalse(watcher1.isChanged()); + + // Invalidate the cache. The first call to isChanged will return true but the second + // call will return false; + sysCache.invalidateCache(); + assertTrue(watcher1.isChanged()); + assertFalse(watcher1.isChanged()); + + // Invalidate the cache. The first call to isChanged will return true but the second + // call will return false; + sysCache.invalidateCache(); + sysCache.invalidateCache(); + assertTrue(watcher1.isChanged()); + assertFalse(watcher1.isChanged()); + + NonceWatcher watcher2 = sysCache.getNonceWatcher(); + // This watcher return isChanged() immediately because the nonce is not UNSET. + assertTrue(watcher2.isChanged()); + } + } + + // Verify that NonceWatcher wait-for-change works properly + @Test + public void testNonceWatcherWait() throws Exception { + // Create a cache that will write a system nonce. + TestCache sysCache = new TestCache(MODULE_TEST, "watcher1"); + + // Use the watcher outside a try-with-resources block. + NonceWatcher watcher1 = sysCache.getNonceWatcher(); + + // Invalidate the cache and then "wait". + sysCache.invalidateCache(); + assertEquals(watcher1.waitForChange(), 1); + + // Invalidate the cache three times and then "wait". + sysCache.invalidateCache(); + sysCache.invalidateCache(); + sysCache.invalidateCache(); + assertEquals(watcher1.waitForChange(), 3); + + // Wait for a change. It won't happen, but the code will time out after 10ms. + assertEquals(watcher1.waitForChange(10, TimeUnit.MILLISECONDS), 0); + + watcher1.close(); + } + // Verify the behavior of shared memory nonce storage. This does not directly test the cache // storing nonces in shared memory. @RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED) @@ -502,10 +565,8 @@ public class PropertyInvalidatedCacheTests { // Create a server-side store and a client-side store. The server's store is mutable and // the client's store is not mutable. - PropertyInvalidatedCache.NonceStore server = - new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), true); - PropertyInvalidatedCache.NonceStore client = - new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), false); + NonceStore server = new NonceStore(shmem.getSystemNonceBlock(), true); + NonceStore client = new NonceStore(shmem.getSystemNonceBlock(), false); final String name1 = "name1"; assertEquals(server.getHandleForName(name1), INVALID_NONCE_INDEX); diff --git a/core/tests/coretests/src/android/appwidget/AppWidgetEventsTest.kt b/core/tests/coretests/src/android/appwidget/AppWidgetEventsTest.kt new file mode 100644 index 000000000000..ea1158c88055 --- /dev/null +++ b/core/tests/coretests/src/android/appwidget/AppWidgetEventsTest.kt @@ -0,0 +1,51 @@ +/* + * 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.appwidget + +import android.graphics.Rect +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class AppWidgetEventsTest { + @Test + fun createWidgetInteractionEvent() { + val appWidgetId = 1 + val durationMs = 1000L + val position = Rect(1, 2, 3, 4) + val clicked = intArrayOf(1, 2, 3) + val scrolled = intArrayOf(4, 5, 6) + val bundle = AppWidgetManager.createWidgetInteractionEvent( + appWidgetId, + durationMs, + position, + clicked, + scrolled + ) + + assertThat(bundle.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID)).isEqualTo(appWidgetId) + assertThat(bundle.getLong(AppWidgetManager.EXTRA_EVENT_DURATION_MS)).isEqualTo(durationMs) + assertThat(bundle.getIntArray(AppWidgetManager.EXTRA_EVENT_POSITION_RECT)) + .asList().containsExactly(position.left, position.top, position.right, position.bottom) + assertThat(bundle.getIntArray(AppWidgetManager.EXTRA_EVENT_CLICKED_VIEWS)) + .asList().containsExactly(clicked[0], clicked[1], clicked[2]) + assertThat(bundle.getIntArray(AppWidgetManager.EXTRA_EVENT_SCROLLED_VIEWS)) + .asList().containsExactly(scrolled[0], scrolled[1], scrolled[2]) + } +} diff --git a/core/tests/coretests/src/android/appwidget/OWNERS b/core/tests/coretests/src/android/appwidget/OWNERS new file mode 100644 index 000000000000..d724cac4aa3e --- /dev/null +++ b/core/tests/coretests/src/android/appwidget/OWNERS @@ -0,0 +1 @@ +include /core/java/android/appwidget/OWNERS diff --git a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java index ecacdb2bde0b..6d2dd5355ff0 100644 --- a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java +++ b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java @@ -73,6 +73,7 @@ public class ApkLiteParseUtilsTest { private static final String TEST_APP_USING_SDK1_AND_SDK1 = "HelloWorldUsingSdk1AndSdk1.apk"; private static final String TEST_APP_USING_SDK_MALFORMED_VERSION = "HelloWorldUsingSdkMalformedNegativeVersion.apk"; + private static final String TEST_STATIC_LIB_APP = "CtsStaticSharedLibProviderApp1.apk"; private static final String TEST_APP_USING_STATIC_LIB = "CtsStaticSharedLibConsumerApp1.apk"; private static final String TEST_APP_USING_STATIC_LIB_TWO_CERTS = "CtsStaticSharedLibConsumerApp3.apk"; @@ -207,6 +208,17 @@ public class ApkLiteParseUtilsTest { assertThat(liteCerts).isEqualTo(pkgCerts); } + @Test + public void testParseApkLite_isIsStaticLibrary() throws Exception { + File apkFile = copyApkToTmpDir(TEST_STATIC_LIB_APP); + ParseResult<ApkLite> result = ApkLiteParseUtils + .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0); + assertThat(result.isError()).isFalse(); + ApkLite baseApk = result.getResult(); + + assertThat(baseApk.isIsStaticLibrary()).isTrue(); + } + @SuppressLint("CheckResult") @Test public void testParseApkLite_malformedUsesSdkLibrary_duplicate() throws Exception { diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS index 4620cb8d8148..c45080fb5e26 100644 --- a/core/tests/coretests/src/android/os/OWNERS +++ b/core/tests/coretests/src/android/os/OWNERS @@ -15,3 +15,6 @@ per-file IpcDataCache* = file:/PERFORMANCE_OWNERS # RemoteCallbackList per-file RemoteCallbackListTest.java = shayba@google.com + +# MessageQueue +per-file MessageQueueTest.java = mfasheh@google.com, shayba@google.com diff --git a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java index 726ee85dddd5..915ace058121 100644 --- a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java +++ b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java @@ -16,9 +16,9 @@ package android.view; -import static androidx.test.InstrumentationRegistry.getTargetContext; +import static android.view.flags.Flags.FLAG_SCROLL_CAPTURE_TARGET_Z_ORDER_FIX; -import static com.google.common.truth.Truth.assertThat; +import static androidx.test.InstrumentationRegistry.getTargetContext; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -32,13 +32,16 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.CancellationSignal; import android.os.SystemClock; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.annotation.NonNull; 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; @@ -56,6 +59,8 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class ScrollCaptureSearchResultsTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final Rect EMPTY_RECT = new Rect(); private static final String TAG = "Test"; @@ -98,6 +103,45 @@ public class ScrollCaptureSearchResultsTest { assertNull("Expected null due to no valid targets", results.getTopResult()); } + /** + * A scrolling target should be excluded even when larger if it will be drawn over by another + * scrolling target. + */ + @EnableFlags(FLAG_SCROLL_CAPTURE_TARGET_Z_ORDER_FIX) + @Test + public void testCoveredTargetsAreExcluded() { + ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); + + FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mDirectExec); + callback1.setScrollBounds(new Rect(0, 0, 200, 200)); // 200 tall + View view1 = new FakeView(getTargetContext(), 0, 0, 200, 200, 1); + ScrollCaptureTarget target1 = createTargetWithView(view1, callback1, + new Rect(0, 0, 200, 200), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); + + FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec); + callback2.setScrollBounds(new Rect(0, 0, 200, 180)); // 180 tall + View view2 = new FakeView(getTargetContext(), 0, 20, 200, 200, 2); + ScrollCaptureTarget target2 = createTargetWithView(view2, callback2, + new Rect(0, 0, 200, 180), new Point(0, 20), View.SCROLL_CAPTURE_HINT_AUTO); + + // Top z-order but smaller, and non-intersecting. (positioned further Y than the first two) + FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mDirectExec); + callback3.setScrollBounds(new Rect(0, 0, 50, 50)); + View view3 = new FakeView(getTargetContext(), 75, 250, 125, 300, 3); + ScrollCaptureTarget target3 = createTargetWithView(view3, callback3, + new Rect(0, 0, 50, 50), new Point(75, 250), View.SCROLL_CAPTURE_HINT_AUTO); + + results.addTarget(target1); + results.addTarget(target2); + results.addTarget(target3); + + assertTrue(results.isComplete()); + ScrollCaptureTarget result = results.getTopResult(); + assertSame("Expected the second target because of higher z-Index", target2, result); + assertEquals("result has wrong scroll bounds", + new Rect(0, 0, 200, 180), result.getScrollBounds()); + } + @Test public void testSingleTarget() { ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec); @@ -152,29 +196,29 @@ public class ScrollCaptureSearchResultsTest { // 2 - 10x10 + HINT_INCLUDE FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec); - callback2.setScrollBounds(new Rect(0, 0, 10, 10)); - ViewGroup targetView2 = new FakeView(getTargetContext(), 0, 0, 60, 60, 2); + callback2.setScrollBounds(new Rect(25, 25, 35, 35)); // 10x10 + ViewGroup targetView2 = new FakeView(getTargetContext(), 0, 60, 60, 120, 2); ScrollCaptureTarget target2 = createTargetWithView(targetView2, callback2, new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_INCLUDE); // 3 - 20x20 + AUTO FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mDirectExec); callback3.setScrollBounds(new Rect(0, 0, 20, 20)); - ViewGroup targetView3 = new FakeView(getTargetContext(), 0, 0, 60, 60, 3); + ViewGroup targetView3 = new FakeView(getTargetContext(), 0, 120, 60, 180, 3); ScrollCaptureTarget target3 = createTargetWithView(targetView3, callback3, new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); // 4 - 30x30 + AUTO FakeScrollCaptureCallback callback4 = new FakeScrollCaptureCallback(mDirectExec); callback4.setScrollBounds(new Rect(0, 0, 10, 10)); - ViewGroup targetView4 = new FakeView(getTargetContext(), 0, 0, 60, 60, 4); + ViewGroup targetView4 = new FakeView(getTargetContext(), 0, 180, 60, 240, 4); ScrollCaptureTarget target4 = createTargetWithView(targetView4, callback4, new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); // 5 - 10x10 + child of #4 FakeScrollCaptureCallback callback5 = new FakeScrollCaptureCallback(mDirectExec); callback5.setScrollBounds(new Rect(0, 0, 10, 10)); - ViewGroup targetView5 = new FakeView(getTargetContext(), 0, 0, 60, 60, 5); + ViewGroup targetView5 = new FakeView(getTargetContext(), 0, 0, 60, 30, 5); ScrollCaptureTarget target5 = createTargetWithView(targetView5, callback5, new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); targetView4.addView(targetView5); @@ -182,7 +226,7 @@ public class ScrollCaptureSearchResultsTest { // 6 - 20x20 + child of #4 FakeScrollCaptureCallback callback6 = new FakeScrollCaptureCallback(mDirectExec); callback6.setScrollBounds(new Rect(0, 0, 20, 20)); - ViewGroup targetView6 = new FakeView(getTargetContext(), 0, 0, 60, 60, 6); + ViewGroup targetView6 = new FakeView(getTargetContext(), 0, 30, 30, 60, 6); ScrollCaptureTarget target6 = createTargetWithView(targetView6, callback6, new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO); targetView4.addView(targetView6); @@ -194,20 +238,10 @@ public class ScrollCaptureSearchResultsTest { results.addTarget(target4); results.addTarget(target5); results.addTarget(target6); - assertTrue(results.isComplete()); + assertTrue("results.isComplete()", results.isComplete()); // Verify "top" result - assertEquals(target2, results.getTopResult()); - - // Verify priority ("best" first) - assertThat(results.getTargets()) - .containsExactly( - target2, - target6, - target5, - target4, - target3, - target1); + assertEquals("top result", target2, results.getTopResult()); } /** @@ -291,27 +325,22 @@ public class ScrollCaptureSearchResultsTest { new Rect(1, 2, 3, 4), result.getScrollBounds()); } - private void setupTargetView(View view, Rect localVisibleRect, int scrollCaptureHint) { - view.setScrollCaptureHint(scrollCaptureHint); - view.onVisibilityAggregated(true); - // Treat any offset as padding, outset localVisibleRect on all sides and use this as - // child bounds - Rect bounds = new Rect(localVisibleRect); - bounds.inset(-bounds.left, -bounds.top, bounds.left, bounds.top); - view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom); - view.onVisibilityAggregated(true); - } - private ScrollCaptureTarget createTarget(ScrollCaptureCallback callback, Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) { - View mockView = new View(getTargetContext()); + Rect bounds = new Rect(localVisibleRect); + // Use localVisibleRect as position, treat left/top offset as padding + bounds.left = 0; + bounds.top = 0; + View mockView = new FakeView(getTargetContext(), bounds.left, bounds.top, bounds.right, + bounds.bottom, View.NO_ID); return createTargetWithView(mockView, callback, localVisibleRect, positionInWindow, scrollCaptureHint); } private ScrollCaptureTarget createTargetWithView(View view, ScrollCaptureCallback callback, Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) { - setupTargetView(view, localVisibleRect, scrollCaptureHint); + view.setScrollCaptureHint(scrollCaptureHint); + view.onVisibilityAggregated(true); return new ScrollCaptureTarget(view, localVisibleRect, positionInWindow, callback); } @@ -326,6 +355,32 @@ public class ScrollCaptureSearchResultsTest { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { } + + /** Ignores window attachment state. The standard impl always returns [0,0] if the view is + * not attached. This override allows testing without dealing with AttachInfo. + */ + @Override + public void getLocationInWindow(int[] outLocation) { + outLocation[0] = mLeft; + outLocation[1] = mTop; + ViewParent viewParent = getParent(); + while (viewParent instanceof View) { + final View view = (View) viewParent; + + outLocation[0] -= view.mScrollX; + outLocation[1] -= view.mScrollY; + + // Explicitly do not handle matrix/transforms, not needed for testing + if (!view.hasIdentityMatrix()) { + throw new IllegalStateException("This mock does not handle transforms!"); + } + + outLocation[0] += view.mLeft; + outLocation[1] += view.mTop; + + viewParent = view.mParent; + } + } } static class FakeScrollCaptureCallback implements ScrollCaptureCallback { diff --git a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java index 25608c328f95..adf7a720071a 100644 --- a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java +++ b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.flags.Flags.FLAG_SCROLL_CAPTURE_TARGET_Z_ORDER_FIX; + import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertEquals; @@ -30,16 +32,20 @@ import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.os.CancellationSignal; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.annotation.NonNull; import androidx.test.filters.MediumTest; import androidx.test.filters.SmallTest; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; +import java.util.List; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -51,6 +57,9 @@ import java.util.function.Consumer; @RunWith(MockitoJUnitRunner.class) public class ViewGroupScrollCaptureTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final Executor DIRECT_EXECUTOR = Runnable::run; /** Make sure the hint flags are saved and loaded correctly. */ @@ -239,6 +248,56 @@ public class ViewGroupScrollCaptureTest { assertNull(results.getTopResult()); } + @EnableFlags(FLAG_SCROLL_CAPTURE_TARGET_Z_ORDER_FIX) + @MediumTest + @Test + public void testDispatchScrollCaptureSearch_traversesInDrawingOrder() throws Exception { + final Context context = getInstrumentation().getContext(); + // Uses childDrawingOrder to reverse drawing order of children. + final MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200); + + // w=200, h=180, z=10, drawn on top + final MockView view1 = new MockView(context, 0, 20, 200, 200); + TestScrollCaptureCallback callback1 = new TestScrollCaptureCallback(); + view1.setScrollCaptureCallback(callback1); + view1.setZ(10f); + + // w=200, h=200, z=0, drawn first, under view1 + final MockView view2 = new MockView(context, 0, 0, 200, 200); + TestScrollCaptureCallback callback2 = new TestScrollCaptureCallback(); + view2.setScrollCaptureCallback(callback2); + + viewGroup.addView(view1); // test order is dependent on draw order by adding z=10 first + viewGroup.addView(view2); + + Rect localVisibleRect = new Rect(0, 0, 200, 200); + Point windowOffset = new Point(0, 0); + + // Where targets are added + final ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR); + + viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget); + callback1.completeSearchRequest(new Rect(0, 0, 200, 180)); + callback2.completeSearchRequest(new Rect(0, 0, 200, 200)); + assertTrue(results.isComplete()); + + List<ScrollCaptureTarget> targets = results.getTargets(); + List<View> targetViews = + targets.stream().map(ScrollCaptureTarget::getContainingView).toList(); + assertEquals(List.of(view2, view1), targetViews); + } + + static final class ReverseDrawingViewGroup extends MockViewGroup { + ReverseDrawingViewGroup(Context context, int left, int top, int right, int bottom) { + super(context, left, top, right, bottom, View.SCROLL_CAPTURE_HINT_AUTO); + } + + @Override + protected int getChildDrawingOrder(int childCount, int drawingPosition) { + return childCount == 0 ? 0 : childCount - (drawingPosition + 1); + } + } + /** * Test scroll capture search dispatch to child views. * <p> @@ -511,7 +570,7 @@ public class ViewGroupScrollCaptureTest { } }; - public static final class MockViewGroup extends ViewGroup { + public static class MockViewGroup extends ViewGroup { private ScrollCaptureCallback mInternalCallback; private Rect mOnScrollCaptureSearchLastLocalVisibleRect; private Point mOnScrollCaptureSearchLastWindowOffset; diff --git a/core/tests/coretests/src/android/widget/ProgressBarTest.java b/core/tests/coretests/src/android/widget/ProgressBarTest.java index fa6dd31923ac..0cbfaa97faef 100644 --- a/core/tests/coretests/src/android/widget/ProgressBarTest.java +++ b/core/tests/coretests/src/android/widget/ProgressBarTest.java @@ -23,8 +23,12 @@ import static org.junit.Assert.assertTrue; import android.app.Instrumentation; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.Flags; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -32,6 +36,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,6 +45,10 @@ import org.junit.runner.RunWith; @Presubmit public class ProgressBarTest { private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); + + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); private ProgressBar mBar; private AccessibilityNodeInfo mInfo; @@ -181,4 +190,16 @@ public class ProgressBarTest { mBar.onInitializeAccessibilityNodeInfo(mInfo); assertEquals("custom state", mInfo.getStateDescription().toString()); } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_INDETERMINATE_RANGE_INFO) + public void testRangeInfo_indeterminateProgressBar_usesTypeIndeterminate() { + mBar.setIndeterminate(true); + assertTrue(mBar.isIndeterminate()); + + mBar.onInitializeAccessibilityNodeInfo(mInfo); + + assertEquals(mInfo.getRangeInfo().getType(), + AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INDETERMINATE); + } } diff --git a/core/tests/coretests/src/android/window/WindowContextTest.java b/core/tests/coretests/src/android/window/WindowContextTest.java index f1fbd559b8f8..21930d17ed68 100644 --- a/core/tests/coretests/src/android/window/WindowContextTest.java +++ b/core/tests/coretests/src/android/window/WindowContextTest.java @@ -29,6 +29,11 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import android.app.Activity; import android.app.EmptyActivity; @@ -60,6 +65,8 @@ import androidx.test.rule.ActivityTestRule; import com.android.frameworks.coretests.R; +import org.junit.After; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -88,9 +95,21 @@ public class WindowContextTest { private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); private final WindowContext mWindowContext = createWindowContext(); private final IWindowManager mWms = WindowManagerGlobal.getWindowManagerService(); + private WindowTokenClientController mOriginalWindowTokenClientController; private static final int TIMEOUT_IN_SECONDS = 4; + @Before + public void setUp() { + // Keeping the original to set it back after each test, in case they applied any override. + mOriginalWindowTokenClientController = WindowTokenClientController.getInstance(); + } + + @After + public void tearDown() { + WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController); + } + @Test public void testCreateWindowContextWindowManagerAttachClientToken() { final WindowManager windowContextWm = WindowManagerImpl @@ -320,6 +339,20 @@ public class WindowContextTest { } } + @Test + public void updateDisplay_wasAttached_detachThenAttachedPropagatedToTokenController() { + final WindowTokenClientController mockWindowTokenClientController = + mock(WindowTokenClientController.class); + WindowTokenClientController.overrideForTesting(mockWindowTokenClientController); + + mWindowContext.updateDisplay(DEFAULT_DISPLAY + 1); + + verify(mockWindowTokenClientController).detachIfNeeded(any()); + verify(mockWindowTokenClientController).attachToDisplayArea(any(), + anyInt(), /* displayId= */ eq(DEFAULT_DISPLAY + 1), + any()); + } + private WindowContext createWindowContext() { return createWindowContext(TYPE_APPLICATION_OVERLAY); } diff --git a/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java b/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java index a3725af04ba6..bb2fe1bcfc64 100644 --- a/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java +++ b/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java @@ -162,6 +162,22 @@ public class WindowTokenClientControllerTest { } @Test + public void testAttachToDisplayContent_keepTrackWithoutWMS() { + // WMS is not initialized + doReturn(null).when(mController).getWindowManagerService(); + + assertFalse(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY)); + + // Can report config change + mController.onWindowContextInfoChanged(mWindowTokenClient, mWindowContextInfo); + + verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY); + + // No crash to detach even if WMS is not initialized. + mController.detachIfNeeded(mWindowTokenClient); + } + + @Test public void testAttachToWindowToken() throws RemoteException { doReturn(null).when(mWindowManagerService).attachWindowContextToWindowToken( any(), any(), any()); diff --git a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java index 1cc38ded1c2c..40710577b154 100644 --- a/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java +++ b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java @@ -16,6 +16,8 @@ package android.os.vibrator.persistence; +import static android.os.VibrationEffect.Composition.DELAY_TYPE_PAUSE; +import static android.os.VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET; import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK; import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK; import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN; @@ -31,6 +33,7 @@ import android.os.PersistableBundle; import android.os.VibrationEffect; import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; +import android.os.vibrator.PrimitiveSegment; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; @@ -437,7 +440,7 @@ public class VibrationEffectXmlSerializationTest { @Test @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS) - public void testVendorEffect_featureFlagEnabled_allSucceed() throws Exception { + public void testVendorEffect_allSucceed() throws Exception { PersistableBundle vendorData = new PersistableBundle(); vendorData.putInt("id", 1); vendorData.putDouble("scale", 0.5); @@ -476,7 +479,7 @@ public class VibrationEffectXmlSerializationTest { @Test @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS) - public void testInvalidVendorEffect_featureFlagEnabled_allFail() throws IOException { + public void testInvalidVendorEffect_allFail() throws IOException { String emptyTag = "<vibration-effect><vendor-effect/></vibration-effect>"; assertPublicApisParserFails(emptyTag); assertHiddenApisParserFails(emptyTag); @@ -526,6 +529,81 @@ public class VibrationEffectXmlSerializationTest { assertHiddenApisSerializerFails(vendorEffect); } + @Test + @EnableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) + public void testPrimitiveDelayType_allSucceed() throws Exception { + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET) + .addPrimitive(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE) + .compose(); + String xml = """ + <vibration-effect> + <primitive-effect name="tick" delayType="relative_start_offset"/> + <primitive-effect name="click" scale="0.123" delayMs="10"/> + </vibration-effect> + """; + + assertPublicApisParserSucceeds(xml, effect); + assertPublicApisSerializerSucceeds(effect, "tick", "click"); + // Delay type pause is not serialized, as it's the default one + assertPublicApisSerializerSucceeds(effect, "relative_start_offset", "click"); + assertPublicApisRoundTrip(effect); + + assertHiddenApisParserSucceeds(xml, effect); + assertHiddenApisSerializerSucceeds(effect, "tick", "click"); + assertHiddenApisRoundTrip(effect); + + // Check PersistableBundle from round-trip + VibrationEffect.Composed parsedEffect = ((VibrationEffect.Composed) parseVibrationEffect( + serialize(effect), /* flags= */ 0)); + assertThat(parsedEffect.getRepeatIndex()).isEqualTo(-1); + assertThat(parsedEffect.getSegments()).containsExactly( + new PrimitiveSegment(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET), + new PrimitiveSegment(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE)) + .inOrder(); + } + + @Test + @EnableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) + public void testPrimitiveInvalidDelayType_allFail() { + String emptyAttribute = """ + <vibration-effect> + <primitive-effect name="tick" delayType=""/> + </vibration-effect> + """; + assertPublicApisParserFails(emptyAttribute); + assertHiddenApisParserFails(emptyAttribute); + + String invalidString = """ + <vibration-effect> + <primitive-effect name="tick" delayType="invalid"/> + </vibration-effect> + """; + assertPublicApisParserFails(invalidString); + assertHiddenApisParserFails(invalidString); + } + + @Test + @DisableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) + public void testPrimitiveDelayType_featureFlagDisabled_allFail() { + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET) + .addPrimitive(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE) + .compose(); + String xml = """ + <vibration-effect> + <primitive-effect name="tick" delayType="relative_start_offset"/> + <primitive-effect name="click" scale="0.123" delayMs="10" delayType="pause"/> + </vibration-effect> + """; + + assertPublicApisParserFails(xml); + assertPublicApisSerializerFails(effect); + + assertHiddenApisParserFails(xml); + assertHiddenApisSerializerFails(effect); + } + private void assertPublicApisParserFails(String xml) { assertThrows("Expected parseVibrationEffect to fail for " + xml, VibrationXmlParser.ParseFailedException.class, diff --git a/core/xsd/permission.xsd b/core/xsd/permission.xsd index 0ec8f7d4f2ae..0a0ca7c2248b 100644 --- a/core/xsd/permission.xsd +++ b/core/xsd/permission.xsd @@ -43,6 +43,7 @@ <xs:element name="disabled-until-used-preinstalled-carrier-app" type="disabled-until-used-preinstalled-carrier-app"/> <xs:element name="privapp-permissions" type="privapp-permissions"/> <xs:element name="oem-permissions" type="oem-permissions"/> + <xs:element name="signature-permissions" type="signature-permissions"/> <xs:element name="hidden-api-whitelisted-app" type="hidden-api-whitelisted-app"/> <xs:element name="allow-association" type="allow-association"/> <xs:element name="bugreport-whitelisted" type="bugreport-whitelisted"/> @@ -156,6 +157,21 @@ </xs:sequence> <xs:attribute name="package" type="xs:string"/> </xs:complexType> + <xs:complexType name="signature-permissions"> + <xs:sequence> + <xs:element name="permission" minOccurs="0" maxOccurs="unbounded"> + <xs:complexType> + <xs:attribute name="name" type="xs:string"/> + </xs:complexType> + </xs:element> + <xs:element name="deny-permission" minOccurs="0" maxOccurs="unbounded"> + <xs:complexType> + <xs:attribute name="name" type="xs:string"/> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="package" type="xs:string"/> + </xs:complexType> <xs:complexType name="hidden-api-whitelisted-app"> <xs:attribute name="package" type="xs:string"/> </xs:complexType> diff --git a/core/xsd/schema/current.txt b/core/xsd/schema/current.txt index f3beea1b96a9..cdec6ab6e014 100644 --- a/core/xsd/schema/current.txt +++ b/core/xsd/schema/current.txt @@ -183,6 +183,7 @@ package com.android.xml.permission.configfile { method public java.util.List<com.android.xml.permission.configfile.OemPermissions> getOemPermissions_optional(); method public java.util.List<com.android.xml.permission.configfile.Permission> getPermission_optional(); method public java.util.List<com.android.xml.permission.configfile.PrivappPermissions> getPrivappPermissions_optional(); + method public java.util.List<com.android.xml.permission.configfile.SignaturePermissions> getSignaturePermissions_optional(); method public java.util.List<com.android.xml.permission.configfile.SplitPermission> getSplitPermission_optional(); method public java.util.List<com.android.xml.permission.configfile.SystemUserBlacklistedApp> getSystemUserBlacklistedApp_optional(); method public java.util.List<com.android.xml.permission.configfile.SystemUserWhitelistedApp> getSystemUserWhitelistedApp_optional(); @@ -209,6 +210,26 @@ package com.android.xml.permission.configfile { method public void setName(String); } + public class SignaturePermissions { + ctor public SignaturePermissions(); + method public java.util.List<com.android.xml.permission.configfile.SignaturePermissions.DenyPermission> getDenyPermission(); + method public java.util.List<com.android.xml.permission.configfile.SignaturePermissions.Permission> getPermission(); + method public String get_package(); + method public void set_package(String); + } + + public static class SignaturePermissions.DenyPermission { + ctor public SignaturePermissions.DenyPermission(); + method public String getName(); + method public void setName(String); + } + + public static class SignaturePermissions.Permission { + ctor public SignaturePermissions.Permission(); + method public String getName(); + method public void setName(String); + } + public class SplitPermission { ctor public SplitPermission(); method public java.util.List<com.android.xml.permission.configfile.SplitPermission.Library> getLibrary(); diff --git a/core/xsd/vibrator/vibration/schema/current.txt b/core/xsd/vibrator/vibration/schema/current.txt index 280b40516b7e..b4148d657b0d 100644 --- a/core/xsd/vibrator/vibration/schema/current.txt +++ b/core/xsd/vibrator/vibration/schema/current.txt @@ -15,12 +15,20 @@ package com.android.internal.vibrator.persistence { enum_constant public static final com.android.internal.vibrator.persistence.PredefinedEffectName tick; } + public enum PrimitiveDelayType { + method public String getRawName(); + enum_constant public static final com.android.internal.vibrator.persistence.PrimitiveDelayType pause; + enum_constant public static final com.android.internal.vibrator.persistence.PrimitiveDelayType relative_start_offset; + } + public class PrimitiveEffect { ctor public PrimitiveEffect(); method public java.math.BigInteger getDelayMs(); + method public com.android.internal.vibrator.persistence.PrimitiveDelayType getDelayType(); method public com.android.internal.vibrator.persistence.PrimitiveEffectName getName(); method public float getScale(); method public void setDelayMs(java.math.BigInteger); + method public void setDelayType(com.android.internal.vibrator.persistence.PrimitiveDelayType); method public void setName(com.android.internal.vibrator.persistence.PrimitiveEffectName); method public void setScale(float); } diff --git a/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd b/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd index 21a6facad242..910a9b700b5c 100644 --- a/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd +++ b/core/xsd/vibrator/vibration/vibration-plus-hidden-apis.xsd @@ -147,6 +147,7 @@ <xs:attribute name="name" type="PrimitiveEffectName" use="required"/> <xs:attribute name="scale" type="PrimitiveScale"/> <xs:attribute name="delayMs" type="xs:nonNegativeInteger"/> + <xs:attribute name="delayType" type="PrimitiveDelayType"/> </xs:complexType> <!-- Primitive names as defined by VibrationEffect.Composition.PRIMITIVE_* --> @@ -171,4 +172,12 @@ </xs:restriction> </xs:simpleType> + <!-- Primitive delay types VibrationEffect.Composition.DELAY_TYPE_* --> + <xs:simpleType name="PrimitiveDelayType"> + <xs:restriction base="xs:string"> + <xs:enumeration value="pause"/> + <xs:enumeration value="relative_start_offset"/> + </xs:restriction> + </xs:simpleType> + </xs:schema> diff --git a/core/xsd/vibrator/vibration/vibration.xsd b/core/xsd/vibrator/vibration/vibration.xsd index d35d777d4450..3c8e01605659 100644 --- a/core/xsd/vibrator/vibration/vibration.xsd +++ b/core/xsd/vibrator/vibration/vibration.xsd @@ -124,6 +124,7 @@ <xs:attribute name="name" type="PrimitiveEffectName" use="required"/> <xs:attribute name="scale" type="PrimitiveScale"/> <xs:attribute name="delayMs" type="xs:nonNegativeInteger"/> + <xs:attribute name="delayType" type="PrimitiveDelayType"/> </xs:complexType> <!-- Primitive names as defined by VibrationEffect.Composition.PRIMITIVE_* --> @@ -148,4 +149,12 @@ </xs:restriction> </xs:simpleType> + <!-- Primitive delay types VibrationEffect.Composition.DELAY_TYPE_* --> + <xs:simpleType name="PrimitiveDelayType"> + <xs:restriction base="xs:string"> + <xs:enumeration value="pause"/> + <xs:enumeration value="relative_start_offset"/> + </xs:restriction> + </xs:simpleType> + </xs:schema> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index af690f4449af..897fc543517e 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -677,8 +677,4 @@ applications that come with the platform <permission name="android.permission.BATTERY_STATS"/> <permission name="android.permission.ENTER_TRADE_IN_MODE"/> </privapp-permissions> - - <privapp-permissions package="com.android.multiuser"> - <permission name="android.permission.MANAGE_USERS"/> - </privapp-permissions> </permissions> diff --git a/errorprone/Android.bp b/errorprone/Android.bp index b559a15c3a60..1428b8965473 100644 --- a/errorprone/Android.bp +++ b/errorprone/Android.bp @@ -31,6 +31,14 @@ java_library_host { "//external/auto:auto_service_annotations", ], + javacflags: [ + // These exports are needed because this errorprone plugin access some private classes + // of the java compiler. + "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + ], + plugins: [ "//external/auto:auto_service_plugin", ], diff --git a/graphics/java/android/graphics/RuntimeColorFilter.java b/graphics/java/android/graphics/RuntimeColorFilter.java index d112f7153fca..a64acfe767a9 100644 --- a/graphics/java/android/graphics/RuntimeColorFilter.java +++ b/graphics/java/android/graphics/RuntimeColorFilter.java @@ -280,7 +280,8 @@ public class RuntimeColorFilter extends ColorFilter { if (colorFilter == null) { throw new NullPointerException("The colorFilter parameter must not be null"); } - nativeUpdateChild(getNativeInstance(), filterName, colorFilter.getNativeInstance()); + nativeUpdateInputColorFilter(getNativeInstance(), filterName, + colorFilter.getNativeInstance()); } /** @@ -318,5 +319,6 @@ public class RuntimeColorFilter extends ColorFilter { long colorFilter, String uniformName, int value1, int value2, int value3, int value4, int count); private static native void nativeUpdateChild(long colorFilter, String childName, long child); - + private static native void nativeUpdateInputColorFilter(long colorFilter, String childName, + long inputFilter); } diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java index 6316c1fb8b47..3543e991924e 100644 --- a/graphics/java/android/graphics/RuntimeShader.java +++ b/graphics/java/android/graphics/RuntimeShader.java @@ -264,6 +264,9 @@ public class RuntimeShader extends Shader { * enable better heap tracking & tooling support */ private ArrayMap<String, Shader> mShaderUniforms = new ArrayMap<>(); + private ArrayMap<String, ColorFilter> mColorFilterUniforms = new ArrayMap<>(); + private ArrayMap<String, RuntimeXfermode> mXfermodeUniforms = new ArrayMap<>(); + /** * Creates a new RuntimeShader. @@ -544,8 +547,10 @@ public class RuntimeShader extends Shader { if (colorFilter == null) { throw new NullPointerException("The colorFilter parameter must not be null"); } - nativeUpdateChild(mNativeInstanceRuntimeShaderBuilder, filterName, + mColorFilterUniforms.put(filterName, colorFilter); + nativeUpdateColorFilter(mNativeInstanceRuntimeShaderBuilder, filterName, colorFilter.getNativeInstance()); + discardNativeInstance(); } /** @@ -563,8 +568,10 @@ public class RuntimeShader extends Shader { if (xfermode == null) { throw new NullPointerException("The xfermode parameter must not be null"); } + mXfermodeUniforms.put(xfermodeName, xfermode); nativeUpdateChild(mNativeInstanceRuntimeShaderBuilder, xfermodeName, xfermode.createNativeInstance()); + discardNativeInstance(); } @@ -594,6 +601,8 @@ public class RuntimeShader extends Shader { int value4, int count); private static native void nativeUpdateShader( long shaderBuilder, String shaderName, long shader); + private static native void nativeUpdateColorFilter( + long shaderBuilder, String colorFilterName, long colorFilter); private static native void nativeUpdateChild( long shaderBuilder, String childName, long child); } diff --git a/graphics/java/android/graphics/RuntimeXfermode.java b/graphics/java/android/graphics/RuntimeXfermode.java index 51d97a4b7487..c8a0b1a11339 100644 --- a/graphics/java/android/graphics/RuntimeXfermode.java +++ b/graphics/java/android/graphics/RuntimeXfermode.java @@ -285,7 +285,8 @@ public class RuntimeXfermode extends Xfermode { if (colorFilter == null) { throw new NullPointerException("The colorFilter parameter must not be null"); } - nativeUpdateChild(mBuilderNativeInstance, filterName, colorFilter.getNativeInstance()); + nativeUpdateColorFilter(mBuilderNativeInstance, filterName, + colorFilter.getNativeInstance()); } /** @@ -325,5 +326,6 @@ public class RuntimeXfermode extends Xfermode { long builder, String uniformName, int value1, int value2, int value3, int value4, int count); private static native void nativeUpdateChild(long builder, String childName, long child); + private static native void nativeUpdateColorFilter(long builder, String childName, long filter); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java index 089613853555..5ea38431829e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java @@ -30,8 +30,6 @@ import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; -import androidx.annotation.BinderThread; -import androidx.annotation.GuardedBy; import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -39,14 +37,12 @@ import androidx.window.common.layout.CommonFoldingFeature; import androidx.window.common.layout.DisplayFoldFeatureCommon; import com.android.internal.R; -import com.android.window.flags.Flags; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.Executor; import java.util.function.Consumer; /** @@ -70,20 +66,8 @@ public final class DeviceStateManagerFoldingFeatureProducer * "rear display". Concurrent mode for example is activated via public API and can be active in * both the "open" and "half folded" device states. */ - // TODO: b/337820752 - Add @GuardedBy("mCurrentDeviceStateLock") after flag cleanup. private DeviceState mCurrentDeviceState = INVALID_DEVICE_STATE; - /** - * Lock to synchronize access to {@link #mCurrentDeviceState}. - * - * <p>This lock is used to ensure thread-safety when accessing and modifying the - * {@link #mCurrentDeviceState} field. It is acquired by both the binder thread (if - * {@link Flags#wlinfoOncreate()} is enabled) and the main thread (if - * {@link Flags#wlinfoOncreate()} is disabled) to prevent race conditions and - * ensure data consistency. - */ - private final Object mCurrentDeviceStateLock = new Object(); - @NonNull private final RawFoldingFeatureProducer mRawFoldSupplier; @@ -95,15 +79,12 @@ public final class DeviceStateManagerFoldingFeatureProducer // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the getData() // implementation. See https://errorprone.info/bugpattern/GuardedBy for limitations. @SuppressWarnings("GuardedBy") - @BinderThread // When Flags.wlinfoOncreate() is enabled. - @MainThread // When Flags.wlinfoOncreate() is disabled. + @MainThread @Override public void onDeviceStateChanged(@NonNull DeviceState state) { - synchronized (mCurrentDeviceStateLock) { - mCurrentDeviceState = state; - mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer.this - ::notifyFoldingFeatureChangeLocked); - } + mCurrentDeviceState = state; + mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer.this + ::notifyFoldingFeatureChangeLocked); } }; @@ -115,10 +96,8 @@ public final class DeviceStateManagerFoldingFeatureProducer new DeviceStateMapper(context, deviceStateManager.getSupportedDeviceStates()); if (!mDeviceStateMapper.isDeviceStateToPostureMapEmpty()) { - final Executor executor = - Flags.wlinfoOncreate() ? Runnable::run : context.getMainExecutor(); Objects.requireNonNull(deviceStateManager) - .registerCallback(executor, mDeviceStateCallback); + .registerCallback(context.getMainExecutor(), mDeviceStateCallback); } } @@ -145,21 +124,17 @@ public final class DeviceStateManagerFoldingFeatureProducer @Override protected void onListenersChanged() { super.onListenersChanged(); - synchronized (mCurrentDeviceStateLock) { - if (hasListeners()) { - mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChangeLocked); - } else { - mCurrentDeviceState = INVALID_DEVICE_STATE; - mRawFoldSupplier.removeDataChangedCallback(this::notifyFoldingFeatureChangeLocked); - } + if (hasListeners()) { + mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChangeLocked); + } else { + mCurrentDeviceState = INVALID_DEVICE_STATE; + mRawFoldSupplier.removeDataChangedCallback(this::notifyFoldingFeatureChangeLocked); } } @NonNull private DeviceState getCurrentDeviceState() { - synchronized (mCurrentDeviceStateLock) { - return mCurrentDeviceState; - } + return mCurrentDeviceState; } @NonNull @@ -231,7 +206,6 @@ public final class DeviceStateManagerFoldingFeatureProducer }); } - @GuardedBy("mCurrentDeviceStateLock") private void notifyFoldingFeatureChangeLocked(String displayFeaturesString) { final DeviceState state = mCurrentDeviceState; if (!mDeviceStateMapper.isDeviceStateValid(state)) { @@ -252,29 +226,16 @@ public final class DeviceStateManagerFoldingFeatureProducer return parseListFromString(displayFeaturesString, hingeState); } - /** - * Internal class to map device states to corresponding postures. - * - * <p>This class encapsulates the logic for mapping device states to postures. The mapping is - * immutable after initialization to ensure thread safety. - */ + /** Internal class to map device states to corresponding postures. */ private static class DeviceStateMapper { /** * Emulated device state * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)} to * {@link CommonFoldingFeature.State} map. - * - * <p>This map must be immutable after initialization to ensure thread safety, as it may be - * accessed from multiple threads. Modifications should only occur during object - * construction. */ private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray(); - /** - * The list of device states that are supported. - * - * <p>This list must be immutable after initialization to ensure thread safety. - */ + /** The list of device states that are supported. */ @NonNull private final List<DeviceState> mSupportedStates; diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt index 90887a747a6f..fb01cd88158c 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt @@ -20,9 +20,6 @@ import android.content.Context import android.content.res.Resources import android.hardware.devicestate.DeviceState import android.hardware.devicestate.DeviceStateManager -import android.platform.test.annotations.DisableFlags -import android.platform.test.annotations.EnableFlags -import android.platform.test.flag.junit.SetFlagsRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.window.common.layout.CommonFoldingFeature import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_FLAT @@ -34,15 +31,11 @@ import androidx.window.common.layout.DisplayFoldFeatureCommon import androidx.window.common.layout.DisplayFoldFeatureCommon.DISPLAY_FOLD_FEATURE_PROPERTY_SUPPORTS_HALF_OPENED import androidx.window.common.layout.DisplayFoldFeatureCommon.DISPLAY_FOLD_FEATURE_TYPE_SCREEN_FOLD_IN import com.android.internal.R -import com.android.window.flags.Flags import com.google.common.truth.Truth.assertThat import java.util.Optional -import java.util.concurrent.Executor import java.util.function.Consumer -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn @@ -60,9 +53,6 @@ import org.mockito.kotlin.verify */ @RunWith(AndroidJUnit4::class) class DeviceStateManagerFoldingFeatureProducerTest { - @get:Rule - val setFlagsRule: SetFlagsRule = SetFlagsRule() - private val mMockDeviceStateManager = mock<DeviceStateManager>() private val mMockResources = mock<Resources> { on { getStringArray(R.array.config_device_state_postures) } doReturn DEVICE_STATE_POSTURES @@ -79,8 +69,7 @@ class DeviceStateManagerFoldingFeatureProducerTest { } @Test - @DisableFlags(Flags.FLAG_WLINFO_ONCREATE) - fun testRegisterCallback_whenWlinfoOncreateIsDisabled_usesMainExecutor() { + fun testRegisterCallback_usesMainExecutor() { DeviceStateManagerFoldingFeatureProducer( mMockContext, mRawFoldSupplier, @@ -91,23 +80,6 @@ class DeviceStateManagerFoldingFeatureProducerTest { } @Test - @EnableFlags(Flags.FLAG_WLINFO_ONCREATE) - fun testRegisterCallback_whenWlinfoOncreateIsEnabled_usesRunnableRun() { - val executorCaptor = ArgumentCaptor.forClass(Executor::class.java) - val runnable = mock<Runnable>() - - DeviceStateManagerFoldingFeatureProducer( - mMockContext, - mRawFoldSupplier, - mMockDeviceStateManager, - ) - - verify(mMockDeviceStateManager).registerCallback(executorCaptor.capture(), any()) - executorCaptor.value.execute(runnable) - verify(runnable).run() - } - - @Test fun testGetCurrentData_validCurrentState_returnsFoldingFeatureWithState() { val ffp = DeviceStateManagerFoldingFeatureProducer( mMockContext, diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index a354bf78bb39..4c75ea4777da 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -182,6 +182,7 @@ android_library { "kotlinx-coroutines-core", "//frameworks/libs/systemui:com_android_systemui_shared_flags_lib", "//frameworks/libs/systemui:iconloader_base", + "com_android_launcher3_flags_lib", "com_android_wm_shell_flags_lib", "PlatformAnimationLib", "WindowManager-Shell-proto", diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt new file mode 100644 index 000000000000..f535fbd653c5 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.bubbles + +import android.content.Context +import android.content.pm.LauncherApps +import android.graphics.Insets +import android.graphics.Rect +import android.os.Handler +import android.os.UserManager +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import android.view.IWindowManager +import android.view.WindowManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.internal.protolog.ProtoLog +import com.android.internal.statusbar.IStatusBarService +import com.android.wm.shell.Flags +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.WindowManagerShellWrapper +import com.android.wm.shell.bubbles.Bubbles.SysuiProxy +import com.android.wm.shell.bubbles.properties.ProdBubbleProperties +import com.android.wm.shell.bubbles.storage.BubblePersistentRepository +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayInsetsController +import com.android.wm.shell.common.FloatingContentCoordinator +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.common.TaskStackListenerImpl +import com.android.wm.shell.draganddrop.DragAndDropController +import com.android.wm.shell.shared.TransactionPool +import com.android.wm.shell.shared.bubbles.BubbleBarLocation +import com.android.wm.shell.shared.bubbles.BubbleBarUpdate +import com.android.wm.shell.sysui.ShellCommandHandler +import com.android.wm.shell.sysui.ShellController +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.taskview.TaskViewTransitions +import com.android.wm.shell.transition.Transitions +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors.directExecutor +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import java.util.Optional + +/** Tests for [BubbleController] when using bubble bar */ +@SmallTest +@EnableFlags(Flags.FLAG_ENABLE_BUBBLE_BAR) +@RunWith(AndroidJUnit4::class) +class BubbleControllerBubbleBarTest { + + companion object { + private const val SCREEN_WIDTH = 2000 + private const val SCREEN_HEIGHT = 1000 + } + + @get:Rule val setFlagsRule = SetFlagsRule() + + private val context = ApplicationProvider.getApplicationContext<Context>() + + private lateinit var bubbleController: BubbleController + private lateinit var uiEventLoggerFake: UiEventLoggerFake + private lateinit var bubblePositioner: BubblePositioner + private lateinit var bubbleData: BubbleData + private lateinit var mainExecutor: TestExecutor + private lateinit var bgExecutor: TestExecutor + + @Before + fun setUp() { + ProtoLog.REQUIRE_PROTOLOGTOOL = false + ProtoLog.init() + + mainExecutor = TestExecutor() + bgExecutor = TestExecutor() + + uiEventLoggerFake = UiEventLoggerFake() + val bubbleLogger = BubbleLogger(uiEventLoggerFake) + + val deviceConfig = + DeviceConfig( + windowBounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), + isLargeScreen = true, + isSmallTablet = false, + isLandscape = true, + isRtl = false, + insets = Insets.of(10, 20, 30, 40), + ) + + bubblePositioner = BubblePositioner(context, deviceConfig) + bubblePositioner.isShowingInBubbleBar = true + + bubbleData = + BubbleData( + context, + bubbleLogger, + bubblePositioner, + BubbleEducationController(context), + mainExecutor, + bgExecutor, + ) + + val shellInit = ShellInit(mainExecutor) + + bubbleController = + createBubbleController( + shellInit, + bubbleData, + bubbleLogger, + bubblePositioner, + mainExecutor, + bgExecutor, + ) + bubbleController.asBubbles().setSysuiProxy(Mockito.mock(SysuiProxy::class.java)) + + shellInit.init() + + mainExecutor.flushAll() + bgExecutor.flushAll() + + bubbleController.registerBubbleStateListener(FakeBubblesStateListener()) + } + + @After + fun tearDown() { + mainExecutor.flushAll() + bgExecutor.flushAll() + } + + @Test + fun testEventLogging_bubbleBar_dragBarLeft() { + addBubble() + + bubblePositioner.bubbleBarLocation = BubbleBarLocation.RIGHT + + bubbleController.setBubbleBarLocation( + BubbleBarLocation.LEFT, + BubbleBarLocation.UpdateSource.DRAG_BAR, + ) + + // 2 events: add bubble + drag event + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2) + assertThat(uiEventLoggerFake.eventId(1)) + .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BAR.id) + } + + @Test + fun testEventLogging_bubbleBar_dragBarRight() { + addBubble() + + bubblePositioner.bubbleBarLocation = BubbleBarLocation.LEFT + + bubbleController.setBubbleBarLocation( + BubbleBarLocation.RIGHT, + BubbleBarLocation.UpdateSource.DRAG_BAR, + ) + + // 2 events: add bubble + drag event + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2) + assertThat(uiEventLoggerFake.eventId(1)) + .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BAR.id) + } + + @Test + fun testEventLogging_bubbleBar_dragBubbleLeft() { + addBubble() + + bubblePositioner.bubbleBarLocation = BubbleBarLocation.RIGHT + + bubbleController.setBubbleBarLocation( + BubbleBarLocation.LEFT, + BubbleBarLocation.UpdateSource.DRAG_BUBBLE, + ) + + // 2 events: add bubble + drag event + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2) + assertThat(uiEventLoggerFake.eventId(1)) + .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BUBBLE.id) + } + + @Test + fun testEventLogging_bubbleBar_dragBubbleRight() { + addBubble() + + bubblePositioner.bubbleBarLocation = BubbleBarLocation.LEFT + + bubbleController.setBubbleBarLocation( + BubbleBarLocation.RIGHT, + BubbleBarLocation.UpdateSource.DRAG_BUBBLE, + ) + + // 2 events: add bubble + drag event + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(2) + assertThat(uiEventLoggerFake.eventId(1)) + .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BUBBLE.id) + } + + private fun addBubble(): Bubble { + val bubble = FakeBubbleFactory.createChatBubble(context) + bubble.setInflateSynchronously(true) + bubbleData.notificationEntryUpdated( + bubble, + /* suppressFlyout= */ true, + /* showInShade= */ true, + ) + return bubble + } + + private fun createBubbleController( + shellInit: ShellInit, + bubbleData: BubbleData, + bubbleLogger: BubbleLogger, + bubblePositioner: BubblePositioner, + mainExecutor: TestExecutor, + bgExecutor: TestExecutor, + ): BubbleController { + val shellCommandHandler = ShellCommandHandler() + val shellController = + ShellController( + context, + shellInit, + shellCommandHandler, + mock<DisplayInsetsController>(), + mainExecutor, + ) + val surfaceSynchronizer = { obj: Runnable -> obj.run() } + + val bubbleDataRepository = + BubbleDataRepository( + mock<LauncherApps>(), + mainExecutor, + bgExecutor, + BubblePersistentRepository(context), + ) + + val shellTaskOrganizer = mock<ShellTaskOrganizer>() + whenever(shellTaskOrganizer.executor).thenReturn(directExecutor()) + + return BubbleController( + context, + shellInit, + shellCommandHandler, + shellController, + bubbleData, + surfaceSynchronizer, + FloatingContentCoordinator(), + bubbleDataRepository, + mock<IStatusBarService>(), + mock<WindowManager>(), + WindowManagerShellWrapper(mainExecutor), + mock<UserManager>(), + mock<LauncherApps>(), + bubbleLogger, + mock<TaskStackListenerImpl>(), + shellTaskOrganizer, + bubblePositioner, + mock<DisplayController>(), + /* oneHandedOptional= */ Optional.empty(), + mock<DragAndDropController>(), + mainExecutor, + mock<Handler>(), + bgExecutor, + mock<TaskViewTransitions>(), + mock<Transitions>(), + SyncTransactionQueue(TransactionPool(), mainExecutor), + mock<IWindowManager>(), + ProdBubbleProperties, + ) + } + + private class TestExecutor : ShellExecutor { + + private val runnables: MutableList<Runnable> = mutableListOf() + + override fun execute(runnable: Runnable) { + runnables.add(runnable) + } + + override fun executeDelayed(runnable: Runnable, delayMillis: Long) { + execute(runnable) + } + + override fun removeCallbacks(runnable: Runnable?) {} + + override fun hasCallback(runnable: Runnable?): Boolean = false + + fun flushAll() { + while (runnables.isNotEmpty()) { + runnables.removeAt(0).run() + } + } + } + + private class FakeBubblesStateListener : Bubbles.BubbleStateListener { + override fun onBubbleStateChange(update: BubbleBarUpdate?) {} + + override fun animateBubbleBarLocation(location: BubbleBarLocation?) {} + } +} diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt index cb6fb62bf89b..3279d561d4f1 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/FakeBubbleFactory.kt @@ -32,10 +32,10 @@ class FakeBubbleFactory { return BubbleViewInfo().apply { bubbleBarExpandedView = bubbleExpandedView } } - fun createChatBubbleWithViewInfo( + fun createChatBubble( context: Context, key: String = "key", - viewInfo: BubbleViewInfo, + viewInfo: BubbleViewInfo? = null, ): Bubble { val bubble = Bubble( @@ -50,7 +50,9 @@ class FakeBubbleFactory { directExecutor(), directExecutor(), ) {} - bubble.setViewInfo(viewInfo) + if (viewInfo != null) { + bubble.setViewInfo(viewInfo) + } return bubble } } diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt index 6ac36a3319c9..1bf6af8d1f6d 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt @@ -17,6 +17,7 @@ package com.android.wm.shell.bubbles.bar import android.app.ActivityManager +import android.content.ComponentName import android.content.Context import android.content.pm.ShortcutInfo import android.graphics.Insets @@ -24,6 +25,7 @@ import android.graphics.Rect import android.view.LayoutInflater import android.view.View import android.view.WindowManager +import android.widget.FrameLayout import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -110,9 +112,9 @@ class BubbleBarExpandedViewTest { regionSamplingProvider = TestRegionSamplingProvider() - bubbleExpandedView = (inflater.inflate( + bubbleExpandedView = inflater.inflate( R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ - ) as BubbleBarExpandedView) + ) as BubbleBarExpandedView bubbleExpandedView.initialize( expandedViewManager, positioner, @@ -124,11 +126,11 @@ class BubbleBarExpandedViewTest { regionSamplingProvider, ) - getInstrumentation().runOnMainSync(Runnable { + getInstrumentation().runOnMainSync { bubbleExpandedView.onAttachedToWindow() // Helper should be created once attached to window testableRegionSamplingHelper = regionSamplingProvider!!.helper - }) + } bubble = Bubble( "key", @@ -254,6 +256,93 @@ class BubbleBarExpandedViewTest { assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble) } + @Test + fun animateExpansion_waitsUntilTaskCreated() { + var animated = false + bubbleExpandedView.animateExpansionWhenTaskViewVisible { animated = true } + assertThat(animated).isFalse() + bubbleExpandedView.onTaskCreated() + assertThat(animated).isTrue() + } + + @Test + fun animateExpansion_taskViewAttachedAndVisible() { + val inflater = LayoutInflater.from(context) + val expandedView = inflater.inflate( + R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ + ) as BubbleBarExpandedView + val taskView = FakeBubbleTaskViewFactory().create() + val taskViewParent = FrameLayout(context) + taskViewParent.addView(taskView.taskView) + taskView.listener.onTaskCreated(666, ComponentName(context, "BubbleBarExpandedViewTest")) + assertThat(taskView.isVisible).isTrue() + + expandedView.initialize( + expandedViewManager, + positioner, + BubbleLogger(uiEventLoggerFake), + false /* isOverflow */, + taskView, + mainExecutor, + bgExecutor, + regionSamplingProvider, + ) + + // the task view should be removed from its parent + assertThat(taskView.taskView.parent).isNull() + + var animated = false + expandedView.animateExpansionWhenTaskViewVisible { animated = true } + assertThat(animated).isFalse() + + // send an invisible signal to simulate the surface getting destroyed + expandedView.onContentVisibilityChanged(false) + + // send a visible signal to simulate a new surface getting created + expandedView.onContentVisibilityChanged(true) + + assertThat(taskView.taskView.parent).isEqualTo(expandedView) + assertThat(animated).isTrue() + } + + @Test + fun animateExpansion_taskViewAttachedAndInvisible() { + val inflater = LayoutInflater.from(context) + val expandedView = inflater.inflate( + R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ + ) as BubbleBarExpandedView + val taskView = FakeBubbleTaskViewFactory().create() + val taskViewParent = FrameLayout(context) + taskViewParent.addView(taskView.taskView) + taskView.listener.onTaskCreated(666, ComponentName(context, "BubbleBarExpandedViewTest")) + assertThat(taskView.isVisible).isTrue() + taskView.listener.onTaskVisibilityChanged(666, false) + assertThat(taskView.isVisible).isFalse() + + expandedView.initialize( + expandedViewManager, + positioner, + BubbleLogger(uiEventLoggerFake), + false /* isOverflow */, + taskView, + mainExecutor, + bgExecutor, + regionSamplingProvider, + ) + + // the task view should be added to the expanded view + assertThat(taskView.taskView.parent).isEqualTo(expandedView) + + var animated = false + expandedView.animateExpansionWhenTaskViewVisible { animated = true } + assertThat(animated).isFalse() + + // send a visible signal to simulate a new surface getting created + expandedView.onContentVisibilityChanged(true) + + assertThat(animated).isTrue() + } + private fun BubbleBarExpandedView.menuView(): BubbleBarMenuView { return findViewByPredicate { it is BubbleBarMenuView } } diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt index 0044593ad228..7280f8aa07a6 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt @@ -165,7 +165,7 @@ class BubbleBarLayerViewTest { } val viewInfo = FakeBubbleFactory.createViewInfo(bubbleBarExpandedView) - bubble = FakeBubbleFactory.createChatBubbleWithViewInfo(context, viewInfo = viewInfo) + bubble = FakeBubbleFactory.createChatBubble(context, viewInfo = viewInfo) } @After @@ -253,6 +253,7 @@ class BubbleBarLayerViewTest { getInstrumentation().runOnMainSync { bubbleBarLayerView.showExpandedView(bubble) + bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true) } waitForExpandedViewAnimation() @@ -276,6 +277,7 @@ class BubbleBarLayerViewTest { getInstrumentation().runOnMainSync { bubbleBarLayerView.showExpandedView(bubble) + bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true) } waitForExpandedViewAnimation() diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml index e7ead63a8df6..62782a784db9 100644 --- a/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml +++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml @@ -22,14 +22,14 @@ android:gravity="bottom|end"> <include android:id="@+id/size_compat_hint" - android:visibility="invisible" + android:visibility="gone" android:layout_width="@dimen/compat_hint_width" android:layout_height="wrap_content" layout="@layout/compat_mode_hint"/> <ImageButton android:id="@+id/size_compat_restart_button" - android:visibility="invisible" + android:visibility="gone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/compat_button_margin" diff --git a/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml b/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml index b5f04c3b815a..433d8546ece0 100644 --- a/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml +++ b/libs/WindowManager/Shell/res/layout/user_aspect_ratio_settings_layout.xml @@ -22,14 +22,14 @@ android:gravity="bottom|end"> <include android:id="@+id/user_aspect_ratio_settings_hint" - android:visibility="invisible" + android:visibility="gone" android:layout_width="@dimen/compat_hint_width" android:layout_height="wrap_content" layout="@layout/compat_mode_hint"/> <ImageButton android:id="@+id/user_aspect_ratio_settings_button" - android:visibility="invisible" + android:visibility="gone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/compat_button_margin" diff --git a/libs/WindowManager/Shell/shared/res/values/strings.xml b/libs/WindowManager/Shell/shared/res/values/strings.xml new file mode 100644 index 000000000000..d1804c04cbec --- /dev/null +++ b/libs/WindowManager/Shell/shared/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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> + <!-- Accessibility text for the icons generated by the Manage Windows submenu + in desktop mode and taskbar. [CHAR LIMIT=NONE] --> + <string name="manage_windows_icon_text">Open Window %1$d</string> +</resources> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/multiinstance/ManageWindowsViewContainer.kt index f14dfdbc7d30..0954b52ff151 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/multiinstance/ManageWindowsViewContainer.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.shared.desktopmode +package com.android.wm.shell.shared.multiinstance import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet @@ -34,6 +34,7 @@ import android.view.View.SCALE_Y import android.view.ViewGroup.MarginLayoutParams import android.widget.LinearLayout import android.window.TaskSnapshot +import com.android.wm.shell.shared.R /** * View for the All Windows menu option, used by both Desktop Windowing and Taskbar. @@ -167,6 +168,9 @@ abstract class ManageWindowsViewContainer( val appSnapshotButton = SurfaceView(context) appSnapshotButton.cornerRadius = iconRadius appSnapshotButton.setZOrderOnTop(true) + appSnapshotButton.contentDescription = context.resources.getString( + R.string.manage_windows_icon_text, iconCount + 1 + ) appSnapshotButton.setOnClickListener { onIconClickListener?.invoke(taskId) } @@ -230,10 +234,12 @@ abstract class ManageWindowsViewContainer( /** Play the animation for opening the menu. */ fun animateOpen() { animateView(rootView, MENU_BOUNDS_SHRUNK_SCALE, MENU_BOUNDS_FULL_SCALE, - MENU_START_ALPHA, MENU_FULL_ALPHA) + MENU_START_ALPHA, MENU_FULL_ALPHA + ) for (view in iconViews) { animateView(view, MENU_BOUNDS_SHRUNK_SCALE, MENU_BOUNDS_FULL_SCALE, - MENU_START_ALPHA, MENU_FULL_ALPHA) + MENU_START_ALPHA, MENU_FULL_ALPHA + ) } createAnimatorSet().start() } @@ -241,10 +247,12 @@ abstract class ManageWindowsViewContainer( /** Play the animation for closing the menu. */ fun animateClose(callback: () -> Unit) { animateView(rootView, MENU_BOUNDS_FULL_SCALE, MENU_BOUNDS_SHRUNK_SCALE, - MENU_FULL_ALPHA, MENU_START_ALPHA) + MENU_FULL_ALPHA, MENU_START_ALPHA + ) for (view in iconViews) { animateView(view, MENU_BOUNDS_FULL_SCALE, MENU_BOUNDS_SHRUNK_SCALE, - MENU_FULL_ALPHA, MENU_START_ALPHA) + MENU_FULL_ALPHA, MENU_START_ALPHA + ) } createAnimatorSet().apply { addListener( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt index 37339307f5b0..7e5a82e640cc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt @@ -39,6 +39,8 @@ import android.view.animation.DecelerateInterpolator import android.view.animation.Interpolator import android.view.animation.Transformation import android.window.BackEvent +import android.window.BackEvent.EDGE_LEFT +import android.window.BackEvent.EDGE_RIGHT import android.window.BackMotionEvent import android.window.BackNavigationInfo import android.window.BackProgressAnimator @@ -50,6 +52,7 @@ import com.android.internal.jank.Cuj import com.android.internal.policy.ScreenDecorationsUtils import com.android.internal.policy.SystemBarUtils import com.android.internal.protolog.ProtoLog +import com.android.window.flags.Flags.predictiveBackTimestampApi import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.protolog.ShellProtoLogGroup @@ -118,7 +121,9 @@ abstract class CrossActivityBackAnimation( private val postCommitFlingSpring = SpringForce(SPRING_SCALE) .setStiffness(SpringForce.STIFFNESS_LOW) .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) + private var swipeEdge = EDGE_LEFT protected var gestureProgress = 0f + private val velocityTracker = ProgressVelocityTracker() /** Background color to be used during the animation, also see [getBackgroundColor] */ protected var customizedBackgroundColor = 0 @@ -175,6 +180,7 @@ abstract class CrossActivityBackAnimation( ) return } + swipeEdge = backMotionEvent.swipeEdge triggerBack = backMotionEvent.triggerBack initialTouchPos.set(backMotionEvent.touchX, backMotionEvent.touchY) @@ -241,6 +247,9 @@ abstract class CrossActivityBackAnimation( ) applyTransaction() background.customizeStatusBarAppearance(currentClosingRect.top.toInt()) + if (predictiveBackTimestampApi()) { + velocityTracker.addPosition(backEvent.frameTimeMillis, progress) + } } private fun getYOffset(centeredRect: RectF, touchY: Float): Float { @@ -272,10 +281,19 @@ abstract class CrossActivityBackAnimation( // kick off spring animation with the current velocity from the pre-commit phase, this // affects the scaling of the closing and/or opening activity during post-commit - val startVelocity = - if (gestureProgress < 0.1f) -DEFAULT_FLING_VELOCITY else -velocity * SPRING_SCALE + + var startVelocity = if (predictiveBackTimestampApi()) { + // pronounce fling animation more for gestures + val velocityFactor = if (swipeEdge == EDGE_LEFT || swipeEdge == EDGE_RIGHT) 2f else 1f + velocity * SPRING_SCALE * (1f - MAX_SCALE) * velocityFactor + } else { + velocity * SPRING_SCALE + } + if (gestureProgress < 0.1f) { + startVelocity = startVelocity.coerceAtLeast(DEFAULT_FLING_VELOCITY) + } val flingAnimation = SpringAnimation(postCommitFlingScale, SPRING_SCALE) - .setStartVelocity(startVelocity.coerceIn(-MAX_FLING_VELOCITY, 0f)) + .setStartVelocity(-startVelocity.coerceIn(0f, MAX_FLING_VELOCITY)) .setStartValue(SPRING_SCALE) .setSpring(postCommitFlingSpring) flingAnimation.start() @@ -338,6 +356,7 @@ abstract class CrossActivityBackAnimation( lastPostCommitFlingScale = SPRING_SCALE gestureProgress = 0f triggerBack = false + velocityTracker.resetTracking() } protected fun applyTransform( @@ -520,7 +539,11 @@ abstract class CrossActivityBackAnimation( override fun onBackInvoked() { triggerBack = true progressAnimator.reset() - onGestureCommitted(progressAnimator.velocity) + if (predictiveBackTimestampApi()) { + onGestureCommitted(velocityTracker.calculateVelocity()) + } else { + onGestureCommitted(progressAnimator.velocity) + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java index dc50fdbd1af3..f48b3ffcd598 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java @@ -16,7 +16,6 @@ package com.android.wm.shell.back; -import static android.view.MotionEvent.ACTION_MOVE; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.window.BackEvent.EDGE_RIGHT; @@ -43,10 +42,8 @@ import android.util.TimeUtils; import android.view.Choreographer; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; -import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; -import android.view.VelocityTracker; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.window.BackEvent; @@ -132,9 +129,8 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { private final SpringForce mPostCommitFlingSpring = new SpringForce(SPRING_SCALE) .setStiffness(FLING_SPRING_STIFFNESS) .setDampingRatio(1f); - private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); + private final ProgressVelocityTracker mVelocityTracker = new ProgressVelocityTracker(); private float mGestureProgress = 0f; - private long mDownTime = 0L; @Inject public CrossTaskBackAnimation(Context context, BackAnimationBackground background, @@ -316,8 +312,7 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { mClosingCurrentRect.setEmpty(); mInitialTouchPos.set(0, 0); mGestureProgress = 0; - mDownTime = 0; - mVelocityTracker.clear(); + mVelocityTracker.resetTracking(); if (mFinishCallback != null) { try { @@ -333,22 +328,13 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { private void onGestureProgress(@NonNull BackEvent backEvent) { if (!mBackInProgress) { mBackInProgress = true; - mDownTime = backEvent.getFrameTimeMillis(); } float progress = backEvent.getProgress(); mTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY()); float interpolatedProgress = getInterpolatedProgress(progress); if (predictiveBackTimestampApi()) { - mVelocityTracker.addMovement( - MotionEvent.obtain( - /* downTime */ mDownTime, - /* eventTime */ backEvent.getFrameTimeMillis(), - /* action */ ACTION_MOVE, - /* x */ interpolatedProgress * SPRING_SCALE, - /* y */ 0f, - /* metaState */ 0 - ) - ); + mVelocityTracker.addPosition(backEvent.getFrameTimeMillis(), + interpolatedProgress * SPRING_SCALE); } updateGestureBackProgress(interpolatedProgress, backEvent); } @@ -362,9 +348,8 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { if (predictiveBackTimestampApi()) { // kick off spring animation with the current velocity from the pre-commit phase, this // affects the scaling of the closing and/or opening task during post-commit - mVelocityTracker.computeCurrentVelocity(1000); float startVelocity = mGestureProgress < 0.1f - ? -DEFAULT_FLING_VELOCITY : -mVelocityTracker.getXVelocity(); + ? -DEFAULT_FLING_VELOCITY : -mVelocityTracker.calculateVelocity(); SpringAnimation flingAnimation = new SpringAnimation(mPostCommitFlingScale, SPRING_SCALE) .setStartVelocity(Math.max(-MAX_FLING_VELOCITY, Math.min(0f, startVelocity))) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ProgressVelocityTracker.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ProgressVelocityTracker.kt new file mode 100644 index 000000000000..6bbda0fd7b19 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ProgressVelocityTracker.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.back + +import android.view.MotionEvent +import android.view.VelocityTracker + +internal class ProgressVelocityTracker { + private val velocityTracker: VelocityTracker = VelocityTracker.obtain() + private var downTime = -1L + + fun addPosition(timeMillis: Long, position: Float) { + if (downTime == -1L) downTime = timeMillis + velocityTracker.addMovement( + MotionEvent.obtain( + /* downTime */ downTime, + /* eventTime */ timeMillis, + /* action */ MotionEvent.ACTION_MOVE, + /* x */ position, + /* y */ 0f, + /* metaState */0 + ) + ) + } + + /** calculates current velocity (unit: progress per second) */ + fun calculateVelocity(): Float { + velocityTracker.computeCurrentVelocity(1000) + return velocityTracker.xVelocity + } + + fun resetTracking() { + velocityTracker.clear() + downTime = -1L + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 294569190f68..dc2025bb2dce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -898,7 +898,10 @@ public class BubbleData { Bubble oldest = mOverflowBubbles.get(mOverflowBubbles.size() - 1); ProtoLog.d(WM_SHELL_BUBBLES, "overflow full, remove=%s", oldest.getKey()); mStateChange.bubbleRemoved(oldest, Bubbles.DISMISS_OVERFLOW_MAX_REACHED); - mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_MAX_REACHED); + if (!mPositioner.isShowingInBubbleBar()) { + // Only logged for bubbles in stack view + mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_MAX_REACHED); + } mOverflowBubbles.remove(oldest); mStateChange.removedOverflowBubble = oldest; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 068b2d246500..0fd4206c0545 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -101,9 +101,13 @@ public class BubblePositioner { private int mBubbleBarTopOnScreen; public BubblePositioner(Context context, WindowManager windowManager) { + this(context, DeviceConfig.create(context, windowManager)); + } + + public BubblePositioner(Context context, DeviceConfig deviceConfig) { mContext = context; - mDeviceConfig = DeviceConfig.create(context, windowManager); - update(mDeviceConfig); + mDeviceConfig = deviceConfig; + update(deviceConfig); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt index 68fc0c99abee..a517a2d550b9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt @@ -42,6 +42,16 @@ class BubbleTaskView(val taskView: TaskView, executor: Executor) { var componentName: ComponentName? = null private set + /** + * Whether the task view is visible and has a surface. Note that this does not check the alpha + * value of the task view. + * + * When this is `true` it is safe to start showing the task view. Otherwise if this is `false` + * callers should wait for it to be visible which will be indicated either by a call to + * [TaskView.Listener.onTaskCreated] or [TaskView.Listener.onTaskVisibilityChanged]. */ + var isVisible = false + private set + /** [TaskView.Listener] for users of this class. */ var delegateListener: TaskView.Listener? = null @@ -61,9 +71,12 @@ class BubbleTaskView(val taskView: TaskView, executor: Executor) { this@BubbleTaskView.taskId = taskId isCreated = true componentName = name + // when the task is created it is visible + isVisible = true } override fun onTaskVisibilityChanged(taskId: Int, visible: Boolean) { + this@BubbleTaskView.isVisible = visible delegateListener?.onTaskVisibilityChanged(taskId, visible) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index 74c3748dccaf..a313bd004a51 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -161,6 +161,7 @@ public class BubbleBarAnimationHelper { updateExpandedView(); bbev.setAnimating(true); + bbev.setSurfaceZOrderedOnTop(true); bbev.setContentVisibility(false); bbev.setAlpha(0f); bbev.setTaskViewAlpha(0f); @@ -171,28 +172,29 @@ public class BubbleBarAnimationHelper { bbev.setAnimationMatrix(mExpandedViewContainerMatrix); - mExpandedViewAlphaAnimator.start(); - - PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); - PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) - .spring(AnimatableScaleMatrix.SCALE_X, - AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), - mScaleInSpringConfig) - .spring(AnimatableScaleMatrix.SCALE_Y, - AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), - mScaleInSpringConfig) - .addUpdateListener((target, values) -> { - bbev.setAnimationMatrix(mExpandedViewContainerMatrix); - }) - .withEndActions(() -> { - bbev.setAnimationMatrix(null); - updateExpandedView(); - bbev.setSurfaceZOrderedOnTop(false); - if (afterAnimation != null) { - afterAnimation.run(); - } - }) - .start(); + bbev.animateExpansionWhenTaskViewVisible(() -> { + mExpandedViewAlphaAnimator.start(); + + PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); + PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) + .spring(AnimatableScaleMatrix.SCALE_X, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), + mScaleInSpringConfig) + .spring(AnimatableScaleMatrix.SCALE_Y, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), + mScaleInSpringConfig) + .addUpdateListener((target, values) -> { + bbev.setAnimationMatrix(mExpandedViewContainerMatrix); + }) + .withEndActions(() -> { + bbev.setAnimationMatrix(null); + updateExpandedView(); + if (afterAnimation != null) { + afterAnimation.run(); + } + }) + .start(); + }); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 3764bcd42ac6..ed49417ad3bd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -131,6 +131,11 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView /** Current corner radius */ private float mCurrentCornerRadius = 0f; + /** A runnable to start the expansion animation as soon as the task view is made visible. */ + @Nullable + private Runnable mAnimateExpansion = null; + private TaskViewVisibilityState mVisibilityState = TaskViewVisibilityState.INVISIBLE; + /** * Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If * {@link #mIsAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha @@ -140,6 +145,18 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private boolean mIsAnimating; private boolean mIsDragging; + /** An enum value that tracks the visibility state of the task view */ + private enum TaskViewVisibilityState { + /** The task view is going away, and we're waiting for the surface to be destroyed. */ + PENDING_INVISIBLE, + /** The task view is invisible and does not have a surface. */ + INVISIBLE, + /** The task view is in the process of being added to a surface. */ + PENDING_VISIBLE, + /** The task view is visible and has a surface. */ + VISIBLE + } + public BubbleBarExpandedView(Context context) { this(context, null); } @@ -206,16 +223,27 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, expandedViewManager, /* listener= */ this, bubbleTaskView, /* viewParent= */ this); + + // if the task view is already attached to a parent we need to remove it if (mTaskView.getParent() != null) { + // it's possible that the task view is visible, e.g. if we're unfolding, in which + // case removing it will trigger a visibility change. we have to wait for that + // signal before we can add it to this expanded view, otherwise the signal will be + // incorrect because the task view will have a surface. + // if the task view is not visible, then it has no surface and removing it will not + // trigger any visibility change signals. + if (bubbleTaskView.isVisible()) { + mVisibilityState = TaskViewVisibilityState.PENDING_INVISIBLE; + } ((ViewGroup) mTaskView.getParent()).removeView(mTaskView); } - FrameLayout.LayoutParams lp = - new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT); - addView(mTaskView, lp); - mTaskView.setEnableSurfaceClipping(true); - mTaskView.setCornerRadius(mCurrentCornerRadius); - mTaskView.setVisibility(VISIBLE); - mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0)); + + // if we're invisible it's safe to setup the task view and then await on the visibility + // signal. + if (mVisibilityState == TaskViewVisibilityState.INVISIBLE) { + mVisibilityState = TaskViewVisibilityState.PENDING_VISIBLE; + setupTaskView(); + } // Handle view needs to draw on top of task view. bringChildToFront(mHandleView); @@ -269,6 +297,16 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView }); } + private void setupTaskView() { + FrameLayout.LayoutParams lp = + new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT); + addView(mTaskView, lp); + mTaskView.setEnableSurfaceClipping(true); + mTaskView.setCornerRadius(mCurrentCornerRadius); + mTaskView.setVisibility(VISIBLE); + mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0)); + } + public BubbleBarHandleView getHandleView() { return mHandleView; } @@ -326,15 +364,28 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override public void onTaskCreated() { - setContentVisibility(true); + if (mTaskView != null) { + mTaskView.setAlpha(0); + } if (mListener != null) { mListener.onTaskCreated(); } + // when the task is created we're visible + onTaskViewVisible(); } @Override public void onContentVisibilityChanged(boolean visible) { - setContentVisibility(visible); + if (mVisibilityState == TaskViewVisibilityState.PENDING_INVISIBLE && !visible) { + // the surface is now destroyed. set up the task view and wait for the visibility + // signal. + mVisibilityState = TaskViewVisibilityState.PENDING_VISIBLE; + setupTaskView(); + return; + } + if (visible) { + onTaskViewVisible(); + } } @Override @@ -350,6 +401,25 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mListener.onBackPressed(); } + void animateExpansionWhenTaskViewVisible(Runnable animateExpansion) { + if (mVisibilityState == TaskViewVisibilityState.VISIBLE || mIsOverflow) { + animateExpansion.run(); + } else { + mAnimateExpansion = animateExpansion; + } + } + + private void onTaskViewVisible() { + // if we're waiting to be visible, start the expansion animation if it's pending. + if (mVisibilityState == TaskViewVisibilityState.PENDING_VISIBLE) { + mVisibilityState = TaskViewVisibilityState.VISIBLE; + if (mAnimateExpansion != null) { + mAnimateExpansion.run(); + mAnimateExpansion = null; + } + } + } + /** * Set whether this view is currently being dragged. * diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java index 9abf0f678179..de5c834f3120 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerCallback.java @@ -33,6 +33,9 @@ public interface TaskStackListenerCallback { default void onRecentTaskListFrozenChanged(boolean frozen) { } + /** A task is removed from recents as a result of another task being added to recent tasks. */ + default void onRecentTaskRemovedForAddTask(int taskId) { } + @BinderThread default void onTaskStackChangedBackground() { } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java index d8859bac471f..4e1dec60d956 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TaskStackListenerImpl.java @@ -59,6 +59,7 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler. private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 18; private static final int ON_TASK_DESCRIPTION_CHANGED = 19; private static final int ON_ACTIVITY_ROTATION = 20; + private static final int ON_RECENT_TASK_REMOVED_FOR_ADD_TASK = 21; /** * List of {@link TaskStackListenerCallback} registered from {@link #addListener}. @@ -132,6 +133,11 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler. } @Override + public void onRecentTaskRemovedForAddTask(int taskId) { + mMainHandler.obtainMessage(ON_RECENT_TASK_REMOVED_FOR_ADD_TASK, taskId).sendToTarget(); + } + + @Override public void onTaskStackChanged() { // Call the task changed callback for the non-ui thread listeners first. Copy to a set // of temp listeners so that we don't lock on mTaskStackListeners while calling all the @@ -408,6 +414,13 @@ public class TaskStackListenerImpl extends TaskStackListener implements Handler. } break; } + case ON_RECENT_TASK_REMOVED_FOR_ADD_TASK: { + final int taskId = (int) msg.obj; + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onRecentTaskRemovedForAddTask(taskId); + } + break; + } case ON_TASK_DESCRIPTION_CHANGED: { final ActivityManager.RunningTaskInfo info = (ActivityManager.RunningTaskInfo) msg.obj; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/transition/TransitionStateHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/transition/TransitionStateHolder.kt new file mode 100644 index 000000000000..4dda2a894253 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/transition/TransitionStateHolder.kt @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package com.android.wm.shell.common.transition + +import com.android.wm.shell.dagger.WMSingleton +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener +import com.android.wm.shell.recents.RecentsTransitionStateListener.RecentsTransitionState +import com.android.wm.shell.recents.RecentsTransitionStateListener.isRunning +import com.android.wm.shell.sysui.ShellInit +import javax.inject.Inject + +/** + * Holder for the state of the transitions. + */ +@WMSingleton +class TransitionStateHolder @Inject constructor( + shellInit: ShellInit, + private val recentsTransitionHandler: RecentsTransitionHandler +) { + + @Volatile + @RecentsTransitionState + private var recentsTransitionState: Int = + RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING + + init { + shellInit.addInitCallback({ onInit() }, this) + } + + fun isRecentsTransitionRunning(): Boolean = isRunning(recentsTransitionState) + + private fun onInit() { + recentsTransitionHandler.addTransitionStateListener( + object : RecentsTransitionStateListener { + override fun onTransitionStateChanged(@RecentsTransitionState state: Int) { + recentsTransitionState = state + } + } + ) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java index 49c2785f1ecd..688f8ca2dc75 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java @@ -68,7 +68,7 @@ class CompatUILayout extends LinearLayout { private void setViewVisibility(@IdRes int resId, boolean show) { final View view = findViewById(resId); - int visibility = show ? View.VISIBLE : View.INVISIBLE; + int visibility = show ? View.VISIBLE : View.GONE; if (view.getVisibility() == visibility) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java index fd1bbc477cf4..b141bebbe8b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java @@ -100,7 +100,7 @@ public class UserAspectRatioSettingsLayout extends LinearLayout { private void setViewVisibility(@IdRes int resId, boolean show) { final View view = findViewById(resId); - int visibility = show ? View.VISIBLE : View.INVISIBLE; + int visibility = show ? View.VISIBLE : View.GONE; if (view.getVisibility() == visibility) { return; } @@ -171,7 +171,7 @@ public class UserAspectRatioSettingsLayout extends LinearLayout { fadeOut.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - view.setVisibility(View.INVISIBLE); + view.setVisibility(View.GONE); } }); fadeOut.start(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt index 0ac7aff306a0..523e2f5cf7dc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt @@ -18,133 +18,51 @@ package com.android.wm.shell.compatui.letterbox import android.graphics.Rect import android.view.SurfaceControl -import com.android.internal.protolog.ProtoLog -import com.android.wm.shell.dagger.WMSingleton -import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT -import javax.inject.Inject +import android.view.SurfaceControl.Transaction /** - * Component responsible for handling the lifecycle of the letterbox surfaces. + * Abstracts the component responsible to handle a single or multiple letterbox surfaces for a + * specific [Change]. */ -@WMSingleton -class LetterboxController @Inject constructor( - private val letterboxConfiguration: LetterboxConfiguration -) { - - companion object { - /* - * Letterbox surfaces need to stay below the activity layer which is 0. - */ - // TODO(b/378673153): Consider adding this to [TaskConstants]. - @JvmStatic - private val TASK_CHILD_LAYER_LETTERBOX_BACKGROUND = -1000 - @JvmStatic - private val TAG = "LetterboxController" - } - - private val letterboxMap = mutableMapOf<LetterboxKey, LetterboxItem>() +interface LetterboxController { /** * Creates a Letterbox Surface for a given displayId/taskId if it doesn't exist. */ fun createLetterboxSurface( key: LetterboxKey, - startTransaction: SurfaceControl.Transaction, + transaction: Transaction, parentLeash: SurfaceControl - ) { - letterboxMap.runOnItem(key, onMissed = { k, m -> - m[k] = LetterboxItem( - SurfaceControl.Builder() - .setName("ShellLetterboxSurface-$key") - .setHidden(true) - .setColorLayer() - .setParent(parentLeash) - .setCallsite("LetterboxController-createLetterboxSurface") - .build().apply { - startTransaction.setLayer( - this, - TASK_CHILD_LAYER_LETTERBOX_BACKGROUND - ).setColorSpaceAgnostic(this, true) - .setColor(this, letterboxConfiguration.getBackgroundColorRgbArray()) - } - ) - }) - } + ) /** * Invoked to destroy the surfaces for a letterbox session for given displayId/taskId. */ fun destroyLetterboxSurface( key: LetterboxKey, - startTransaction: SurfaceControl.Transaction - ) { - letterboxMap.runOnItem(key, onFound = { item -> - item.fullWindowSurface?.run { - startTransaction.remove(this) - } - }) - letterboxMap.remove(key) - } + transaction: Transaction + ) /** * Invoked to show/hide the letterbox surfaces for given displayId/taskId. */ fun updateLetterboxSurfaceVisibility( key: LetterboxKey, - startTransaction: SurfaceControl.Transaction, - visible: Boolean = true - ) { - letterboxMap.runOnItem(key, onFound = { item -> - item.fullWindowSurface?.run { - startTransaction.setVisibility(this, visible) - } - }) - } + transaction: Transaction, + visible: Boolean + ) /** * Updates the bounds for the letterbox surfaces for given displayId/taskId. */ fun updateLetterboxSurfaceBounds( key: LetterboxKey, - startTransaction: SurfaceControl.Transaction, - bounds: Rect - ) { - letterboxMap.runOnItem(key, onFound = { item -> - item.fullWindowSurface?.run { - startTransaction.moveAndCrop(this, bounds) - } - }) - } + transaction: Transaction, + taskBounds: Rect + ) - /* - * Executes [onFound] on the [LetterboxItem] if present or [onMissed] if not present. + /** + * Utility method to dump the current state. */ - private fun MutableMap<LetterboxKey, LetterboxItem>.runOnItem( - key: LetterboxKey, - onFound: (LetterboxItem) -> Unit = { _ -> }, - onMissed: ( - LetterboxKey, - MutableMap<LetterboxKey, LetterboxItem> - ) -> Unit = { _, _ -> } - ) { - this[key]?.let { - return onFound(it) - } - return onMissed(key, this) - } - - fun dump() { - ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}") - } - - private fun SurfaceControl.Transaction.moveAndCrop( - surface: SurfaceControl, - rect: Rect - ): SurfaceControl.Transaction = - setPosition(surface, rect.left.toFloat(), rect.top.toFloat()) - .setWindowCrop( - surface, - rect.width(), - rect.height() - ) + fun dump() } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt index 98fd2472f1e4..adb034cc4787 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt @@ -16,10 +16,5 @@ package com.android.wm.shell.compatui.letterbox -import android.view.SurfaceControl - // The key to use for identify the letterbox sessions. -data class LetterboxKey(val displayId: Int, val taskId: Int) - -// Encapsulate the objects for the specific letterbox session. -data class LetterboxItem(val fullWindowSurface: SurfaceControl?)
\ No newline at end of file +data class LetterboxKey(val displayId: Int, val taskId: Int)
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilder.kt new file mode 100644 index 000000000000..e88d91f3956c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilder.kt @@ -0,0 +1,59 @@ +/* + * 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. + */ + +package com.android.wm.shell.compatui.letterbox + +import android.view.SurfaceControl +import com.android.wm.shell.dagger.WMSingleton +import javax.inject.Inject + +/** + * Component responsible for the actual creation of the Letterbox surfaces. + */ +@WMSingleton +class LetterboxSurfaceBuilder @Inject constructor( + private val letterboxConfiguration: LetterboxConfiguration +) { + + companion object { + /* + * Letterbox surfaces need to stay below the activity layer which is 0. + */ + // TODO(b/378673153): Consider adding this to [TaskConstants]. + @JvmStatic + private val TASK_CHILD_LAYER_LETTERBOX_BACKGROUND = -1000 + } + + fun createSurface( + tx: SurfaceControl.Transaction, + parentLeash: SurfaceControl, + surfaceName: String, + callSite: String, + surfaceBuilder: SurfaceControl.Builder = SurfaceControl.Builder() + ) = surfaceBuilder + .setName(surfaceName) + .setHidden(true) + .setColorLayer() + .setParent(parentLeash) + .setCallsite(callSite) + .build().apply { + tx.setLayer( + this, + TASK_CHILD_LAYER_LETTERBOX_BACKGROUND + ).setColorSpaceAgnostic(this, true) + .setColor(this, letterboxConfiguration.getBackgroundColorRgbArray()) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt index 67429bdd112b..b50716ad07a3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt @@ -43,12 +43,7 @@ class LetterboxTransitionObserver( init { if (appCompatRefactoring()) { - ProtoLog.v( - WM_SHELL_APP_COMPAT, - "%s: %s", - TAG, - "Initializing LetterboxTransitionObserver" - ) + logV("Initializing LetterboxTransitionObserver") shellInit.addInitCallback({ transitions.registerObserver(this) }, this) @@ -69,38 +64,45 @@ class LetterboxTransitionObserver( for (change in info.changes) { change.taskInfo?.let { ti -> val key = LetterboxKey(ti.displayId, ti.taskId) - if (isClosingType(change.mode)) { - letterboxController.destroyLetterboxSurface( - key, - startTransaction - ) - } else { - val isTopActivityLetterboxed = ti.appCompatTaskInfo.isTopActivityLetterboxed - if (isTopActivityLetterboxed) { - letterboxController.createLetterboxSurface( + val taskBounds = Rect( + change.endRelOffset.x, + change.endRelOffset.y, + change.endAbsBounds.width(), + change.endAbsBounds.height() + ) + with(letterboxController) { + if (isClosingType(change.mode)) { + destroyLetterboxSurface( key, - startTransaction, - change.leash + startTransaction ) - letterboxController.updateLetterboxSurfaceBounds( + } else { + val isTopActivityLetterboxed = ti.appCompatTaskInfo.isTopActivityLetterboxed + if (isTopActivityLetterboxed) { + createLetterboxSurface( + key, + startTransaction, + change.leash + ) + updateLetterboxSurfaceBounds( + key, + startTransaction, + taskBounds + ) + } + updateLetterboxSurfaceVisibility( key, startTransaction, - Rect( - change.endRelOffset.x, - change.endRelOffset.y, - change.endAbsBounds.width(), - change.endAbsBounds.height() - ) + isTopActivityLetterboxed ) } - letterboxController.updateLetterboxSurfaceVisibility( - key, - startTransaction, - isTopActivityLetterboxed - ) + dump() } - letterboxController.dump() } } } + + private fun logV(msg: String) { + ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, msg) + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt new file mode 100644 index 000000000000..f21a7272287e --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt @@ -0,0 +1,136 @@ +/* + * 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. + */ + +package com.android.wm.shell.compatui.letterbox + +import android.graphics.Rect +import android.view.SurfaceControl +import android.view.SurfaceControl.Transaction +import com.android.internal.protolog.ProtoLog +import com.android.wm.shell.dagger.WMSingleton +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT +import javax.inject.Inject + +/** + * Component responsible for handling the lifecycle of a single letterbox surface. + */ +@WMSingleton +class SingleSurfaceLetterboxController @Inject constructor( + private val letterboxBuilder: LetterboxSurfaceBuilder +) : LetterboxController { + + companion object { + @JvmStatic + private val TAG = "LetterboxController" + } + + private val letterboxMap = mutableMapOf<LetterboxKey, SurfaceControl>() + + /** + * Creates a Letterbox Surface for a given displayId/taskId if it doesn't exist. + */ + override fun createLetterboxSurface( + key: LetterboxKey, + transaction: Transaction, + parentLeash: SurfaceControl + ) { + letterboxMap.runOnItem(key, onMissed = { k, m -> + m[k] = letterboxBuilder.createSurface( + transaction, + parentLeash, + surfaceName = "ShellLetterboxSurface-$key", + callSite = "LetterboxController-createLetterboxSurface" + ) + }) + } + + /** + * Invoked to destroy the surfaces for a letterbox session for given displayId/taskId. + */ + override fun destroyLetterboxSurface( + key: LetterboxKey, + transaction: Transaction + ) { + letterboxMap.runOnItem(key, onFound = { item -> + item.run { + transaction.remove(this) + } + }) + letterboxMap.remove(key) + } + + /** + * Invoked to show/hide the letterbox surfaces for given displayId/taskId. + */ + override fun updateLetterboxSurfaceVisibility( + key: LetterboxKey, + transaction: Transaction, + visible: Boolean + ) { + letterboxMap.runOnItem(key, onFound = { item -> + item.run { + transaction.setVisibility(this, visible) + } + }) + } + + /** + * Updates the bounds for the letterbox surfaces for given displayId/taskId. + */ + override fun updateLetterboxSurfaceBounds( + key: LetterboxKey, + transaction: Transaction, + taskBounds: Rect + ) { + letterboxMap.runOnItem(key, onFound = { item -> + item.run { + transaction.moveAndCrop(this, taskBounds) + } + }) + } + + override fun dump() { + ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}") + } + + /* + * Executes [onFound] on the [SurfaceControl] if present or [onMissed] if not present. + */ + private fun MutableMap<LetterboxKey, SurfaceControl>.runOnItem( + key: LetterboxKey, + onFound: (SurfaceControl) -> Unit = { _ -> }, + onMissed: ( + LetterboxKey, + MutableMap<LetterboxKey, SurfaceControl> + ) -> Unit = { _, _ -> } + ) { + this[key]?.let { + return onFound(it) + } + return onMissed(key, this) + } + + private fun Transaction.moveAndCrop( + surface: SurfaceControl, + rect: Rect + ): Transaction = + setPosition(surface, rect.left.toFloat(), rect.top.toFloat()) + .setWindowCrop( + surface, + rect.width(), + rect.height() + ) +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 60237502007d..cb9c20e9b7ec 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -799,10 +799,11 @@ public abstract class WMShellBaseModule { Transitions transitions, TaskStackListenerImpl taskStackListener, @ShellMainThread Handler mainHandler, - @ShellMainThread ShellExecutor mainExecutor) { + @ShellMainThread ShellExecutor mainExecutor, + FocusTransitionObserver focusTransitionObserver) { return new KeyguardTransitionHandler( shellInit, shellController, displayController, transitions, taskStackListener, - mainHandler, mainExecutor); + mainHandler, mainExecutor, focusTransitionObserver); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 9ec80ec48654..974535385334 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -29,6 +29,7 @@ import android.annotation.Nullable; import android.app.KeyguardManager; import android.content.Context; import android.content.pm.LauncherApps; +import android.content.pm.PackageManager; import android.hardware.input.InputManager; import android.os.Handler; import android.os.UserManager; @@ -70,6 +71,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler; import com.android.wm.shell.compatui.letterbox.LetterboxController; import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver; +import com.android.wm.shell.compatui.letterbox.SingleSurfaceLetterboxController; import com.android.wm.shell.dagger.back.ShellBackAnimationModule; import com.android.wm.shell.dagger.pip.PipModule; import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler; @@ -83,6 +85,7 @@ import com.android.wm.shell.desktopmode.DesktopModeDragAndDropTransitionHandler; import com.android.wm.shell.desktopmode.DesktopModeEventLogger; import com.android.wm.shell.desktopmode.DesktopModeKeyGestureHandler; import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver; +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger; import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.desktopmode.DesktopTaskChangeListener; import com.android.wm.shell.desktopmode.DesktopTasksController; @@ -701,6 +704,7 @@ public abstract class WMShellModule { InputManager inputManager, FocusTransitionObserver focusTransitionObserver, DesktopModeEventLogger desktopModeEventLogger, + DesktopModeUiEventLogger desktopModeUiEventLogger, DesktopTilingDecorViewModel desktopTilingDecorViewModel) { return new DesktopTasksController( context, @@ -731,6 +735,7 @@ public abstract class WMShellModule { interactionJankMonitor, mainHandler, desktopModeEventLogger, + desktopModeUiEventLogger, desktopTilingDecorViewModel); } @@ -850,14 +855,15 @@ public abstract class WMShellModule { Optional<DesktopTasksController> desktopTasksController, InputManager inputManager, ShellTaskOrganizer shellTaskOrganizer, - FocusTransitionObserver focusTransitionObserver) { + FocusTransitionObserver focusTransitionObserver, + @ShellMainThread ShellExecutor mainExecutor) { if (DesktopModeStatus.canEnterDesktopMode(context) && useKeyGestureEventHandler() && manageKeyGestures() && (Flags.enableMoveToNextDisplayShortcut() || Flags.enableTaskResizingKeyboardShortcuts())) { return Optional.of(new DesktopModeKeyGestureHandler(context, desktopModeWindowDecorViewModel, desktopTasksController, - inputManager, shellTaskOrganizer, focusTransitionObserver)); + inputManager, shellTaskOrganizer, focusTransitionObserver, mainExecutor)); } return Optional.empty(); } @@ -881,6 +887,7 @@ public abstract class WMShellModule { SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, + Optional<DesktopImmersiveController> desktopImmersiveController, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, @@ -892,7 +899,8 @@ public abstract class WMShellModule { WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, FocusTransitionObserver focusTransitionObserver, - DesktopModeEventLogger desktopModeEventLogger + DesktopModeEventLogger desktopModeEventLogger, + DesktopModeUiEventLogger desktopModeUiEventLogger ) { if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) { return Optional.empty(); @@ -901,11 +909,12 @@ public abstract class WMShellModule { mainChoreographer, bgExecutor, shellInit, shellCommandHandler, windowManager, taskOrganizer, desktopRepository, displayController, shellController, displayInsetsController, syncQueue, transitions, desktopTasksController, + desktopImmersiveController.get(), rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser, assistContentRequester, multiInstanceHelper, desktopTasksLimiter, appHandleEducationController, appToWebEducationController, windowDecorCaptionHandleRepository, activityOrientationChangeHandler, - focusTransitionObserver, desktopModeEventLogger)); + focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger)); } @WMSingleton @@ -1230,6 +1239,15 @@ public abstract class WMShellModule { mainScope); } + @WMSingleton + @Provides + static DesktopModeUiEventLogger provideDesktopUiEventLogger( + UiEventLogger uiEventLogger, + PackageManager packageManager + ) { + return new DesktopModeUiEventLogger(uiEventLogger, packageManager); + } + // // Drag and drop // @@ -1302,4 +1320,9 @@ public abstract class WMShellModule { ) { return new LetterboxTransitionObserver(shellInit, transitions, letterboxController); } + + @WMSingleton + @Binds + abstract LetterboxController bindsLetterboxController( + SingleSurfaceLetterboxController letterboxController); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt index 4723eb273988..dd95273dd4f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt @@ -99,6 +99,7 @@ class DesktopImmersiveController( /** Starts a transition to enter full immersive state inside the desktop. */ fun moveTaskToImmersive(taskInfo: RunningTaskInfo) { + check(taskInfo.isFreeform) { "Task must already be in freeform" } if (inProgress) { logV( "Cannot start entry because transition(s) already in progress: %s", @@ -121,6 +122,7 @@ class DesktopImmersiveController( /** Starts a transition to move an immersive task out of immersive. */ fun moveTaskToNonImmersive(taskInfo: RunningTaskInfo, reason: ExitReason) { + check(taskInfo.isFreeform) { "Task must already be in freeform" } if (inProgress) { logV( "Cannot start exit because transition(s) already in progress: %s", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt index b4eac7a7b2d3..dc23128b7b2a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -446,6 +446,7 @@ class DesktopModeEventLogger { val displayArea: Int?, ) + @JvmStatic fun getInputMethodFromMotionEvent(e: MotionEvent?): InputMethod { if (e == null) return InputMethod.UNKNOWN_INPUT_METHOD diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt index 114563aa18bb..2b0724d64d0e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt @@ -30,9 +30,11 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.content.Context import com.android.hardware.input.Flags.manageKeyGestures import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts +import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.transition.FocusTransitionObserver import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.shared.annotations.ShellMainThread import java.util.Optional /** @@ -45,6 +47,7 @@ class DesktopModeKeyGestureHandler( inputManager: InputManager, private val shellTaskOrganizer: ShellTaskOrganizer, private val focusTransitionObserver: FocusTransitionObserver, + @ShellMainThread private val mainExecutor: ShellExecutor, ) : KeyGestureEventHandler { init { @@ -72,7 +75,8 @@ class DesktopModeKeyGestureHandler( desktopModeWindowDecorViewModel.get().onSnapResize( it.taskId, true, - DesktopModeEventLogger.Companion.InputMethod.KEYBOARD + DesktopModeEventLogger.Companion.InputMethod.KEYBOARD, + /* fromMenu= */ false ) } return true @@ -83,7 +87,8 @@ class DesktopModeKeyGestureHandler( desktopModeWindowDecorViewModel.get().onSnapResize( it.taskId, false, - DesktopModeEventLogger.Companion.InputMethod.KEYBOARD + DesktopModeEventLogger.Companion.InputMethod.KEYBOARD, + /* fromMenu= */ false ) } return true @@ -94,7 +99,7 @@ class DesktopModeKeyGestureHandler( desktopTasksController.get().toggleDesktopTaskSize( it, ResizeTrigger.MAXIMIZE_MENU, - null, + DesktopModeEventLogger.Companion.InputMethod.KEYBOARD, ) } return true @@ -102,9 +107,11 @@ class DesktopModeKeyGestureHandler( KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW -> { logV("Key gesture MINIMIZE_FREEFORM_WINDOW is handled") getGloballyFocusedFreeformTask()?.let { - desktopTasksController.get().minimizeTask( - it, - ) + mainExecutor.execute { + desktopTasksController.get().minimizeTask( + it, + ) + } } return true } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt index a9d4e5f3216e..2c432bcb55ab 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt @@ -16,22 +16,23 @@ package com.android.wm.shell.desktopmode -import android.util.Log +import android.app.ActivityManager.RunningTaskInfo +import android.content.pm.PackageManager import com.android.internal.logging.InstanceId import com.android.internal.logging.InstanceIdSequence import com.android.internal.logging.UiEvent import com.android.internal.logging.UiEventLogger -import com.android.wm.shell.dagger.WMSingleton -import javax.inject.Inject +import com.android.internal.logging.UiEventLogger.UiEventEnum +import com.android.internal.protolog.ProtoLog +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE /** Log Aster UIEvents for desktop windowing mode. */ -@WMSingleton -class DesktopModeUiEventLogger -@Inject -constructor( - private val mUiEventLogger: UiEventLogger, - private val mInstanceIdSequence: InstanceIdSequence +class DesktopModeUiEventLogger( + private val uiEventLogger: UiEventLogger, + private val packageManager: PackageManager, ) { + private val instanceIdSequence = InstanceIdSequence(Integer.MAX_VALUE) + /** * Logs an event for a CUI, on a particular package. * @@ -41,14 +42,25 @@ constructor( */ fun log(uid: Int, packageName: String, event: DesktopUiEventEnum) { if (packageName.isEmpty() || uid < 0) { - Log.d(TAG, "Skip logging since package name is empty or bad uid") + logD("Skip logging since package name is empty or bad uid") return } - mUiEventLogger.log(event, uid, packageName) + uiEventLogger.log(event, uid, packageName) + } + + /** Logs an event for a CUI on a particular task. */ + fun log(taskInfo: RunningTaskInfo, event: DesktopUiEventEnum) { + val packageName = taskInfo.baseActivity?.packageName + if (packageName == null) { + logD("Skip logging due to null base activity") + return + } + val uid = getUid(packageName, taskInfo.userId) + log(uid, packageName, event) } /** Retrieves a new instance id for a new interaction. */ - fun getNewInstanceId(): InstanceId = mInstanceIdSequence.newInstanceId() + fun getNewInstanceId(): InstanceId = instanceIdSequence.newInstanceId() /** * Logs an event as part of a particular CUI, on a particular package. @@ -66,28 +78,71 @@ constructor( event: DesktopUiEventEnum ) { if (packageName.isEmpty() || uid < 0) { - Log.d(TAG, "Skip logging since package name is empty or bad uid") + logD("Skip logging since package name is empty or bad uid") return } - mUiEventLogger.logWithInstanceId(event, uid, packageName, instanceId) + uiEventLogger.logWithInstanceId(event, uid, packageName, instanceId) } - companion object { - /** Enums for logging desktop windowing mode UiEvents. */ - enum class DesktopUiEventEnum(private val mId: Int) : UiEventLogger.UiEventEnum { + private fun getUid(packageName: String, userId: Int): Int = try { + packageManager.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId).uid + } catch (e: PackageManager.NameNotFoundException) { + INVALID_PACKAGE_UID + } - @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the edge") - DESKTOP_WINDOW_EDGE_DRAG_RESIZE(1721), - @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the corner") - DESKTOP_WINDOW_CORNER_DRAG_RESIZE(1722), - @UiEvent(doc = "Tap on the window header maximize button in desktop windowing mode") - DESKTOP_WINDOW_MAXIMIZE_BUTTON_TAP(1723), - @UiEvent(doc = "Double tap on window header to maximize it in desktop windowing mode") - DESKTOP_WINDOW_HEADER_DOUBLE_TAP_TO_MAXIMIZE(1724); + private fun logD(msg: String, vararg arguments: Any?) { + ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } - override fun getId(): Int = mId - } + /** Enums for logging desktop windowing mode UiEvents. */ + enum class DesktopUiEventEnum(private val mId: Int) : UiEventEnum { + + @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the edge") + DESKTOP_WINDOW_EDGE_DRAG_RESIZE(1721), + @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the corner") + DESKTOP_WINDOW_CORNER_DRAG_RESIZE(1722), + @UiEvent(doc = "Tap on the window header maximize button in desktop windowing mode") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_TAP(1723), + @UiEvent(doc = "Double tap on window header to maximize it in desktop windowing mode") + DESKTOP_WINDOW_HEADER_DOUBLE_TAP_TO_MAXIMIZE(1724), + @UiEvent(doc = "Tap on the window Handle to open the Handle Menu") + DESKTOP_WINDOW_APP_HANDLE_TAP(1998), + @UiEvent(doc = "Tap on the desktop mode option under app handle menu") + DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_DESKTOP_MODE(1999), + @UiEvent(doc = "Tap on the split screen option under app handle menu") + DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_SPLIT_SCREEN(2000), + @UiEvent(doc = "Tap on the full screen option under app handle menu") + DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_FULL_SCREEN(2001), + @UiEvent(doc = "When user successfully drags the app handle to desktop mode") + DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_DESKTOP_MODE(2002), + @UiEvent(doc = "When user successfully drags the app handle to split screen") + DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_SPLIT_SCREEN(2003), + @UiEvent(doc = "When user successfully drags the app handle to full screen") + DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_FULL_SCREEN(2004), + @UiEvent(doc = "Drag the window header to the top to switch to full screen mode") + DESKTOP_WINDOW_APP_HEADER_DRAG_TO_FULL_SCREEN(2005), + @UiEvent(doc = "Drag the window header to an edge to tile it to the left side") + DESKTOP_WINDOW_APP_HEADER_DRAG_TO_TILE_TO_LEFT(2006), + @UiEvent(doc = "Drag the window header to an edge to tile it to the right side") + DESKTOP_WINDOW_APP_HEADER_DRAG_TO_TILE_TO_RIGHT(2007), + @UiEvent(doc = "Hover or long press the maximize button to reveal the menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_REVEAL_MENU(2015), + @UiEvent(doc = "Tap on the maximize option in the maximize button menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_MAXIMIZE(2009), + @UiEvent(doc = "Tap on the immersive option in the maximize button menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_IMMERSIVE(2010), + @UiEvent(doc = "Tap on the restore option in the maximize button menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_RESTORE(2011), + @UiEvent(doc = "Tap on the tile to left option in the maximize button menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_TILE_TO_LEFT(2012), + @UiEvent(doc = "Tap on the tile to right option in the maximize button menu") + DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_TILE_TO_RIGHT(2013); + override fun getId(): Int = mId + } + + companion object { private const val TAG = "DesktopModeUiEventLogger" + private const val INVALID_PACKAGE_UID = -1 } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java index 09e77fee7527..80d8ecc127fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java @@ -120,6 +120,7 @@ public class DesktopModeVisualIndicator { private View mView; private IndicatorType mCurrentType; private DragStartState mDragStartState; + private boolean mIsReleased; public DesktopModeVisualIndicator(SyncTransactionQueue syncQueue, ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController, @@ -240,6 +241,7 @@ public class DesktopModeVisualIndicator { * Create a fullscreen indicator with no animation */ private void createView() { + if (mIsReleased) return; final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); final Resources resources = mContext.getResources(); final DisplayMetrics metrics = resources.getDisplayMetrics(); @@ -295,6 +297,12 @@ public class DesktopModeVisualIndicator { * @param finishCallback called when animation ends or gets cancelled */ void fadeOutIndicator(@Nullable Runnable finishCallback) { + if (mCurrentType == NO_INDICATOR) { + // In rare cases, fade out can be requested before the indicator has determined its + // initial type and started animating in. In this case, no animator is needed. + finishCallback.run(); + return; + } final VisualIndicatorAnimator animator = VisualIndicatorAnimator .fadeBoundsOut(mView, mCurrentType, mDisplayController.getDisplayLayout(mTaskInfo.displayId)); @@ -335,6 +343,7 @@ public class DesktopModeVisualIndicator { * Release the indicator and its components when it is no longer needed. */ public void releaseVisualIndicator(SurfaceControl.Transaction t) { + mIsReleased = true; if (mViewHost == null) return; if (mViewHost != null) { mViewHost.release(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index ccbc9204ad9d..ad28dcc5598a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -130,6 +130,7 @@ import java.util.concurrent.Executor import java.util.function.Consumer import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion.DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION @@ -166,6 +167,7 @@ class DesktopTasksController( private val interactionJankMonitor: InteractionJankMonitor, @ShellMainThread private val handler: Handler, private val desktopModeEventLogger: DesktopModeEventLogger, + private val desktopModeUiEventLogger: DesktopModeUiEventLogger, private val desktopTilingDecorViewModel: DesktopTilingDecorViewModel, ) : RemoteCallable<DesktopTasksController>, @@ -786,31 +788,6 @@ class DesktopTasksController( transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) } - /** Moves a task in/out of full immersive state within the desktop. */ - fun toggleDesktopTaskFullImmersiveState(taskInfo: RunningTaskInfo) { - if (taskRepository.isTaskInFullImmersiveState(taskInfo.taskId)) { - exitDesktopTaskFromFullImmersive( - taskInfo, - DesktopImmersiveController.ExitReason.USER_INTERACTION, - ) - } else { - moveDesktopTaskToFullImmersive(taskInfo) - } - } - - private fun moveDesktopTaskToFullImmersive(taskInfo: RunningTaskInfo) { - check(taskInfo.isFreeform) { "Task must already be in freeform" } - desktopImmersiveController.moveTaskToImmersive(taskInfo) - } - - private fun exitDesktopTaskFromFullImmersive( - taskInfo: RunningTaskInfo, - reason: DesktopImmersiveController.ExitReason, - ) { - check(taskInfo.isFreeform) { "Task must already be in freeform" } - desktopImmersiveController.moveTaskToNonImmersive(taskInfo, reason) - } - /** * Quick-resizes a desktop task, toggling between a fullscreen state (represented by the stable * bounds) and a free floating state (either the last saved bounds if available or the default @@ -819,12 +796,12 @@ class DesktopTasksController( fun toggleDesktopTaskSize( taskInfo: RunningTaskInfo, resizeTrigger: ResizeTrigger, - motionEvent: MotionEvent?, + inputMethod: InputMethod, ) { val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds desktopModeEventLogger.logTaskResizingStarted( resizeTrigger, - DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent), + inputMethod, taskInfo, currentTaskBounds.width(), currentTaskBounds.height(), @@ -877,7 +854,7 @@ class DesktopTasksController( taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding) val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) desktopModeEventLogger.logTaskResizingEnded( - resizeTrigger, DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent), + resizeTrigger, inputMethod, taskInfo, destinationBounds.width(), destinationBounds.height(), displayController ) @@ -909,7 +886,11 @@ class DesktopTasksController( return } - toggleDesktopTaskSize(taskInfo, ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER, motionEvent) + toggleDesktopTaskSize( + taskInfo, + ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER, + DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent) + ) } private fun getMaximizeBounds(taskInfo: RunningTaskInfo, stableBounds: Rect): Rect { @@ -2083,6 +2064,10 @@ class DesktopTasksController( if (DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)) { dragToMaximizeDesktopTask(taskInfo, taskSurface, currentDragBounds, motionEvent) } else { + desktopModeUiEventLogger.log( + taskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_APP_HEADER_DRAG_TO_FULL_SCREEN + ) moveToFullscreenWithAnimation( taskInfo, position, @@ -2091,6 +2076,10 @@ class DesktopTasksController( } } IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { + desktopModeUiEventLogger.log( + taskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_APP_HEADER_DRAG_TO_TILE_TO_LEFT + ) handleSnapResizingTaskOnDrag( taskInfo, SnapPosition.LEFT, @@ -2102,6 +2091,10 @@ class DesktopTasksController( ) } IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { + desktopModeUiEventLogger.log( + taskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_APP_HEADER_DRAG_TO_TILE_TO_RIGHT + ) handleSnapResizingTaskOnDrag( taskInfo, SnapPosition.RIGHT, @@ -2188,16 +2181,32 @@ class DesktopTasksController( // Start a new jank interaction for the drag release to desktop window animation. interactionJankMonitor.begin(taskSurface, context, handler, CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE, "to_desktop") + desktopModeUiEventLogger.log( + taskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_DESKTOP_MODE + ) finalizeDragToDesktop(taskInfo) } IndicatorType.NO_INDICATOR, IndicatorType.TO_FULLSCREEN_INDICATOR -> { + desktopModeUiEventLogger.log( + taskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_FULL_SCREEN + ) cancelDragToDesktop(taskInfo) } IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { + desktopModeUiEventLogger.log( + taskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_SPLIT_SCREEN + ) requestSplit(taskInfo, leftOrTop = true) } IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> { + desktopModeUiEventLogger.log( + taskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_SPLIT_SCREEN + ) requestSplit(taskInfo, leftOrTop = false) } } @@ -2341,7 +2350,7 @@ class DesktopTasksController( if (inImmersive && !requestingImmersive && !RecentsTransitionStateListener.isRunning(recentsTransitionState)) { // Exit immersive if the app is no longer requesting it. - exitDesktopTaskFromFullImmersive( + desktopImmersiveController.moveTaskToNonImmersive( taskInfo, DesktopImmersiveController.ExitReason.APP_NOT_IMMERSIVE ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt index e835b2fec232..909a06604382 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt @@ -17,12 +17,10 @@ package com.android.wm.shell.desktopmode import android.app.Activity -import android.app.ActivityManager +import android.app.TaskInfo import android.content.ComponentName import android.os.Bundle import android.view.WindowManager -import com.android.internal.protolog.ProtoLog -import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE /** * A transparent activity used in the desktop mode to show the wallpaper under the freeform windows. @@ -42,11 +40,12 @@ class DesktopWallpaperActivity : Activity() { companion object { private const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui" - private val wallpaperActivityComponent = + @JvmStatic + val wallpaperActivityComponent = ComponentName(SYSTEM_UI_PACKAGE_NAME, DesktopWallpaperActivity::class.java.name) @JvmStatic - fun isWallpaperTask(taskInfo: ActivityManager.RunningTaskInfo) = + fun isWallpaperTask(taskInfo: TaskInfo) = taskInfo.baseIntent.component?.let(::isWallpaperComponent) ?: false @JvmStatic diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt index e01c448be8e5..c5fca028a1a6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt @@ -36,8 +36,8 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource import com.android.wm.shell.windowdecor.common.DecorThemeUtil import com.android.wm.shell.windowdecor.common.Theme import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController -import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipEducationViewConfig import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipColorScheme +import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipEducationViewConfig import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainCoroutineDispatcher @@ -74,293 +74,330 @@ class AppHandleEducationController( @ShellMainThread private val applicationCoroutineScope: CoroutineScope, @ShellBackgroundThread private val backgroundDispatcher: MainCoroutineDispatcher, ) { - private val decorThemeUtil = DecorThemeUtil(context) - private lateinit var openHandleMenuCallback: (Int) -> Unit - private lateinit var toDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit + private val decorThemeUtil = DecorThemeUtil(context) + private lateinit var openHandleMenuCallback: (Int) -> Unit + private lateinit var toDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit + + init { + runIfEducationFeatureEnabled { + applicationCoroutineScope.launch { + // Central block handling the app handle's educational flow end-to-end. + isAppHandleHintViewedFlow() + .flatMapLatest { isAppHandleHintViewed -> + if (isAppHandleHintViewed) { + // If the education is viewed then return emptyFlow() that completes + // immediately. + // This will help us to not listen to [captionHandleStateFlow] after the + // education + // has been viewed already. + emptyFlow() + } else { + // Listen for changes to window decor's caption handle. + windowDecorCaptionHandleRepository.captionStateFlow + // Wait for few seconds before emitting the latest state. + .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) + .filter { captionState -> + captionState is CaptionState.AppHandle && + appHandleEducationFilter.shouldShowAppHandleEducation( + captionState + ) + } + } + } + .flowOn(backgroundDispatcher) + .collectLatest { captionState -> + val tooltipColorScheme = tooltipColorScheme(captionState) + + showEducation(captionState, tooltipColorScheme) + // After showing first tooltip, mark education as viewed + appHandleEducationDatastoreRepository + .updateAppHandleHintViewedTimestampMillis(true) + } + } - init { - runIfEducationFeatureEnabled { - applicationCoroutineScope.launch { - // Central block handling the app handle's educational flow end-to-end. - isEducationViewedFlow() - .flatMapLatest { isEducationViewed -> - if (isEducationViewed) { - // If the education is viewed then return emptyFlow() that completes immediately. - // This will help us to not listen to [captionHandleStateFlow] after the education - // has been viewed already. - emptyFlow() - } else { - // Listen for changes to window decor's caption handle. + applicationCoroutineScope.launch { + if (isAppHandleHintUsed()) return@launch windowDecorCaptionHandleRepository.captionStateFlow - // Wait for few seconds before emitting the latest state. - .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) .filter { captionState -> - captionState is CaptionState.AppHandle && - appHandleEducationFilter.shouldShowAppHandleEducation(captionState) + captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded } - } + .take(1) + .flowOn(backgroundDispatcher) + .collect { + // If user expands app handle, mark user has used the app handle hint + appHandleEducationDatastoreRepository + .updateAppHandleHintUsedTimestampMillis(true) + } + } + } + } + + private inline fun runIfEducationFeatureEnabled(block: () -> Unit) { + if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppHandleEducation()) + block() + } + + private fun showEducation(captionState: CaptionState, tooltipColorScheme: TooltipColorScheme) { + val appHandleBounds = (captionState as CaptionState.AppHandle).globalAppHandleBounds + val tooltipGlobalCoordinates = + Point(appHandleBounds.left + appHandleBounds.width() / 2, appHandleBounds.bottom) + // TODO: b/370546801 - Differentiate between user dismissing the tooltip vs following the + // cue. + // Populate information important to inflate app handle education tooltip. + val appHandleTooltipConfig = + TooltipEducationViewConfig( + tooltipViewLayout = R.layout.desktop_windowing_education_top_arrow_tooltip, + tooltipColorScheme = tooltipColorScheme, + tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, + tooltipText = getString(R.string.windowing_app_handle_education_tooltip), + arrowDirection = + DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP, + onEducationClickAction = { + launchWithExceptionHandling { + showWindowingImageButtonTooltip(tooltipColorScheme) + } + openHandleMenuCallback(captionState.runningTaskInfo.taskId) + }, + onDismissAction = { + launchWithExceptionHandling { + showWindowingImageButtonTooltip(tooltipColorScheme) + } + }, + ) + + windowingEducationViewController.showEducationTooltip( + tooltipViewConfig = appHandleTooltipConfig, + taskId = captionState.runningTaskInfo.taskId, + ) + } + + /** Show tooltip that points to windowing image button in app handle menu */ + private suspend fun showWindowingImageButtonTooltip(tooltipColorScheme: TooltipColorScheme) { + val appInfoPillHeight = getSize(R.dimen.desktop_mode_handle_menu_app_info_pill_height) + val windowingOptionPillHeight = + getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height) + val appHandleMenuWidth = + getSize(R.dimen.desktop_mode_handle_menu_width) + + getSize(R.dimen.desktop_mode_handle_menu_pill_spacing_margin) + val appHandleMenuMargins = + getSize(R.dimen.desktop_mode_handle_menu_margin_top) + + getSize(R.dimen.desktop_mode_handle_menu_pill_spacing_margin) + + windowDecorCaptionHandleRepository.captionStateFlow + // After the first tooltip was dismissed, wait for 400 ms and see if the app handle menu + // has been expanded. + .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds) + .catchTimeoutAndLog { + // TODO: b/341320146 - Log previous tooltip was dismissed + } + // Wait for few milliseconds before emitting the latest state. + .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) + .filter { captionState -> + // Filter out states when app handle is not visible or not expanded. + captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded } + // Before showing this tooltip, stop listening to further emissions to avoid + // accidentally + // showing the same tooltip on future emissions. + .take(1) .flowOn(backgroundDispatcher) .collectLatest { captionState -> - val tooltipColorScheme = tooltipColorScheme(captionState) + captionState as CaptionState.AppHandle + val appHandleBounds = captionState.globalAppHandleBounds + val tooltipGlobalCoordinates = + Point( + appHandleBounds.left + appHandleBounds.width() / 2 + appHandleMenuWidth / 2, + appHandleBounds.top + + appHandleMenuMargins + + appInfoPillHeight + + windowingOptionPillHeight / 2, + ) + // Populate information important to inflate windowing image button education + // tooltip. + val windowingImageButtonTooltipConfig = + TooltipEducationViewConfig( + tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, + tooltipColorScheme = tooltipColorScheme, + tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, + tooltipText = + getString( + R.string.windowing_desktop_mode_image_button_education_tooltip + ), + arrowDirection = + DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, + onEducationClickAction = { + launchWithExceptionHandling { + showExitWindowingTooltip(tooltipColorScheme) + } + toDesktopModeCallback( + captionState.runningTaskInfo.taskId, + DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON, + ) + }, + onDismissAction = { + launchWithExceptionHandling { + showExitWindowingTooltip(tooltipColorScheme) + } + }, + ) - showEducation(captionState, tooltipColorScheme) - // After showing first tooltip, mark education as viewed - appHandleEducationDatastoreRepository.updateEducationViewedTimestampMillis(true) + windowingEducationViewController.showEducationTooltip( + taskId = captionState.runningTaskInfo.taskId, + tooltipViewConfig = windowingImageButtonTooltipConfig, + ) } - } + } - applicationCoroutineScope.launch { - if (isFeatureUsed()) return@launch + /** Show tooltip that points to app chip button and educates user on how to exit desktop mode */ + private suspend fun showExitWindowingTooltip(tooltipColorScheme: TooltipColorScheme) { windowDecorCaptionHandleRepository.captionStateFlow + // After the previous tooltip was dismissed, wait for 400 ms and see if the user entered + // desktop mode. + .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds) + .catchTimeoutAndLog { + // TODO: b/341320146 - Log previous tooltip was dismissed + } + // Wait for few milliseconds before emitting the latest state. + .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) .filter { captionState -> - captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded + // Filter out states when app header is not visible or expanded. + captionState is CaptionState.AppHeader && !captionState.isHeaderMenuExpanded } + // Before showing this tooltip, stop listening to further emissions to avoid + // accidentally + // showing the same tooltip on future emissions. .take(1) .flowOn(backgroundDispatcher) - .collect { - // If user expands app handle, mark user has used the feature - appHandleEducationDatastoreRepository.updateFeatureUsedTimestampMillis(true) + .collectLatest { captionState -> + captionState as CaptionState.AppHeader + val globalAppChipBounds = captionState.globalAppChipBounds + val tooltipGlobalCoordinates = + Point( + globalAppChipBounds.right, + globalAppChipBounds.top + globalAppChipBounds.height() / 2, + ) + // Populate information important to inflate exit desktop mode education tooltip. + val exitWindowingTooltipConfig = + TooltipEducationViewConfig( + tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, + tooltipColorScheme = tooltipColorScheme, + tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, + tooltipText = + getString(R.string.windowing_desktop_mode_exit_education_tooltip), + arrowDirection = + DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, + onDismissAction = {}, + onEducationClickAction = { + openHandleMenuCallback(captionState.runningTaskInfo.taskId) + }, + ) + windowingEducationViewController.showEducationTooltip( + taskId = captionState.runningTaskInfo.taskId, + tooltipViewConfig = exitWindowingTooltipConfig, + ) } - } } - } - - private inline fun runIfEducationFeatureEnabled(block: () -> Unit) { - if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppHandleEducation()) block() - } - - private fun showEducation(captionState: CaptionState, tooltipColorScheme: TooltipColorScheme) { - val appHandleBounds = (captionState as CaptionState.AppHandle).globalAppHandleBounds - val tooltipGlobalCoordinates = - Point(appHandleBounds.left + appHandleBounds.width() / 2, appHandleBounds.bottom) - // TODO: b/370546801 - Differentiate between user dismissing the tooltip vs following the cue. - // Populate information important to inflate app handle education tooltip. - val appHandleTooltipConfig = - TooltipEducationViewConfig( - tooltipViewLayout = R.layout.desktop_windowing_education_top_arrow_tooltip, - tooltipColorScheme = tooltipColorScheme, - tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, - tooltipText = getString(R.string.windowing_app_handle_education_tooltip), - arrowDirection = DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP, - onEducationClickAction = { - launchWithExceptionHandling { showWindowingImageButtonTooltip(tooltipColorScheme) } - openHandleMenuCallback(captionState.runningTaskInfo.taskId) - }, - onDismissAction = { - launchWithExceptionHandling { showWindowingImageButtonTooltip(tooltipColorScheme) } - }, - ) - - windowingEducationViewController.showEducationTooltip( - tooltipViewConfig = appHandleTooltipConfig, taskId = captionState.runningTaskInfo.taskId) - } - - /** Show tooltip that points to windowing image button in app handle menu */ - private suspend fun showWindowingImageButtonTooltip(tooltipColorScheme: TooltipColorScheme) { - val appInfoPillHeight = getSize(R.dimen.desktop_mode_handle_menu_app_info_pill_height) - val windowingOptionPillHeight = getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height) - val appHandleMenuWidth = - getSize(R.dimen.desktop_mode_handle_menu_width) + - getSize(R.dimen.desktop_mode_handle_menu_pill_spacing_margin) - val appHandleMenuMargins = - getSize(R.dimen.desktop_mode_handle_menu_margin_top) + - getSize(R.dimen.desktop_mode_handle_menu_pill_spacing_margin) - - windowDecorCaptionHandleRepository.captionStateFlow - // After the first tooltip was dismissed, wait for 400 ms and see if the app handle menu - // has been expanded. - .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds) - .catchTimeoutAndLog { - // TODO: b/341320146 - Log previous tooltip was dismissed - } - // Wait for few milliseconds before emitting the latest state. - .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) - .filter { captionState -> - // Filter out states when app handle is not visible or not expanded. - captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded - } - // Before showing this tooltip, stop listening to further emissions to avoid accidentally - // showing the same tooltip on future emissions. - .take(1) - .flowOn(backgroundDispatcher) - .collectLatest { captionState -> - captionState as CaptionState.AppHandle - val appHandleBounds = captionState.globalAppHandleBounds - val tooltipGlobalCoordinates = - Point( - appHandleBounds.left + appHandleBounds.width() / 2 + appHandleMenuWidth / 2, - appHandleBounds.top + - appHandleMenuMargins + - appInfoPillHeight + - windowingOptionPillHeight / 2) - // Populate information important to inflate windowing image button education tooltip. - val windowingImageButtonTooltipConfig = - TooltipEducationViewConfig( - tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, - tooltipColorScheme = tooltipColorScheme, - tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, - tooltipText = - getString(R.string.windowing_desktop_mode_image_button_education_tooltip), - arrowDirection = - DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, - onEducationClickAction = { - launchWithExceptionHandling { showExitWindowingTooltip(tooltipColorScheme) } - toDesktopModeCallback( - captionState.runningTaskInfo.taskId, - DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON) - }, - onDismissAction = { - launchWithExceptionHandling { showExitWindowingTooltip(tooltipColorScheme) } - }, - ) - windowingEducationViewController.showEducationTooltip( - taskId = captionState.runningTaskInfo.taskId, - tooltipViewConfig = windowingImageButtonTooltipConfig) - } - } + private fun tooltipColorScheme(captionState: CaptionState): TooltipColorScheme { + context.withStyledAttributes( + set = null, + attrs = + intArrayOf( + com.android.internal.R.attr.materialColorOnTertiaryFixed, + com.android.internal.R.attr.materialColorTertiaryFixed, + com.android.internal.R.attr.materialColorTertiaryFixedDim, + ), + defStyleAttr = 0, + defStyleRes = 0, + ) { + val onTertiaryFixed = getColor(/* index= */ 0, /* defValue= */ 0) + val tertiaryFixed = getColor(/* index= */ 1, /* defValue= */ 0) + val tertiaryFixedDim = getColor(/* index= */ 2, /* defValue= */ 0) + val taskInfo = (captionState as CaptionState.AppHandle).runningTaskInfo - /** Show tooltip that points to app chip button and educates user on how to exit desktop mode */ - private suspend fun showExitWindowingTooltip(tooltipColorScheme: TooltipColorScheme) { - windowDecorCaptionHandleRepository.captionStateFlow - // After the previous tooltip was dismissed, wait for 400 ms and see if the user entered - // desktop mode. - .timeout(APP_HANDLE_EDUCATION_TIMEOUT_MILLIS.milliseconds) - .catchTimeoutAndLog { - // TODO: b/341320146 - Log previous tooltip was dismissed - } - // Wait for few milliseconds before emitting the latest state. - .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS) - .filter { captionState -> - // Filter out states when app header is not visible or expanded. - captionState is CaptionState.AppHeader && !captionState.isHeaderMenuExpanded - } - // Before showing this tooltip, stop listening to further emissions to avoid accidentally - // showing the same tooltip on future emissions. - .take(1) - .flowOn(backgroundDispatcher) - .collectLatest { captionState -> - captionState as CaptionState.AppHeader - val globalAppChipBounds = captionState.globalAppChipBounds - val tooltipGlobalCoordinates = - Point( - globalAppChipBounds.right, - globalAppChipBounds.top + globalAppChipBounds.height() / 2) - // Populate information important to inflate exit desktop mode education tooltip. - val exitWindowingTooltipConfig = - TooltipEducationViewConfig( - tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, - tooltipColorScheme = tooltipColorScheme, - tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, - tooltipText = getString(R.string.windowing_desktop_mode_exit_education_tooltip), - arrowDirection = - DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, - onDismissAction = {}, - onEducationClickAction = { - openHandleMenuCallback(captionState.runningTaskInfo.taskId) - }, - ) - windowingEducationViewController.showEducationTooltip( - taskId = captionState.runningTaskInfo.taskId, - tooltipViewConfig = exitWindowingTooltipConfig, - ) + val tooltipContainerColor = + if (decorThemeUtil.getAppTheme(taskInfo) == Theme.LIGHT) { + tertiaryFixed + } else { + tertiaryFixedDim + } + return TooltipColorScheme(tooltipContainerColor, onTertiaryFixed, onTertiaryFixed) } - } + return TooltipColorScheme(0, 0, 0) + } - private fun tooltipColorScheme(captionState: CaptionState): TooltipColorScheme { - context.withStyledAttributes( - set = null, - attrs = - intArrayOf( - com.android.internal.R.attr.materialColorOnTertiaryFixed, - com.android.internal.R.attr.materialColorTertiaryFixed, - com.android.internal.R.attr.materialColorTertiaryFixedDim), - defStyleAttr = 0, - defStyleRes = 0) { - val onTertiaryFixed = getColor(/* index= */ 0, /* defValue= */ 0) - val tertiaryFixed = getColor(/* index= */ 1, /* defValue= */ 0) - val tertiaryFixedDim = getColor(/* index= */ 2, /* defValue= */ 0) - val taskInfo = (captionState as CaptionState.AppHandle).runningTaskInfo + /** + * Setup callbacks for app handle education tooltips. + * + * @param openHandleMenuCallback callback invoked to open app handle menu or app chip menu. + * @param toDesktopModeCallback callback invoked to move task into desktop mode. + */ + fun setAppHandleEducationTooltipCallbacks( + openHandleMenuCallback: (taskId: Int) -> Unit, + toDesktopModeCallback: (taskId: Int, DesktopModeTransitionSource) -> Unit, + ) { + this.openHandleMenuCallback = openHandleMenuCallback + this.toDesktopModeCallback = toDesktopModeCallback + } - val tooltipContainerColor = - if (decorThemeUtil.getAppTheme(taskInfo) == Theme.LIGHT) { - tertiaryFixed - } else { - tertiaryFixedDim - } - return TooltipColorScheme(tooltipContainerColor, onTertiaryFixed, onTertiaryFixed) + private inline fun <T> Flow<T>.catchTimeoutAndLog(crossinline block: () -> Unit) = + catch { exception -> + if (exception is TimeoutCancellationException) block() else throw exception } - return TooltipColorScheme(0, 0, 0) - } - /** - * Setup callbacks for app handle education tooltips. - * - * @param openHandleMenuCallback callback invoked to open app handle menu or app chip menu. - * @param toDesktopModeCallback callback invoked to move task into desktop mode. - */ - fun setAppHandleEducationTooltipCallbacks( - openHandleMenuCallback: (taskId: Int) -> Unit, - toDesktopModeCallback: (taskId: Int, DesktopModeTransitionSource) -> Unit - ) { - this.openHandleMenuCallback = openHandleMenuCallback - this.toDesktopModeCallback = toDesktopModeCallback - } - - private inline fun <T> Flow<T>.catchTimeoutAndLog(crossinline block: () -> Unit) = - catch { exception -> - if (exception is TimeoutCancellationException) block() else throw exception - } - - private fun launchWithExceptionHandling(block: suspend () -> Unit) = - applicationCoroutineScope.launch { - try { - block() - } catch (e: Throwable) { - Slog.e(TAG, "Error: ", e) + private fun launchWithExceptionHandling(block: suspend () -> Unit) = + applicationCoroutineScope.launch { + try { + block() + } catch (e: Throwable) { + Slog.e(TAG, "Error: ", e) + } } - } - /** - * Listens to the changes to [WindowingEducationProto#hasEducationViewedTimestampMillis()] in - * datastore proto object. - * - * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this flow will always emit false. That means - * it will emit education has not been viewed yet always. - */ - private fun isEducationViewedFlow(): Flow<Boolean> = - appHandleEducationDatastoreRepository.dataStoreFlow - .map { preferences -> - preferences.hasEducationViewedTimestampMillis() && !SHOULD_OVERRIDE_EDUCATION_CONDITIONS - } - .distinctUntilChanged() + /** + * Listens to the changes to [WindowingEducationProto#hasAppHandleHintViewedTimestampMillis()] + * in datastore proto object. + * + * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this flow will always emit false. That + * means it will always emit app handle hint has not been viewed yet. + */ + private fun isAppHandleHintViewedFlow(): Flow<Boolean> = + appHandleEducationDatastoreRepository.dataStoreFlow + .map { preferences -> + preferences.hasAppHandleHintViewedTimestampMillis() && + !SHOULD_OVERRIDE_EDUCATION_CONDITIONS + } + .distinctUntilChanged() - /** - * Listens to the changes to [WindowingEducationProto#hasFeatureUsedTimestampMillis()] in - * datastore proto object. - */ - private suspend fun isFeatureUsed(): Boolean = - appHandleEducationDatastoreRepository.dataStoreFlow.first().hasFeatureUsedTimestampMillis() + /** + * Listens to the changes to [WindowingEducationProto#hasAppHandleHintUsedTimestampMillis()] in + * datastore proto object. + */ + private suspend fun isAppHandleHintUsed(): Boolean = + appHandleEducationDatastoreRepository.dataStoreFlow + .first() + .hasAppHandleHintUsedTimestampMillis() - private fun getSize(@DimenRes resourceId: Int): Int { - if (resourceId == Resources.ID_NULL) return 0 - return context.resources.getDimensionPixelSize(resourceId) - } + private fun getSize(@DimenRes resourceId: Int): Int { + if (resourceId == Resources.ID_NULL) return 0 + return context.resources.getDimensionPixelSize(resourceId) + } - private fun getString(@StringRes resId: Int): String = context.resources.getString(resId) + private fun getString(@StringRes resId: Int): String = context.resources.getString(resId) - companion object { - const val TAG = "AppHandleEducationController" - val APP_HANDLE_EDUCATION_DELAY_MILLIS: Long - get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L) + companion object { + const val TAG = "AppHandleEducationController" + val APP_HANDLE_EDUCATION_DELAY_MILLIS: Long + get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L) - val APP_HANDLE_EDUCATION_TIMEOUT_MILLIS: Long - get() = SystemProperties.getLong("persist.windowing_app_handle_education_timeout", 400L) + val APP_HANDLE_EDUCATION_TIMEOUT_MILLIS: Long + get() = SystemProperties.getLong("persist.windowing_app_handle_education_timeout", 400L) - val SHOULD_OVERRIDE_EDUCATION_CONDITIONS: Boolean - get() = - SystemProperties.getBoolean( - "persist.desktop_windowing_app_handle_education_override_conditions", false) - } + val SHOULD_OVERRIDE_EDUCATION_CONDITIONS: Boolean + get() = + SystemProperties.getBoolean( + "persist.desktop_windowing_app_handle_education_override_conditions", + false, + ) + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt index 144370d76060..9990846fc92e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt @@ -32,106 +32,114 @@ import java.time.Duration /** Filters incoming app handle education triggers based on set conditions. */ class AppHandleEducationFilter( private val context: Context, - private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository + private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository, ) { - private val usageStatsManager = - context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager - - /** - * Returns true if conditions to show app handle education are met, returns false otherwise. - * - * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this method will always return - * ![captionState.isHandleMenuExpanded]. - */ - suspend fun shouldShowAppHandleEducation(captionState: CaptionState): Boolean { - if ((captionState as CaptionState.AppHandle).isHandleMenuExpanded) return false - if (SHOULD_OVERRIDE_EDUCATION_CONDITIONS) return true - - val focusAppPackageName = - captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false - val windowingEducationProto = appHandleEducationDatastoreRepository.windowingEducationProto() - - return isFocusAppInAllowlist(focusAppPackageName) && - !isOtherEducationShowing() && - hasSufficientTimeSinceSetup() && - !isEducationViewedBefore(windowingEducationProto) && - !isFeatureUsedBefore(windowingEducationProto) && - hasMinAppUsage(windowingEducationProto, focusAppPackageName) - } - - private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean = - focusAppPackageName in - context.resources.getStringArray( - R.array.desktop_windowing_app_handle_education_allowlist_apps) - - // TODO: b/350953004 - Add checks based on App compat - // TODO: b/350951797 - Add checks based on PKT tips education - private fun isOtherEducationShowing(): Boolean = isTaskbarEducationShowing() - - private fun isTaskbarEducationShowing(): Boolean = - Secure.getInt(context.contentResolver, Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0) == 1 - - private fun hasSufficientTimeSinceSetup(): Boolean = - Duration.ofMillis(SystemClock.elapsedRealtime()) > - convertIntegerResourceToDuration( - R.integer.desktop_windowing_education_required_time_since_setup_seconds) - - private fun isEducationViewedBefore(windowingEducationProto: WindowingEducationProto): Boolean = - windowingEducationProto.hasEducationViewedTimestampMillis() - - private fun isFeatureUsedBefore(windowingEducationProto: WindowingEducationProto): Boolean = - windowingEducationProto.hasFeatureUsedTimestampMillis() - - private suspend fun hasMinAppUsage( - windowingEducationProto: WindowingEducationProto, - focusAppPackageName: String - ): Boolean = - (launchCountByPackageName(windowingEducationProto)[focusAppPackageName] ?: 0) >= - context.resources.getInteger(R.integer.desktop_windowing_education_min_app_launch_count) - - private suspend fun launchCountByPackageName( - windowingEducationProto: WindowingEducationProto - ): Map<String, Int> = - if (isAppUsageCacheStale(windowingEducationProto)) { - // Query and return user stats, update cache in datastore - getAndCacheAppUsageStats() - } else { - // Return cached usage stats - windowingEducationProto.appHandleEducation.appUsageStatsMap - } - - private fun isAppUsageCacheStale(windowingEducationProto: WindowingEducationProto): Boolean { - val currentTime = currentTimeInDuration() - val lastUpdateTime = - Duration.ofMillis( - windowingEducationProto.appHandleEducation.appUsageStatsLastUpdateTimestampMillis) - val appUsageStatsCachingInterval = - convertIntegerResourceToDuration( - R.integer.desktop_windowing_education_app_usage_cache_interval_seconds) - return (currentTime - lastUpdateTime) > appUsageStatsCachingInterval - } - - private suspend fun getAndCacheAppUsageStats(): Map<String, Int> { - val currentTime = currentTimeInDuration() - val appUsageStats = queryAppUsageStats() - appHandleEducationDatastoreRepository.updateAppUsageStats(appUsageStats, currentTime) - return appUsageStats - } - - private fun queryAppUsageStats(): Map<String, Int> { - val endTime = currentTimeInDuration() - val appLaunchInterval = - convertIntegerResourceToDuration( - R.integer.desktop_windowing_education_app_launch_interval_seconds) - val startTime = endTime - appLaunchInterval - - return usageStatsManager - .queryAndAggregateUsageStats(startTime.toMillis(), endTime.toMillis()) - .mapValues { it.value.appLaunchCount } - } - - private fun convertIntegerResourceToDuration(@IntegerRes resourceId: Int): Duration = - Duration.ofSeconds(context.resources.getInteger(resourceId).toLong()) - - private fun currentTimeInDuration(): Duration = Duration.ofMillis(System.currentTimeMillis()) + private val usageStatsManager = + context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager + + /** + * Returns true if conditions to show app handle education are met, returns false otherwise. + * + * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this method will always return + * ![captionState.isHandleMenuExpanded]. + */ + suspend fun shouldShowAppHandleEducation(captionState: CaptionState): Boolean { + if ((captionState as CaptionState.AppHandle).isHandleMenuExpanded) return false + if (SHOULD_OVERRIDE_EDUCATION_CONDITIONS) return true + + val focusAppPackageName = + captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false + val windowingEducationProto = + appHandleEducationDatastoreRepository.windowingEducationProto() + + return isFocusAppInAllowlist(focusAppPackageName) && + !isOtherEducationShowing() && + hasSufficientTimeSinceSetup() && + !isAppHandleHintViewedBefore(windowingEducationProto) && + !isAppHandleHintUsedBefore(windowingEducationProto) && + hasMinAppUsage(windowingEducationProto, focusAppPackageName) + } + + private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean = + focusAppPackageName in + context.resources.getStringArray( + R.array.desktop_windowing_app_handle_education_allowlist_apps + ) + + // TODO: b/350953004 - Add checks based on App compat + // TODO: b/350951797 - Add checks based on PKT tips education + private fun isOtherEducationShowing(): Boolean = isTaskbarEducationShowing() + + private fun isTaskbarEducationShowing(): Boolean = + Secure.getInt(context.contentResolver, Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0) == 1 + + private fun hasSufficientTimeSinceSetup(): Boolean = + Duration.ofMillis(SystemClock.elapsedRealtime()) > + convertIntegerResourceToDuration( + R.integer.desktop_windowing_education_required_time_since_setup_seconds + ) + + private fun isAppHandleHintViewedBefore( + windowingEducationProto: WindowingEducationProto + ): Boolean = windowingEducationProto.hasAppHandleHintViewedTimestampMillis() + + private fun isAppHandleHintUsedBefore( + windowingEducationProto: WindowingEducationProto + ): Boolean = windowingEducationProto.hasAppHandleHintUsedTimestampMillis() + + private suspend fun hasMinAppUsage( + windowingEducationProto: WindowingEducationProto, + focusAppPackageName: String, + ): Boolean = + (launchCountByPackageName(windowingEducationProto)[focusAppPackageName] ?: 0) >= + context.resources.getInteger(R.integer.desktop_windowing_education_min_app_launch_count) + + private suspend fun launchCountByPackageName( + windowingEducationProto: WindowingEducationProto + ): Map<String, Int> = + if (isAppUsageCacheStale(windowingEducationProto)) { + // Query and return user stats, update cache in datastore + getAndCacheAppUsageStats() + } else { + // Return cached usage stats + windowingEducationProto.appHandleEducation.appUsageStatsMap + } + + private fun isAppUsageCacheStale(windowingEducationProto: WindowingEducationProto): Boolean { + val currentTime = currentTimeInDuration() + val lastUpdateTime = + Duration.ofMillis( + windowingEducationProto.appHandleEducation.appUsageStatsLastUpdateTimestampMillis + ) + val appUsageStatsCachingInterval = + convertIntegerResourceToDuration( + R.integer.desktop_windowing_education_app_usage_cache_interval_seconds + ) + return (currentTime - lastUpdateTime) > appUsageStatsCachingInterval + } + + private suspend fun getAndCacheAppUsageStats(): Map<String, Int> { + val currentTime = currentTimeInDuration() + val appUsageStats = queryAppUsageStats() + appHandleEducationDatastoreRepository.updateAppUsageStats(appUsageStats, currentTime) + return appUsageStats + } + + private fun queryAppUsageStats(): Map<String, Int> { + val endTime = currentTimeInDuration() + val appLaunchInterval = + convertIntegerResourceToDuration( + R.integer.desktop_windowing_education_app_launch_interval_seconds + ) + val startTime = endTime - appLaunchInterval + + return usageStatsManager + .queryAndAggregateUsageStats(startTime.toMillis(), endTime.toMillis()) + .mapValues { it.value.appLaunchCount } + } + + private fun convertIntegerResourceToDuration(@IntegerRes resourceId: Int): Duration = + Duration.ofSeconds(context.resources.getInteger(resourceId).toLong()) + + private fun currentTimeInDuration(): Duration = Duration.ofMillis(System.currentTimeMillis()) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt index 693da81ec4a0..bfe1b12c9605 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt @@ -53,8 +53,8 @@ import kotlinx.coroutines.launch /** * Controls App-to-Web education end to end. * - * Listen to usages of App-to-Web, calls an api to check if the education - * should be shown and controls education UI. + * Listen to usages of App-to-Web, calls an api to check if the education should be shown and + * controls education UI. */ @OptIn(kotlinx.coroutines.FlowPreview::class) @kotlinx.coroutines.ExperimentalCoroutinesApi @@ -88,8 +88,9 @@ class AppToWebEducationController( .debounce(APP_TO_WEB_EDUCATION_DELAY_MILLIS) .filter { captionState -> captionState !is CaptionState.NoCaption && - appToWebEducationFilter - .shouldShowAppToWebEducation(captionState) + appToWebEducationFilter.shouldShowAppToWebEducation( + captionState + ) } } } @@ -104,12 +105,12 @@ class AppToWebEducationController( applicationCoroutineScope.launch { if (isFeatureUsed()) return@launch - windowDecorCaptionHandleRepository.appToWebUsageFlow - .collect { - // If user utilizes App-to-Web, mark user has used the feature - appToWebEducationDatastoreRepository - .updateFeatureUsedTimestampMillis(isViewed = true) - } + windowDecorCaptionHandleRepository.appToWebUsageFlow.collect { + // If user utilizes App-to-Web, mark user has used the feature + appToWebEducationDatastoreRepository.updateFeatureUsedTimestampMillis( + isViewed = true + ) + } } } } @@ -126,10 +127,8 @@ class AppToWebEducationController( val appHandleBounds = captionState.globalAppHandleBounds val educationWidth = loadDimensionPixelSize(R.dimen.desktop_windowing_education_promo_width) - educationGlobalCoordinates = Point( - appHandleBounds.centerX() - educationWidth / 2, - appHandleBounds.bottom - ) + educationGlobalCoordinates = + Point(appHandleBounds.centerX() - educationWidth / 2, appHandleBounds.bottom) taskId = captionState.runningTaskInfo.taskId } @@ -152,19 +151,22 @@ class AppToWebEducationController( viewGlobalCoordinates = educationGlobalCoordinates, educationText = getString(R.string.desktop_windowing_app_to_web_education_text), widthId = R.dimen.desktop_windowing_education_promo_width, - heightId = R.dimen.desktop_windowing_education_promo_height + heightId = R.dimen.desktop_windowing_education_promo_height, ) windowingEducationViewController.showEducation( - viewConfig = educationConfig, taskId = taskId) + viewConfig = educationConfig, + taskId = taskId, + ) } private fun educationColorScheme(captionState: CaptionState): EducationColorScheme? { - val taskInfo: RunningTaskInfo = when (captionState) { - is CaptionState.AppHandle -> captionState.runningTaskInfo - is CaptionState.AppHeader -> captionState.runningTaskInfo - else -> return null - } + val taskInfo: RunningTaskInfo = + when (captionState) { + is CaptionState.AppHandle -> captionState.runningTaskInfo + is CaptionState.AppHeader -> captionState.runningTaskInfo + else -> return null + } val colorScheme = decorThemeUtil.getColorScheme(taskInfo) val tooltipContainerColor = colorScheme.surfaceBright.toArgb() @@ -178,8 +180,7 @@ class AppToWebEducationController( */ private fun isEducationViewLimitReachedFlow(): Flow<Boolean> = appToWebEducationDatastoreRepository.dataStoreFlow - .map { preferences -> - appToWebEducationFilter.isEducationViewLimitReached(preferences)} + .map { preferences -> appToWebEducationFilter.isEducationViewLimitReached(preferences) } .distinctUntilChanged() /** @@ -199,9 +200,6 @@ class AppToWebEducationController( companion object { const val TAG = "AppToWebEducationController" val APP_TO_WEB_EDUCATION_DELAY_MILLIS: Long - get() = SystemProperties.getLong( - "persist.windowing_app_handle_education_delay", - 3000L - ) + get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationFilter.kt index feee6ed86da1..e272b54dd907 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationFilter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationFilter.kt @@ -30,39 +30,41 @@ import java.time.Duration /** Filters incoming App-to-Web education triggers based on set conditions. */ class AppToWebEducationFilter( private val context: Context, - private val appToWebEducationDatastoreRepository: AppToWebEducationDatastoreRepository + private val appToWebEducationDatastoreRepository: AppToWebEducationDatastoreRepository, ) { /** Returns true if conditions to show App-to-web education are met, returns false otherwise. */ suspend fun shouldShowAppToWebEducation(captionState: CaptionState): Boolean { - val (taskInfo: RunningTaskInfo, isCapturedLinkAvailable: Boolean) = when (captionState) { - is CaptionState.AppHandle -> - Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable) - is CaptionState.AppHeader -> - Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable) - else -> return false - } + val (taskInfo: RunningTaskInfo, isCapturedLinkAvailable: Boolean) = + when (captionState) { + is CaptionState.AppHandle -> + Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable) + is CaptionState.AppHeader -> + Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable) + else -> return false + } val focusAppPackageName = taskInfo.topActivityInfo?.packageName ?: return false val windowingEducationProto = appToWebEducationDatastoreRepository.windowingEducationProto() return !isOtherEducationShowing() && - !isEducationViewLimitReached(windowingEducationProto) && - hasSufficientTimeSinceSetup() && - !isFeatureUsedBefore(windowingEducationProto) && - isCapturedLinkAvailable && - isFocusAppInAllowlist(focusAppPackageName) + !isEducationViewLimitReached(windowingEducationProto) && + hasSufficientTimeSinceSetup() && + !isFeatureUsedBefore(windowingEducationProto) && + isCapturedLinkAvailable && + isFocusAppInAllowlist(focusAppPackageName) } private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean = focusAppPackageName in - context.resources.getStringArray( - R.array.desktop_windowing_app_to_web_education_allowlist_apps) + context.resources.getStringArray( + R.array.desktop_windowing_app_to_web_education_allowlist_apps + ) // TODO: b/350953004 - Add checks based on App compat // TODO: b/350951797 - Add checks based on PKT tips education - private fun isOtherEducationShowing(): Boolean = isTaskbarEducationShowing() || - isCompatUiEducationShowing() + private fun isOtherEducationShowing(): Boolean = + isTaskbarEducationShowing() || isCompatUiEducationShowing() private fun isTaskbarEducationShowing(): Boolean = Secure.getInt(context.contentResolver, Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0) == 1 @@ -72,13 +74,14 @@ class AppToWebEducationFilter( private fun hasSufficientTimeSinceSetup(): Boolean = Duration.ofMillis(SystemClock.elapsedRealtime()) > - convertIntegerResourceToDuration( - R.integer.desktop_windowing_education_required_time_since_setup_seconds) + convertIntegerResourceToDuration( + R.integer.desktop_windowing_education_required_time_since_setup_seconds + ) /** Returns true if education is viewed maximum amount of times it should be shown. */ fun isEducationViewLimitReached(windowingEducationProto: WindowingEducationProto): Boolean = windowingEducationProto.getAppToWebEducation().getEducationShownCount() >= - MAXIMUM_TIMES_EDUCATION_SHOWN + MAXIMUM_TIMES_EDUCATION_SHOWN private fun isFeatureUsedBefore(windowingEducationProto: WindowingEducationProto): Boolean = windowingEducationProto.hasFeatureUsedTimestampMillis() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt index d21b208df482..3e120b09a0b6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt @@ -42,101 +42,109 @@ import kotlinx.coroutines.flow.first class AppHandleEducationDatastoreRepository @VisibleForTesting constructor(private val dataStore: DataStore<WindowingEducationProto>) { - constructor( - context: Context - ) : this( - DataStoreFactory.create( - serializer = WindowingEducationProtoSerializer, - produceFile = { context.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_FILEPATH) })) + constructor( + context: Context + ) : this( + DataStoreFactory.create( + serializer = WindowingEducationProtoSerializer, + produceFile = { context.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_FILEPATH) }, + ) + ) - /** Provides dataStore.data flow and handles exceptions thrown during collection */ - val dataStoreFlow: Flow<WindowingEducationProto> = - dataStore.data.catch { exception -> - // dataStore.data throws an IOException when an error is encountered when reading data - if (exception is IOException) { - Log.e( - TAG, - "Error in reading app handle education related data from datastore, data is " + - "stored in a file named $APP_HANDLE_EDUCATION_DATASTORE_FILEPATH", - exception) - } else { - throw exception + /** Provides dataStore.data flow and handles exceptions thrown during collection */ + val dataStoreFlow: Flow<WindowingEducationProto> = + dataStore.data.catch { exception -> + // dataStore.data throws an IOException when an error is encountered when reading data + if (exception is IOException) { + Log.e( + TAG, + "Error in reading app handle education related data from datastore, data is " + + "stored in a file named $APP_HANDLE_EDUCATION_DATASTORE_FILEPATH", + exception, + ) + } else { + throw exception + } } - } - /** - * Reads and returns the [WindowingEducationProto] Proto object from the DataStore. If the - * DataStore is empty or there's an error reading, it returns the default value of Proto. - */ - suspend fun windowingEducationProto(): WindowingEducationProto = dataStoreFlow.first() + /** + * Reads and returns the [WindowingEducationProto] Proto object from the DataStore. If the + * DataStore is empty or there's an error reading, it returns the default value of Proto. + */ + suspend fun windowingEducationProto(): WindowingEducationProto = dataStoreFlow.first() - /** - * Updates [WindowingEducationProto.educationViewedTimestampMillis_] field in datastore with - * current timestamp if [isViewed] is true, if not then clears the field. - */ - suspend fun updateEducationViewedTimestampMillis(isViewed: Boolean) { - dataStore.updateData { preferences -> - if (isViewed) { - preferences - .toBuilder() - .setEducationViewedTimestampMillis(System.currentTimeMillis()) - .build() - } else { - preferences.toBuilder().clearEducationViewedTimestampMillis().build() - } + /** + * Updates [WindowingEducationProto.appHandleHintViewedTimestampMillis_] field in datastore with + * current timestamp if [isViewed] is true, if not then clears the field. + */ + suspend fun updateAppHandleHintViewedTimestampMillis(isViewed: Boolean) { + dataStore.updateData { preferences -> + if (isViewed) { + preferences + .toBuilder() + .setAppHandleHintViewedTimestampMillis(System.currentTimeMillis()) + .build() + } else { + preferences.toBuilder().clearAppHandleHintViewedTimestampMillis().build() + } + } } - } - /** - * Updates [WindowingEducationProto.featureUsedTimestampMillis_] field in datastore with current - * timestamp if [isViewed] is true, if not then clears the field. - */ - suspend fun updateFeatureUsedTimestampMillis(isViewed: Boolean) { - dataStore.updateData { preferences -> - if (isViewed) { - preferences.toBuilder().setFeatureUsedTimestampMillis(System.currentTimeMillis()).build() - } else { - preferences.toBuilder().clearFeatureUsedTimestampMillis().build() - } + /** + * Updates [WindowingEducationProto.appHandleHintUsedTimestampMillis_] field in datastore with + * current timestamp if [isViewed] is true, if not then clears the field. + */ + suspend fun updateAppHandleHintUsedTimestampMillis(isViewed: Boolean) { + dataStore.updateData { preferences -> + if (isViewed) { + preferences + .toBuilder() + .setAppHandleHintUsedTimestampMillis(System.currentTimeMillis()) + .build() + } else { + preferences.toBuilder().clearAppHandleHintUsedTimestampMillis().build() + } + } } - } - /** - * Updates [AppHandleEducation.appUsageStats] and - * [AppHandleEducation.appUsageStatsLastUpdateTimestampMillis] fields in datastore with - * [appUsageStats] and [appUsageStatsLastUpdateTimestamp]. - */ - suspend fun updateAppUsageStats( - appUsageStats: Map<String, Int>, - appUsageStatsLastUpdateTimestamp: Duration - ) { - val currentAppHandleProto = windowingEducationProto().appHandleEducation.toBuilder() - currentAppHandleProto - .putAllAppUsageStats(appUsageStats) - .setAppUsageStatsLastUpdateTimestampMillis(appUsageStatsLastUpdateTimestamp.toMillis()) - dataStore.updateData { preferences: WindowingEducationProto -> - preferences.toBuilder().setAppHandleEducation(currentAppHandleProto).build() + /** + * Updates [AppHandleEducation.appUsageStats] and + * [AppHandleEducation.appUsageStatsLastUpdateTimestampMillis] fields in datastore with + * [appUsageStats] and [appUsageStatsLastUpdateTimestamp]. + */ + suspend fun updateAppUsageStats( + appUsageStats: Map<String, Int>, + appUsageStatsLastUpdateTimestamp: Duration, + ) { + val currentAppHandleProto = windowingEducationProto().appHandleEducation.toBuilder() + currentAppHandleProto + .putAllAppUsageStats(appUsageStats) + .setAppUsageStatsLastUpdateTimestampMillis(appUsageStatsLastUpdateTimestamp.toMillis()) + dataStore.updateData { preferences: WindowingEducationProto -> + preferences.toBuilder().setAppHandleEducation(currentAppHandleProto).build() + } } - } - companion object { - private const val TAG = "AppHandleEducationDatastoreRepository" - private const val APP_HANDLE_EDUCATION_DATASTORE_FILEPATH = "app_handle_education.pb" + companion object { + private const val TAG = "AppHandleEducationDatastoreRepository" + private const val APP_HANDLE_EDUCATION_DATASTORE_FILEPATH = "app_handle_education.pb" - object WindowingEducationProtoSerializer : Serializer<WindowingEducationProto> { + object WindowingEducationProtoSerializer : Serializer<WindowingEducationProto> { - override val defaultValue: WindowingEducationProto = - WindowingEducationProto.getDefaultInstance() + override val defaultValue: WindowingEducationProto = + WindowingEducationProto.getDefaultInstance() - override suspend fun readFrom(input: InputStream): WindowingEducationProto = - try { - WindowingEducationProto.parseFrom(input) - } catch (exception: InvalidProtocolBufferException) { - throw CorruptionException("Cannot read proto.", exception) - } + override suspend fun readFrom(input: InputStream): WindowingEducationProto = + try { + WindowingEducationProto.parseFrom(input) + } catch (exception: InvalidProtocolBufferException) { + throw CorruptionException("Cannot read proto.", exception) + } - override suspend fun writeTo(windowingProto: WindowingEducationProto, output: OutputStream) = - windowingProto.writeTo(output) + override suspend fun writeTo( + windowingProto: WindowingEducationProto, + output: OutputStream, + ) = windowingProto.writeTo(output) + } } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppToWebEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppToWebEducationDatastoreRepository.kt index 8be6e6dff6fe..e5ad901d1435 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppToWebEducationDatastoreRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppToWebEducationDatastoreRepository.kt @@ -25,12 +25,12 @@ import androidx.datastore.core.Serializer import androidx.datastore.dataStoreFile import com.android.framework.protobuf.InvalidProtocolBufferException import com.android.internal.annotations.VisibleForTesting -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.first import java.io.IOException import java.io.InputStream import java.io.OutputStream +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.first /** Updates data in App-to-Web's education datastore. */ class AppToWebEducationDatastoreRepository @@ -41,7 +41,9 @@ constructor(private val dataStore: DataStore<WindowingEducationProto>) { ) : this( DataStoreFactory.create( serializer = WindowingEducationProtoSerializer, - produceFile = { context.dataStoreFile(APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH) })) + produceFile = { context.dataStoreFile(APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH) }, + ) + ) /** Provides dataStore.data flow and handles exceptions thrown during collection */ val dataStoreFlow: Flow<WindowingEducationProto> = @@ -51,8 +53,10 @@ constructor(private val dataStore: DataStore<WindowingEducationProto>) { Slog.e( TAG, "Error in reading App-to-Web education related data from datastore," + - "data is stored in a file named" + - "$APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH", exception) + "data is stored in a file named" + + "$APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH", + exception, + ) } else { throw exception } @@ -72,26 +76,26 @@ constructor(private val dataStore: DataStore<WindowingEducationProto>) { dataStore.updateData { preferences -> if (isViewed) { preferences - .toBuilder().setFeatureUsedTimestampMillis(System.currentTimeMillis()).build() + .toBuilder() + .setFeatureUsedTimestampMillis(System.currentTimeMillis()) + .build() } else { preferences.toBuilder().clearFeatureUsedTimestampMillis().build() } } } - /** - * Increases [AppToWebEducation.educationShownCount] field by one. - */ + /** Increases [AppToWebEducation.educationShownCount] field by one. */ suspend fun updateEducationShownCount() { val currentAppHandleProto = windowingEducationProto().appToWebEducation.toBuilder() - currentAppHandleProto - .setEducationShownCount(currentAppHandleProto.getEducationShownCount() + 1) + currentAppHandleProto.setEducationShownCount( + currentAppHandleProto.getEducationShownCount() + 1 + ) dataStore.updateData { preferences -> preferences.toBuilder().setAppToWebEducation(currentAppHandleProto).build() } } - companion object { private const val TAG = "AppToWebEducationDatastoreRepository" private const val APP_TO_WEB_EDUCATION_DATASTORE_FILEPATH = "app_to_web_education.pb" @@ -110,7 +114,7 @@ constructor(private val dataStore: DataStore<WindowingEducationProto>) { override suspend fun writeTo( windowingProto: WindowingEducationProto, - output: OutputStream + output: OutputStream, ) = windowingProto.writeTo(output) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto index 4cddd01ee96b..0c4d562b8d55 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto @@ -22,9 +22,19 @@ option java_multiple_files = true; // Desktop Windowing education data message WindowingEducationProto { // Timestamp in milliseconds of when the education was last viewed. - optional int64 education_viewed_timestamp_millis = 1; + optional int64 education_viewed_timestamp_millis = 1 [deprecated=true]; // Timestamp in milliseconds of when the feature was last used. - optional int64 feature_used_timestamp_millis = 2; + optional int64 feature_used_timestamp_millis = 2 [deprecated=true]; + + // Timestamp in milliseconds of when the app handle hint was last viewed. + optional int64 app_handle_hint_viewed_timestamp_millis = 5; + // Timestamp in milliseconds of when the app handle hint was last used. + optional int64 app_handle_hint_used_timestamp_millis = 6; + // Timestamp in milliseconds of when the enter desktop mode hint was last viewed. + optional int64 enter_desktop_mode_hint_viewed_timestamp_millis = 7; + // Timestamp in milliseconds of when the exit desktop mode hint was last viewed. + optional int64 exit_desktop_mode_hint_viewed_timestamp_millis = 8; + oneof education_data { // Fields specific to app handle education AppHandleEducation app_handle_education = 3; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index e4f83333edbf..4c316de98744 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java @@ -64,6 +64,7 @@ import com.android.wm.shell.shared.annotations.ExternalThread; import com.android.wm.shell.sysui.KeyguardChangeListener; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.FocusTransitionObserver; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions.TransitionFinishCallback; @@ -89,6 +90,7 @@ public class KeyguardTransitionHandler private final ArrayMap<IBinder, StartedTransition> mStartedTransitions = new ArrayMap<>(); private final TaskStackListenerImpl mTaskStackListener; + private final FocusTransitionObserver mFocusTransitionObserver; /** * Local IRemoteTransition implementations registered by the keyguard service. @@ -129,7 +131,8 @@ public class KeyguardTransitionHandler @NonNull Transitions transitions, @NonNull TaskStackListenerImpl taskStackListener, @NonNull Handler mainHandler, - @NonNull ShellExecutor mainExecutor) { + @NonNull ShellExecutor mainExecutor, + @NonNull FocusTransitionObserver focusTransitionObserver) { mTransitions = transitions; mShellController = shellController; mDisplayController = displayController; @@ -137,6 +140,7 @@ public class KeyguardTransitionHandler mMainExecutor = mainExecutor; mTaskStackListener = taskStackListener; shellInit.addInitCallback(this::onInit, this); + mFocusTransitionObserver = focusTransitionObserver; } private void onInit() { @@ -396,7 +400,8 @@ public class KeyguardTransitionHandler final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (taskInfo != null && taskInfo.taskId != INVALID_TASK_ID && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM - && taskInfo.isFocused && change.getContainer() != null) { + && mFocusTransitionObserver.hasGlobalFocus(taskInfo) + && change.getContainer() != null) { wct.setWindowingMode(change.getContainer(), WINDOWING_MODE_FULLSCREEN); wct.setBounds(change.getContainer(), null); return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 30f1948efa2d..fb4afe41e193 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -26,6 +26,8 @@ import static android.util.RotationUtils.rotateBounds; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP; import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; +import static com.android.wm.shell.desktopmode.DesktopModeUtils.calculateInitialBounds; +import static com.android.wm.shell.desktopmode.DesktopTasksController.DESKTOP_MODE_INITIAL_BOUNDS_SCALE; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS; import static com.android.wm.shell.pip.PipAnimationController.FRACTION_START; @@ -152,6 +154,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Nullable private final PipPerfHintController mPipPerfHintController; private final Optional<DesktopRepository> mDesktopRepositoryOptional; private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; + private final DisplayController mDisplayController; protected final ShellTaskOrganizer mTaskOrganizer; protected final ShellExecutor mMainExecutor; @@ -425,6 +428,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipPerfHintController = pipPerfHintControllerOptional.orElse(null); mDesktopRepositoryOptional = desktopRepositoryOptional; mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; + mDisplayController = displayController; mTaskOrganizer = shellTaskOrganizer; mMainExecutor = mainExecutor; @@ -754,19 +758,30 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, /** Returns the bounds to restore to when exiting PIP mode. */ // TODO(b/377581840): Instead of manually tracking bounds, use bounds from Core. public Rect getExitDestinationBounds() { - if (isPipLaunchedInDesktopMode()) { - final Rect freeformBounds = mDesktopRepositoryOptional.get().removeBoundsBeforeMinimize( + if (isPipExitingToDesktopMode()) { + // If we are exiting PiP while device is in Desktop mode: + // 1) If PiP was entered via Desktop minimize (e.g. via minimize button), restore to the + // previous freeform bounds that is saved in DesktopRepository. + // 2) If PiP was entered through other means (e.g. user swipe up), exit to initial + // freeform bounds. Note that this case has a flicker at the moment (b/379984108). + Rect freeformBounds = mDesktopRepositoryOptional.get().removeBoundsBeforeMinimize( mTaskInfo.taskId); - return Objects.requireNonNullElseGet(freeformBounds, mPipBoundsState::getDisplayBounds); + return freeformBounds != null + ? freeformBounds + : calculateInitialBounds( + mDisplayController.getDisplayLayout(mTaskInfo.displayId), + mTaskInfo, + DESKTOP_MODE_INITIAL_BOUNDS_SCALE); } return mPipBoundsState.getDisplayBounds(); } - /** Returns whether PiP was launched while in desktop mode. */ - // TODO(377581840): Update this check to include non-minimized cases, e.g. split to PiP etc. - private boolean isPipLaunchedInDesktopMode() { + /** Returns whether PiP is exiting while we're in desktop mode. */ + // TODO(b/377581840): Update this check to include non-minimized cases, e.g. split to PiP etc. + private boolean isPipExitingToDesktopMode() { return Flags.enableDesktopWindowingPip() && mDesktopRepositoryOptional.isPresent() - && mDesktopRepositoryOptional.get().isMinimizedTask(mTaskInfo.taskId); + && (mDesktopRepositoryOptional.get().getVisibleTaskCount(mTaskInfo.displayId) > 0 + || isDisplayInFreeform()); } private void exitLaunchIntoPipTask(WindowContainerTransaction wct) { @@ -1827,23 +1842,28 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, == SPLIT_POSITION_TOP_OR_LEFT; } + private boolean isDisplayInFreeform() { + final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo( + mTaskInfo.displayId); + if (tdaInfo != null) { + return tdaInfo.configuration.windowConfiguration.getWindowingMode() + == WINDOWING_MODE_FREEFORM; + } + return false; + } + /** * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined * and can be overridden to restore to an alternate windowing mode. */ public int getOutPipWindowingMode() { - final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo( - mTaskInfo.displayId); - - // If PiP was launched while in desktop mode (we should return the task to freeform - // windowing mode): + // If we are exiting PiP while the device is in Desktop mode (the task should expand to + // freeform windowing mode): // 1) If the display windowing mode is freeform, set windowing mode to undefined so it will // resolve the windowing mode to the display's windowing mode. // 2) If the display windowing mode is not freeform, set windowing mode to freeform. - if (tdaInfo != null && isPipLaunchedInDesktopMode()) { - final int displayWindowingMode = - tdaInfo.configuration.windowConfiguration.getWindowingMode(); - if (displayWindowingMode == WINDOWING_MODE_FREEFORM) { + if (isPipExitingToDesktopMode()) { + if (isDisplayInFreeform()) { return WINDOWING_MODE_UNDEFINED; } else { return WINDOWING_MODE_FREEFORM; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index f7aed4401247..72c1ef06806a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -73,6 +73,7 @@ import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.pip.PipUtils; +import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.TransitionUtil; import com.android.wm.shell.shared.pip.PipContentOverlay; @@ -1346,6 +1347,13 @@ public class PipTransition extends PipTransitionController { return true; } + @Override + public boolean isPackageActiveInPip(@Nullable String packageName) { + final TaskInfo inPipTask = mPipOrganizer.getTaskInfo(); + return packageName != null && inPipTask != null && mPipOrganizer.isInPip() + && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent)); + } + private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 79a9ce5212c6..a273822759f6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -312,9 +312,8 @@ public abstract class PipTransitionController implements Transitions.TransitionH /** Whether a particular package is same as current pip package. */ public boolean isPackageActiveInPip(@Nullable String packageName) { - return packageName != null - && mPipBoundsState.getLastPipComponentName() != null - && packageName.equals(mPipBoundsState.getLastPipComponentName().getPackageName()); + // No-op, to be handled differently in PIP1 and PIP2 + return false; } /** Add PiP-related changes to `outWCT` for the given request. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java index 6d2df952ee58..2c5d346224a3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java @@ -254,6 +254,7 @@ public class PipController implements ConfigurationChangeListener, @Override public void onConfigurationChanged(Configuration newConfiguration) { mPipDisplayLayoutState.onConfigurationChanged(); + mPipTouchHandler.onConfigurationChanged(); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java index 65972fb7df48..44cc563eadf4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java @@ -363,12 +363,10 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha mMotionHelper.synchronizePinnedStackBounds(); reloadResources(); - /* - if (mPipTaskOrganizer.isInPip()) { + if (mPipTransitionState.isInPip()) { // Recreate the dismiss target for the new orientation. mPipDismissTargetHandler.createOrUpdateDismissTarget(); } - */ } void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index 2bcbe3013397..8f02c1b157b5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -35,6 +35,7 @@ import android.animation.ValueAnimator; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.PictureInPictureParams; +import android.app.TaskInfo; import android.content.Context; import android.graphics.Point; import android.graphics.PointF; @@ -57,6 +58,7 @@ import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.pip.PipUtils; +import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip2.animation.PipAlphaAnimator; import com.android.wm.shell.pip2.animation.PipEnterAnimator; @@ -74,8 +76,8 @@ public class PipTransition extends PipTransitionController implements private static final String TAG = PipTransition.class.getSimpleName(); // Used when for ENTERING_PIP state update. - private static final String PIP_TASK_TOKEN = "pip_task_token"; private static final String PIP_TASK_LEASH = "pip_task_leash"; + private static final String PIP_TASK_INFO = "pip_task_info"; // Used for PiP CHANGING_BOUNDS state update. static final String PIP_START_TX = "pip_start_tx"; @@ -245,8 +247,8 @@ public class PipTransition extends PipTransitionController implements // Update the PipTransitionState while supplying the PiP leash and token to be cached. Bundle extra = new Bundle(); - extra.putParcelable(PIP_TASK_TOKEN, pipChange.getContainer()); extra.putParcelable(PIP_TASK_LEASH, pipChange.getLeash()); + extra.putParcelable(PIP_TASK_INFO, pipChange.getTaskInfo()); mPipTransitionState.setState(PipTransitionState.ENTERING_PIP, extra); if (isInSwipePipToHomeTransition()) { @@ -371,7 +373,9 @@ public class PipTransition extends PipTransitionController implements // Update the src-rect-hint in params in place, to set up initial animator transform. Rect sourceRectHint = getAdjustedSourceRectHint(info, pipChange, pipActivityChange); - pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint().set(sourceRectHint); + final PictureInPictureParams params = getPipParams(pipChange); + params.copyOnlySet( + new PictureInPictureParams.Builder().setSourceRectHint(sourceRectHint).build()); // Config-at-end transitions need to have their activities transformed before starting // the animation; this makes the buffer seem like it's been updated to final size. @@ -416,9 +420,7 @@ public class PipTransition extends PipTransitionController implements final SurfaceControl pipLeash = getLeash(pipChange); final Rect startBounds = pipChange.getStartAbsBounds(); final Rect endBounds = pipChange.getEndAbsBounds(); - final PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams != null - ? pipChange.getTaskInfo().pictureInPictureParams - : new PictureInPictureParams.Builder().build(); + final PictureInPictureParams params = getPipParams(pipChange); final Rect adjustedSourceRectHint = getAdjustedSourceRectHint(info, pipChange, pipActivityChange); @@ -598,10 +600,10 @@ public class PipTransition extends PipTransitionController implements PictureInPictureParams params = null; if (pipChange.getTaskInfo() != null) { // single activity - params = pipChange.getTaskInfo().pictureInPictureParams; + params = getPipParams(pipChange); } else if (parentBeforePip != null && parentBeforePip.getTaskInfo() != null) { // multi activity - params = parentBeforePip.getTaskInfo().pictureInPictureParams; + params = getPipParams(parentBeforePip); } final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, endBounds, startBounds); @@ -842,19 +844,25 @@ public class PipTransition extends PipTransitionController implements initActivityPos.y); } } + void cacheAndStartTransitionAnimator(@NonNull ValueAnimator animator) { + mTransitionAnimator = animator; + mTransitionAnimator.start(); + } + + @NonNull + private static PictureInPictureParams getPipParams(@NonNull TransitionInfo.Change pipChange) { + return pipChange.getTaskInfo().pictureInPictureParams != null + ? pipChange.getTaskInfo().pictureInPictureParams + : new PictureInPictureParams.Builder().build(); + } @NonNull - private SurfaceControl getLeash(TransitionInfo.Change change) { + private static SurfaceControl getLeash(TransitionInfo.Change change) { SurfaceControl leash = change.getLeash(); Preconditions.checkNotNull(leash, "Leash is null for change=" + change); return leash; } - void cacheAndStartTransitionAnimator(@NonNull ValueAnimator animator) { - mTransitionAnimator = animator; - mTransitionAnimator.start(); - } - // // Miscellaneous callbacks and listeners // @@ -893,10 +901,10 @@ public class PipTransition extends PipTransitionController implements Preconditions.checkState(extra != null, "No extra bundle for " + mPipTransitionState); - mPipTransitionState.setPipTaskToken(extra.getParcelable( - PIP_TASK_TOKEN, WindowContainerToken.class)); mPipTransitionState.setPinnedTaskLeash(extra.getParcelable( PIP_TASK_LEASH, SurfaceControl.class)); + mPipTransitionState.setPipTaskInfo(extra.getParcelable( + PIP_TASK_INFO, TaskInfo.class)); boolean hasValidTokenAndLeash = mPipTransitionState.getPipTaskToken() != null && mPipTransitionState.getPinnedTaskLeash() != null; @@ -904,9 +912,21 @@ public class PipTransition extends PipTransitionController implements "Unexpected bundle for " + mPipTransitionState); break; case PipTransitionState.EXITED_PIP: - mPipTransitionState.setPipTaskToken(null); + // Save the PiP bounds in case, we re-enter the PiP with the same component. + float snapFraction = mPipBoundsAlgorithm.getSnapFraction( + mPipBoundsState.getBounds()); + mPipBoundsState.saveReentryState(snapFraction); + mPipTransitionState.setPinnedTaskLeash(null); + mPipTransitionState.setPipTaskInfo(null); break; } } + + @Override + public boolean isPackageActiveInPip(@Nullable String packageName) { + final TaskInfo inPipTask = mPipTransitionState.getPipTaskInfo(); + return packageName != null && inPipTask != null && mPipTransitionState.isInPip() + && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent)); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java index 8e90bfee2636..6f9f40a487c7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java @@ -17,6 +17,7 @@ package com.android.wm.shell.pip2.phone; import android.annotation.IntDef; +import android.app.TaskInfo; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; @@ -133,17 +134,17 @@ public class PipTransitionState { private final Rect mSwipePipToHomeAppBounds = new Rect(); // - // Tokens and leashes + // Task related caches // - // pinned PiP task's WC token - @Nullable - private WindowContainerToken mPipTaskToken; - // pinned PiP task's leash @Nullable private SurfaceControl mPinnedTaskLeash; + // pinned PiP task info + @Nullable + private TaskInfo mPipTaskInfo; + // Overlay leash potentially used during swipe PiP to home transition; // if null while mInSwipePipToHomeTransition is true, then srcRectHint was invalid. @Nullable @@ -305,11 +306,7 @@ public class PipTransitionState { } @Nullable WindowContainerToken getPipTaskToken() { - return mPipTaskToken; - } - - public void setPipTaskToken(@Nullable WindowContainerToken token) { - mPipTaskToken = token; + return mPipTaskInfo != null ? mPipTaskInfo.getToken() : null; } @Nullable SurfaceControl getPinnedTaskLeash() { @@ -320,6 +317,14 @@ public class PipTransitionState { mPinnedTaskLeash = leash; } + @Nullable TaskInfo getPipTaskInfo() { + return mPipTaskInfo; + } + + void setPipTaskInfo(@Nullable TaskInfo pipTaskInfo) { + mPipTaskInfo = pipTaskInfo; + } + /** * @return true if either in swipe or button-nav fixed rotation. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index d917f937b16c..363c95fcf010 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -19,8 +19,10 @@ package com.android.wm.shell.recents; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.PackageManager.FEATURE_PC; +import static android.view.Display.INVALID_DISPLAY; import static com.android.wm.shell.Flags.enableShellTopTaskTracking; +import static com.android.wm.shell.desktopmode.DesktopWallpaperActivity.isWallpaperTask; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_OBSERVER; import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS; @@ -53,6 +55,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.protolog.ProtoLog; +import com.android.launcher3.Flags; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; @@ -282,6 +285,17 @@ public class RecentTasksController implements TaskStackListenerCallback, notifyRecentTasksChanged(); } + /** + * This method only gets notified when a task is removed from recents as a result of another + * task being added to recent tasks. + */ + @Override + public void onRecentTaskRemovedForAddTask(int taskId) { + mDesktopRepository.ifPresent( + repo -> repo.removeFreeformTask(INVALID_DISPLAY, taskId) + ); + } + public void onTaskAdded(RunningTaskInfo taskInfo) { notifyRunningTaskAppeared(taskInfo); } @@ -530,6 +544,10 @@ public class RecentTasksController implements TaskStackListenerCallback, groupedTasks.add(GroupedTaskInfo.forSplitTasks(taskInfo, pairedTaskInfo, mTaskSplitBoundsMap.get(pairedTaskId))); } else { + if (Flags.enableRefactorTaskThumbnail() && isWallpaperTask(taskInfo)) { + // Don't add the wallpaper task as an entry in grouped tasks + continue; + } // TODO(346588978): Consolidate multiple visible fullscreen tasks into the same // grouped task groupedTasks.add(GroupedTaskInfo.forFullscreenTasks(taskInfo)); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 86d689831bdc..88a95660f098 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -507,8 +507,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (enableFlexibleSplit()) { StageTaskListener stageToDeactivate = mStageOrderOperator.getAllStages().stream() .filter(stage -> stage.getId() == stageToTop) - .findFirst().orElseThrow(); - stageToDeactivate.deactivate(wct, true /*toTop*/); + .findFirst().orElse(null); + if (stageToDeactivate != null) { + stageToDeactivate.deactivate(wct, true /*toTop*/); + } else { + // If no one stage is meant to go to the top, deactivate all stages to move any + // child tasks out from under their respective stage root tasks. + mStageOrderOperator.getAllStages().forEach(stage -> + stage.deactivate(wct, false /*reparentTasksToTop*/)); + } mStageOrderOperator.onExitingSplit(); } else { mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN); @@ -1848,13 +1855,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } if (present) { updateRecentTasksSplitPair(); - } else if (mMainStage.getChildCount() == 0 && mSideStage.getChildCount() == 0) { - mRecentTasks.ifPresent(recentTasks -> { - // remove the split pair mapping from recentTasks, and disable further updates - // to splits in the recents until we enter split again. - recentTasks.removeSplitPair(taskId); - }); - dismissSplitScreen(-1, EXIT_REASON_ROOT_TASK_VANISHED); + } else { + // TODO (b/349828130): Test b/333270112 for flex split (launch adjacent for flex + // currently not working) + boolean allRootsEmpty = enableFlexibleSplit() + ? runForActiveStagesAllMatch(stageTaskListener -> + stageTaskListener.getChildCount() == 0) + : mMainStage.getChildCount() == 0 && mSideStage.getChildCount() == 0; + if (allRootsEmpty) { + mRecentTasks.ifPresent(recentTasks -> { + // remove the split pair mapping from recentTasks, and disable further updates + // to splits in the recents until we enter split again. + recentTasks.removeSplitPair(taskId); + }); + dismissSplitScreen(INVALID_TASK_ID, EXIT_REASON_ROOT_TASK_VANISHED); + } } for (int i = mListeners.size() - 1; i >= 0; --i) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt index b8c91519422c..4d95cde1492f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHandleManageWindowsMenu.kt @@ -24,7 +24,7 @@ import android.view.WindowManager import android.window.TaskSnapshot import androidx.compose.ui.graphics.toArgb import com.android.wm.shell.shared.desktopmode.DesktopModeStatus -import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer +import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer import com.android.wm.shell.shared.split.SplitScreenConstants import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt index dd68105d28c1..101467df03ac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt @@ -33,7 +33,7 @@ import com.android.window.flags.Flags import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.desktopmode.DesktopRepository -import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer +import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 43ed23b3b52a..f8a409014f6c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -39,7 +39,7 @@ import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.Indica import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; -import static com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer.MANAGE_WINDOWS_MINIMUM_INSTANCES; +import static com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer.MANAGE_WINDOWS_MINIMUM_INSTANCES; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -107,7 +107,10 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.compatui.CompatUIController; import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; +import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.desktopmode.DesktopModeEventLogger; +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger; +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum; import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.desktopmode.DesktopTasksController; @@ -172,6 +175,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, private final DisplayController mDisplayController; private final SyncTransactionQueue mSyncQueue; private final DesktopTasksController mDesktopTasksController; + private final DesktopImmersiveController mDesktopImmersiveController; private final InputManager mInputManager; private final InteractionJankMonitor mInteractionJankMonitor; private final MultiInstanceHelper mMultiInstanceHelper; @@ -228,6 +232,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, private final TaskPositionerFactory mTaskPositionerFactory; private final FocusTransitionObserver mFocusTransitionObserver; private final DesktopModeEventLogger mDesktopModeEventLogger; + private final DesktopModeUiEventLogger mDesktopModeUiEventLogger; public DesktopModeWindowDecorViewModel( Context context, @@ -246,6 +251,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, + DesktopImmersiveController desktopImmersiveController, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, @@ -257,7 +263,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, FocusTransitionObserver focusTransitionObserver, - DesktopModeEventLogger desktopModeEventLogger) { + DesktopModeEventLogger desktopModeEventLogger, + DesktopModeUiEventLogger desktopModeUiEventLogger) { this( context, shellExecutor, @@ -275,6 +282,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, syncQueue, transitions, desktopTasksController, + desktopImmersiveController, genericLinksParser, assistContentRequester, multiInstanceHelper, @@ -292,7 +300,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, activityOrientationChangeHandler, new TaskPositionerFactory(), focusTransitionObserver, - desktopModeEventLogger); + desktopModeEventLogger, + desktopModeUiEventLogger); } @VisibleForTesting @@ -313,6 +322,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, + DesktopImmersiveController desktopImmersiveController, AppToWebGenericLinksParser genericLinksParser, AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, @@ -330,7 +340,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler, TaskPositionerFactory taskPositionerFactory, FocusTransitionObserver focusTransitionObserver, - DesktopModeEventLogger desktopModeEventLogger) { + DesktopModeEventLogger desktopModeEventLogger, + DesktopModeUiEventLogger desktopModeUiEventLogger) { mContext = context; mMainExecutor = shellExecutor; mMainHandler = mainHandler; @@ -345,6 +356,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mSyncQueue = syncQueue; mTransitions = transitions; mDesktopTasksController = desktopTasksController.get(); + mDesktopImmersiveController = desktopImmersiveController; mMultiInstanceHelper = multiInstanceHelper; mShellCommandHandler = shellCommandHandler; mWindowManager = windowManager; @@ -393,6 +405,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mTaskPositionerFactory = taskPositionerFactory; mFocusTransitionObserver = focusTransitionObserver; mDesktopModeEventLogger = desktopModeEventLogger; + mDesktopModeUiEventLogger = desktopModeUiEventLogger; shellInit.addInitCallback(this::onInit, this); } @@ -577,7 +590,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, decoration.mTaskSurface, mContext, mMainHandler, Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source); mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo, resizeTrigger, - motionEvent); + DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent)); decoration.closeHandleMenu(); decoration.closeMaximizeMenu(); } @@ -587,16 +600,33 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, if (decoration == null) { return; } - mDesktopTasksController.toggleDesktopTaskFullImmersiveState(decoration.mTaskInfo); + if (mDesktopRepository.isTaskInFullImmersiveState(taskId)) { + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_RESTORE); + mDesktopImmersiveController.moveTaskToNonImmersive( + decoration.mTaskInfo, DesktopImmersiveController.ExitReason.USER_INTERACTION); + } else { + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_IMMERSIVE); + mDesktopImmersiveController.moveTaskToImmersive(decoration.mTaskInfo); + } decoration.closeMaximizeMenu(); } - public void onSnapResize(int taskId, boolean left, InputMethod inputMethod) { + /** Snap-resize a task to the left or right side of the desktop. */ + public void onSnapResize(int taskId, boolean left, InputMethod inputMethod, boolean fromMenu) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId); if (decoration == null) { return; } + if (fromMenu) { + final DesktopModeUiEventLogger.DesktopUiEventEnum event = left + ? DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_TILE_TO_LEFT + : DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_TILE_TO_RIGHT; + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, event); + } + mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext, mMainHandler, Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable"); mDesktopTasksController.handleInstantSnapResizingTask( @@ -637,6 +667,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, decoration.addCaptionInset(wct); mDesktopTasksController.moveTaskToDesktop(taskId, wct, source); decoration.closeHandleMenu(); + + if (source == DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON) { + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_DESKTOP_MODE); + } } private void onToFullscreen(int taskId) { @@ -652,6 +687,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mDesktopTasksController.moveToFullscreen(taskId, DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON); } + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_FULL_SCREEN); } private void onToSplitScreen(int taskId) { @@ -664,6 +701,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, // we shouldn't receive input for it any longer. decoration.disposeStatusBarInputLayer(); mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */); + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_MENU_TAP_TO_SPLIT_SCREEN); } private void onNewWindow(int taskId) { @@ -803,6 +842,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } else if (id == R.id.back_button) { mTaskOperations.injectBackKey(mDisplayId); } else if (id == R.id.caption_handle || id == R.id.open_menu_button) { + if (id == R.id.caption_handle && !decoration.mTaskInfo.isFreeform()) { + // Clicking the App Handle. + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_TAP); + } if (!decoration.isHandleMenuActive()) { moveTaskToFront(decoration.mTaskInfo); openHandleMenu(mTaskId); @@ -911,6 +955,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, decoration.closeMaximizeMenu(); } else { mHasLongClicked = true; + mDesktopModeUiEventLogger.log(decoration.mTaskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_REVEAL_MENU); decoration.createMaximizeMenu(); } return true; @@ -1554,8 +1600,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, final DesktopModeTouchEventListener touchEventListener = new DesktopModeTouchEventListener(taskInfo, taskPositioner); - InputMethod inputMethod = DesktopModeEventLogger.Companion.getInputMethodFromMotionEvent( - touchEventListener.mMotionEvent); windowDecoration.setOnMaximizeOrRestoreClickListener(() -> { onMaximizeOrRestore(taskInfo.taskId, "maximize_menu", ResizeTrigger.MAXIMIZE_MENU, touchEventListener.mMotionEvent); @@ -1566,11 +1610,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, return Unit.INSTANCE; }); windowDecoration.setOnLeftSnapClickListener(() -> { - onSnapResize(taskInfo.taskId, /* isLeft= */ true, inputMethod); + onSnapResize(taskInfo.taskId, /* isLeft= */ true, + DesktopModeEventLogger.getInputMethodFromMotionEvent( + touchEventListener.mMotionEvent), /* fromMenu= */ true); return Unit.INSTANCE; }); windowDecoration.setOnRightSnapClickListener(() -> { - onSnapResize(taskInfo.taskId, /* isLeft= */ false, inputMethod); + onSnapResize(taskInfo.taskId, /* isLeft= */ false, + DesktopModeEventLogger.getInputMethodFromMotionEvent( + touchEventListener.mMotionEvent), /* fromMenu= */ true); return Unit.INSTANCE; }); windowDecoration.setOnToDesktopClickListener(desktopModeTransitionSource -> { @@ -1599,6 +1647,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, CompatUIController.launchUserAspectRatioSettings(mContext, taskInfo); return Unit.INSTANCE; }); + windowDecoration.setOnMaximizeHoverListener(() -> { + if (!windowDecoration.isMaximizeMenuActive()) { + mDesktopModeUiEventLogger.log(taskInfo, + DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_REVEAL_MENU); + windowDecoration.createMaximizeMenu(); + } + return Unit.INSTANCE; + }); windowDecoration.setCaptionListeners( touchEventListener, touchEventListener, touchEventListener, touchEventListener); windowDecoration.setExclusionRegionListener(mExclusionRegionListener); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index b6fa059e891d..96cc559a64ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -104,7 +104,7 @@ import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository; import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; -import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer; +import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.windowdecor.extension.TaskInfoKt; import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder; @@ -155,6 +155,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private Function0<Unit> mOnNewWindowClickListener; private Function0<Unit> mOnManageWindowsClickListener; private Function0<Unit> mOnChangeAspectRatioClickListener; + private Function0<Unit> mOnMaximizeHoverListener; private DragPositioningCallback mDragPositioningCallback; private DragResizeInputListener mDragResizeListener; private Runnable mCurrentViewHostRunnable = null; @@ -370,6 +371,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mOnChangeAspectRatioClickListener = listener; } + /** Registers a listener to be called when the maximize header button is hovered. */ + void setOnMaximizeHoverListener(Function0<Unit> listener) { + mOnMaximizeHoverListener = listener; + } + void setCaptionListeners( View.OnClickListener onCaptionButtonClickListener, View.OnTouchListener onCaptionTouchListener, @@ -841,12 +847,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mOnCaptionGenericMotionListener, mAppName, mAppIconBitmap, - () -> { - if (!isMaximizeMenuActive()) { - createMaximizeMenu(); - } - return Unit.INSTANCE; - }); + mOnMaximizeHoverListener); } throw new IllegalArgumentException("Unexpected layout resource id"); } @@ -1417,7 +1418,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mHandleMenu.show( /* onToDesktopClickListener= */ () -> { mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON); - mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON); return Unit.INSTANCE; }, /* onToFullscreenClickListener= */ mOnToFullscreenClickListener, @@ -1459,10 +1459,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @NonNull Function1<Integer, Unit> onIconClickListener ) { if (mTaskInfo.isFreeform()) { + // The menu uses display-wide coordinates for positioning, so make position the sum + // of task position and caption position. + final Rect taskBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); mManageWindowsMenu = new DesktopHeaderManageWindowsMenu( mTaskInfo, - /* x= */ mResult.mCaptionX, - /* y= */ mResult.mCaptionY + mResult.mCaptionTopPadding, + /* x= */ taskBounds.left + mResult.mCaptionX, + /* y= */ taskBounds.top + mResult.mCaptionY + mResult.mCaptionTopPadding, mDisplayController, mRootTaskDisplayAreaOrganizer, mContext, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index b3213996b868..a6d503d0d991 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -460,7 +460,7 @@ class DragResizeInputListener implements AutoCloseable { || ctrlType == CTRL_TYPE_RIGHT || ctrlType == CTRL_TYPE_LEFT) ? ResizeTrigger.EDGE : ResizeTrigger.CORNER; mDesktopModeEventLogger.logTaskResizingStarted(mResizeTrigger, - DesktopModeEventLogger.Companion.getInputMethodFromMotionEvent(e), + DesktopModeEventLogger.getInputMethodFromMotionEvent(e), mTaskInfo, mDragStartTaskBounds.width(), mDragStartTaskBounds.height(), /* displayController= */ null, /* displayLayoutSize= */ mDisplayLayoutSizeSupplier.get()); @@ -514,7 +514,7 @@ class DragResizeInputListener implements AutoCloseable { } mDesktopModeEventLogger.logTaskResizingEnded(mResizeTrigger, - DesktopModeEventLogger.Companion.getInputMethodFromMotionEvent( + DesktopModeEventLogger.getInputMethodFromMotionEvent( mLastMotionEventOnDown), mTaskInfo, taskBounds.width(), taskBounds.height(), /* displayController= */ null, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java index 6f72d34eb0ae..c8aff78cbb36 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java @@ -74,8 +74,7 @@ public final class DragResizeWindowGeometry { mFineTaskCorners = new TaskCorners(mTaskSize, fineCornerSize, disabledEdge); // Save touch areas for each edge. - mTaskEdges = new TaskEdges(mTaskSize, mResizeHandleEdgeOutset, mResizeHandleEdgeInset, - mDisabledEdge); + mTaskEdges = new TaskEdges(mTaskSize, mResizeHandleEdgeOutset, mDisabledEdge); } /** @@ -459,7 +458,7 @@ public final class DragResizeWindowGeometry { private final @NonNull DisabledEdge mDisabledEdge; private TaskEdges(@NonNull Size taskSize, int resizeHandleThickness, - int resizeHandleEdgeInset, DisabledEdge disabledEdge) { + DisabledEdge disabledEdge) { // Save touch areas for each edge. mDisabledEdge = disabledEdge; // Save touch areas for each edge. @@ -471,16 +470,16 @@ public final class DragResizeWindowGeometry { mLeftEdgeBounds = new Rect( -resizeHandleThickness, 0, - resizeHandleEdgeInset, + resizeHandleThickness, taskSize.getHeight()); mRightEdgeBounds = new Rect( - taskSize.getWidth() - resizeHandleEdgeInset, + taskSize.getWidth() - resizeHandleThickness, 0, taskSize.getWidth() + resizeHandleThickness, taskSize.getHeight()); mBottomEdgeBounds = new Rect( -resizeHandleThickness, - taskSize.getHeight() - resizeHandleEdgeInset, + taskSize.getHeight() - resizeHandleThickness, taskSize.getWidth() + resizeHandleThickness, taskSize.getHeight() + resizeHandleThickness); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt index 6cdc517c9cb7..583282247f58 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt @@ -18,10 +18,12 @@ package com.android.wm.shell.windowdecor.tiling import android.content.Context import android.content.res.Configuration +import android.graphics.Path import android.graphics.PixelFormat import android.graphics.Rect import android.graphics.Region import android.os.Binder +import android.util.Size import android.view.LayoutInflater import android.view.MotionEvent import android.view.RoundedCorner @@ -40,7 +42,6 @@ import android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER import android.view.WindowlessWindowManager import com.android.wm.shell.R import com.android.wm.shell.common.SyncTransactionQueue -import com.android.wm.shell.desktopmode.DesktopModeEventLogger import java.util.function.Supplier /** @@ -48,7 +49,7 @@ import java.util.function.Supplier * when two tasks are tiled on left and right to resize them simultaneously. */ class DesktopTilingDividerWindowManager( - private val config: Configuration, + config: Configuration, private val windowName: String, private val context: Context, private val leash: SurfaceControl, @@ -61,7 +62,11 @@ class DesktopTilingDividerWindowManager( private lateinit var viewHost: SurfaceControlViewHost private var tilingDividerView: TilingDividerView? = null private var dividerShown = false - private var handleRegionWidth: Int = -1 + private var handleRegionSize: Size = + Size( + context.resources.getDimensionPixelSize(R.dimen.split_divider_handle_region_width), + context.resources.getDimensionPixelSize(R.dimen.split_divider_handle_region_height), + ) private var setTouchRegion = true private val maxRoundedCornerRadius = getMaxRoundedCornerRadius() @@ -74,9 +79,62 @@ class DesktopTilingDividerWindowManager( rect.set(dividerBounds) } - /** Sets the touch region for the SurfaceControlViewHost. */ - fun setTouchRegion(region: Rect) { - setTouchRegion(viewHost.windowToken.asBinder(), Region(region)) + /** + * Sets the touch region for the SurfaceControlViewHost. + * + * The region includes the area around the handle (for accessibility), the divider itself and + * the rounded corners (to prevent click reaching windows behind). + */ + fun setTouchRegion(handle: Rect, divider: Rect, cornerRadius: Float) { + val path = Path() + path.fillType = Path.FillType.WINDING + // The UI starts on the top-left corner, the region will be: + // + // cornerLeft cornerRight + // c1Top +--------+ + // |corners | + // c1Bottom +--+ +--+ + // | | + // handleLeft| | handleRight + // handleTop +----+ +----+ + // | handle | + // handleBot +----+ +----+ + // | | + // | | + // c2Top +--+ +--+ + // |corners | + // c2Bottom +--------+ + val cornerLeft = 0f + val centerX = cornerRadius + divider.width() / 2f + val centerY = divider.height() + val cornerRight = divider.width() + 2 * cornerRadius + val handleLeft = centerX - handle.width() / 2f + val handleRight = handleLeft + handle.width() + val dividerLeft = centerY - divider.width() / 2f + val dividerRight = dividerLeft + divider.width() + + val c1Top = 0f + val c1Bottom = cornerRadius + val handleTop = centerY - handle.height() / 2f + val handleBottom = handleTop + handle.height() + val c2Top = divider.height() - cornerRadius + val c2Bottom = divider.height().toFloat() + + // Top corners + path.addRect(cornerLeft, c1Top, cornerRight, c1Bottom, Path.Direction.CCW) + // Bottom corners + path.addRect(cornerLeft, c1Top, cornerRight, c2Bottom, Path.Direction.CCW) + // Handle + path.addRect(handleLeft, handleTop, handleRight, handleBottom, Path.Direction.CCW) + // Divider + path.addRect(dividerLeft, c2Top, dividerRight, c2Bottom, Path.Direction.CCW) + + val clip = Rect(handleLeft.toInt(), c1Top.toInt(), handleRight.toInt(), c2Bottom.toInt()) + + val region = Region() + region.setPath(path, Region(clip)) + + setTouchRegion(viewHost.windowToken.asBinder(), region) } /** @@ -96,7 +154,7 @@ class DesktopTilingDividerWindowManager( surfaceControlViewHost.setView(dividerView, lp) val tmpDividerBounds = Rect() getDividerBounds(tmpDividerBounds) - dividerView.setup(this, tmpDividerBounds) + dividerView.setup(this, tmpDividerBounds, handleRegionSize) t.setRelativeLayer(leash, relativeLeash, 1) .setPosition( leash, @@ -112,7 +170,7 @@ class DesktopTilingDividerWindowManager( viewHost = surfaceControlViewHost dividerView.addOnLayoutChangeListener(this) tilingDividerView = dividerView - handleRegionWidth = dividerView.handleRegionWidth + updateTouchRegion() } /** Hides the divider bar. */ @@ -176,8 +234,8 @@ class DesktopTilingDividerWindowManager( private fun getWindowManagerParams(): WindowManager.LayoutParams { val lp = WindowManager.LayoutParams( - dividerBounds.width() + 2 * maxRoundedCornerRadius, - dividerBounds.height(), + /* w= */ dividerBounds.width() + 2 * maxRoundedCornerRadius, + /* h= */ dividerBounds.height(), TYPE_DOCK_DIVIDER, FLAG_NOT_FOCUSABLE or FLAG_NOT_TOUCH_MODAL or @@ -216,13 +274,16 @@ class DesktopTilingDividerWindowManager( ) { if (!setTouchRegion) return - val startX = (dividerBounds.width() - handleRegionWidth) / 2 - val startY = 0 - val tempRect = Rect(startX, startY, startX + handleRegionWidth, dividerBounds.height()) - setTouchRegion(tempRect) + updateTouchRegion() setTouchRegion = false } + private fun updateTouchRegion() { + val startX = -handleRegionSize.width / 2 + val handle = Rect(startX, 0, startX + handleRegionSize.width, dividerBounds.height()) + setTouchRegion(handle, dividerBounds, maxRoundedCornerRadius.toFloat()) + } + private fun setSlippery(slippery: Boolean) { val lp = tilingDividerView?.layoutParams as WindowManager.LayoutParams val isSlippery = (lp.flags and FLAG_SLIPPERY) != 0 diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt index bc7576c91da2..3b5c6ca2e58a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt @@ -492,9 +492,8 @@ class DesktopTilingWindowDecoration( // Only called if [taskInfo] relates to a focused task private fun isTilingRefocused(taskInfo: RunningTaskInfo): Boolean { - return !isTilingFocused && - (taskInfo.taskId == leftTaskResizingHelper?.taskInfo?.taskId || - taskInfo.taskId == rightTaskResizingHelper?.taskInfo?.taskId) + return taskInfo.taskId == leftTaskResizingHelper?.taskInfo?.taskId || + taskInfo.taskId == rightTaskResizingHelper?.taskInfo?.taskId } private fun buildTiledTasksMoveToFront(leftOnTop: Boolean): WindowContainerTransaction { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt index 111e28e450bd..b8e3b0fdb8d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt @@ -21,8 +21,10 @@ import android.graphics.Paint import android.graphics.Rect import android.provider.DeviceConfig import android.util.AttributeSet +import android.util.Size import android.view.MotionEvent import android.view.PointerIcon +import android.view.RoundedCorner import android.view.View import android.view.ViewConfiguration import android.widget.FrameLayout @@ -42,6 +44,7 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion private lateinit var callback: DividerMoveCallback private lateinit var handle: DividerHandleView private lateinit var corners: DividerRoundedCorner + private var cornersRadius: Int = 0 private var touchElevation = 0 private var moving = false @@ -49,8 +52,7 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion var handleRegionWidth: Int = 0 private var handleRegionHeight = 0 private var lastAcceptedPos = 0 - @VisibleForTesting var handleStartY = 0 - @VisibleForTesting var handleEndY = 0 + @VisibleForTesting var handleY: IntRange = 0..0 private var canResize = false private var resized = false /** @@ -79,16 +81,19 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion ) : super(context, attrs, defStyleAttr, defStyleRes) /** Sets up essential dependencies of the divider bar. */ - fun setup(dividerMoveCallback: DividerMoveCallback, dividerBounds: Rect) { + fun setup( + dividerMoveCallback: DividerMoveCallback, + dividerBounds: Rect, + handleRegionSize: Size, + ) { callback = dividerMoveCallback this.dividerBounds.set(dividerBounds) handle.setIsLeftRightSplit(true) corners.setIsLeftRightSplit(true) - handleRegionHeight = - resources.getDimensionPixelSize(R.dimen.split_divider_handle_region_width) - - handleRegionWidth = - resources.getDimensionPixelSize(R.dimen.split_divider_handle_region_height) + handleRegionHeight = handleRegionSize.height + handleRegionWidth = handleRegionSize.width + cornersRadius = + context.display.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT)?.radius ?: 0 initHandleYCoordinates() dragDetector = DragDetector( @@ -241,17 +246,17 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion return true } - private fun isWithinHandleRegion(touchYPos: Int): Boolean { - return touchYPos in handleStartY..handleEndY - } + private fun isWithinHandleRegion(touchYPos: Int): Boolean = touchYPos in handleY private fun initHandleYCoordinates() { - handleStartY = (dividerBounds.height() - handleRegionHeight) / 2 - handleEndY = handleStartY + handleRegionHeight + val handleStartY = (dividerBounds.height() - handleRegionHeight) / 2 + val handleEndY = handleStartY + handleRegionHeight + handleY = handleStartY..handleEndY } companion object { const val TOUCH_ANIMATION_DURATION: Long = 150 const val TOUCH_RELEASE_ANIMATION_DURATION: Long = 200 + private val TAG = TilingDividerView::class.java.simpleName } } diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt index 0cc8b0c7ba8d..d9c36cc70790 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt @@ -25,6 +25,7 @@ import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart import android.tools.flicker.assertors.assertions.AppWindowAlignsWithOnlyOneDisplayCornerAtEnd import android.tools.flicker.assertors.assertions.AppWindowBecomesInvisible +import android.tools.flicker.assertors.assertions.AppWindowBecomesPinned import android.tools.flicker.assertors.assertions.AppWindowBecomesTopWindow import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible import android.tools.flicker.assertors.assertions.AppWindowCoversLeftHalfScreenAtEnd @@ -465,5 +466,25 @@ class DesktopModeFlickerScenarios { AppWindowIsVisibleAlways(SIMPLE_APP) ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) + + val MINIMIZE_AUTO_PIP_APP = + FlickerConfigEntry( + scenarioId = ScenarioId("MINIMIZE_AUTO_PIP_APP"), + extractor = + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> = + transitions.filter { it.type == TransitionType.PIP } + } + ), + assertions = + AssertionTemplates.COMMON_ASSERTIONS + + listOf( + AppWindowBecomesPinned(DESKTOP_MODE_APP), + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }) + ) } } diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAutoPipAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAutoPipAppWindow.kt new file mode 100644 index 000000000000..b10db689e5bd --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAutoPipAppWindow.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_AUTO_PIP_APP +import com.android.wm.shell.scenarios.MinimizeAutoPipAppWindow +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class MinimizeAutoPipAppWindow : MinimizeAutoPipAppWindow() { + @ExpectedScenarios(["MINIMIZE_AUTO_PIP_APP"]) + @Test + override fun minimizePipAppWindow() = super.minimizePipAppWindow() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MINIMIZE_AUTO_PIP_APP) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt index c43a57594fb3..bb812ad51135 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt @@ -45,9 +45,9 @@ open class DragAppWindowMultiWindowAndPip : DragAppWindowScenarioTestBase() @Before fun setup() { Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + testApp.enterDesktopMode(wmHelper, device) // Set string extra to ensure the app is on PiP mode at launch pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = mapOf("enter_pip" to "true")) - testApp.enterDesktopMode(wmHelper, device) mailApp.launchViaIntent(wmHelper) newTasksApp.launchViaIntent(wmHelper) imeApp.launchViaIntent(wmHelper) diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt index 0226eb35de14..41452c3bdd9f 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt @@ -63,9 +63,9 @@ constructor(val rotation: Rotation = Rotation.ROTATION_0, Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) + testApp.enterDesktopMode(wmHelper, device) // Set string extra to ensure the app is on PiP mode at launch pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = mapOf("enter_pip" to "true")) - testApp.enterDesktopMode(wmHelper, device) mailApp.launchViaIntent(wmHelper) newTasksApp.launchViaIntent(wmHelper) imeApp.launchViaIntent(wmHelper) diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp index 176020ffc3b9..6d12b00a61e6 100644 --- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp +++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp @@ -49,6 +49,7 @@ java_library { "wm-flicker-common-assertions", "launcher-helper-lib", "launcher-aosp-tapl", + "com_android_wm_shell_flags_lib", ], } diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt index 6d396ea6e9d4..9c71510df8a0 100644 --- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt +++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt @@ -17,12 +17,16 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest import android.tools.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice +import com.android.wm.shell.Flags import com.android.wm.shell.flicker.splitscreen.benchmark.DismissSplitScreenByGoHomeBenchmark import com.android.wm.shell.flicker.utils.ICommonAssertions import com.android.wm.shell.flicker.utils.appWindowBecomesInvisible @@ -30,6 +34,7 @@ import com.android.wm.shell.flicker.utils.layerBecomesInvisible import com.android.wm.shell.flicker.utils.splitAppLayerBoundsBecomesInvisible import com.android.wm.shell.flicker.utils.splitScreenDividerBecomesInvisible import org.junit.FixMethodOrder +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -44,8 +49,13 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2) class DismissSplitScreenByGoHome(override val flicker: LegacyFlickerTest) : DismissSplitScreenByGoHomeBenchmark(flicker), ICommonAssertions { + @JvmField + @Rule + val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + override val transition: FlickerBuilder.() -> Unit get() = { defaultSetup(this) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java index 9c31b46a80e5..310c2d725c09 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.mock; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.WindowConfiguration; +import android.content.ComponentName; import android.content.Intent; import android.graphics.Point; import android.graphics.Rect; @@ -42,7 +43,9 @@ public final class TestRunningTaskInfoBuilder { private int mParentTaskId = INVALID_TASK_ID; private int mUid = INVALID_TASK_ID; private int mTaskId = INVALID_TASK_ID; + private int mUserId = -1; private Intent mBaseIntent = new Intent(); + private ComponentName mBaseActivity = null; private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD; private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED; private @WindowConfiguration.ActivityType int mTopActivityType = ACTIVITY_TYPE_STANDARD; @@ -88,6 +91,12 @@ public final class TestRunningTaskInfoBuilder { return this; } + /** Sets the task info's user id. */ + public TestRunningTaskInfoBuilder setUserId(int userId) { + mUserId = userId; + return this; + } + /** * Set {@link ActivityManager.RunningTaskInfo#baseIntent} for the task info, by default * an empty intent is assigned @@ -97,6 +106,14 @@ public final class TestRunningTaskInfoBuilder { return this; } + /** + * Set {@link ActivityManager.RunningTaskInfo#baseActivity} for the task info. + */ + public TestRunningTaskInfoBuilder setBaseActivity(@NonNull ComponentName activity) { + mBaseActivity = activity; + return this; + } + public TestRunningTaskInfoBuilder setActivityType( @WindowConfiguration.ActivityType int activityType) { mActivityType = activityType; @@ -172,6 +189,8 @@ public final class TestRunningTaskInfoBuilder { info.isTopActivityTransparent = mIsTopActivityTransparent; info.numActivities = mNumActivities; info.lastActiveTime = mLastActiveTime; + info.userId = mUserId; + info.baseActivity = mBaseActivity; return info; } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/transition/TransitionStateHolderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/transition/TransitionStateHolderTest.kt new file mode 100644 index 000000000000..7b1d27a8b823 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/transition/TransitionStateHolderTest.kt @@ -0,0 +1,100 @@ +/* + * 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. + */ + +package com.android.wm.shell.common.transition + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.server.testutils.any +import com.android.wm.shell.TestShellExecutor +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener +import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING +import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING +import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED +import com.android.wm.shell.sysui.ShellInit +import kotlin.test.assertNotNull +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify +import org.mockito.kotlin.never + +/** + * Test class for {@link TransitionStateHolder} + * + * Usage: atest WMShellUnitTests:TransitionStateHolderTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class TransitionStateHolderTest { + + lateinit var recentTransitionHandler: RecentsTransitionHandler + lateinit var shellInit: ShellInit + + @Before + fun before() { + recentTransitionHandler = mock(RecentsTransitionHandler::class.java) + shellInit = ShellInit(TestShellExecutor()) + } + + @Test + fun `No TransitionStateHolder listeners before initialization`() { + TransitionStateHolder(shellInit, recentTransitionHandler) + verify(recentTransitionHandler, never()).addTransitionStateListener(any()) + } + + @Test + fun `When TransitionStateHolder initialized a listener has been registered `() { + TransitionStateHolder(shellInit, recentTransitionHandler) + shellInit.init() + assertNotNull(recentsTransitionStateListener) + } + + @Test + fun `When TransitionStateHolder is created no recent animation running`() { + val holder = TransitionStateHolder(shellInit, recentTransitionHandler) + shellInit.init() + assertFalse(holder.isRecentsTransitionRunning()) + } + + @Test + fun `Recent animation running updates after callback value`() { + val holder = TransitionStateHolder(shellInit, recentTransitionHandler) + shellInit.init() + + assertFalse(holder.isRecentsTransitionRunning()) + + recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_NOT_RUNNING) + assertFalse(holder.isRecentsTransitionRunning()) + + recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_REQUESTED) + assertTrue(holder.isRecentsTransitionRunning()) + + recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING) + assertTrue(holder.isRecentsTransitionRunning()) + } + + private val recentsTransitionStateListener: RecentsTransitionStateListener + get() = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java).run { + verify(recentTransitionHandler).addTransitionStateListener(capture()) + value + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt index 803e5d4442a9..1d390007d470 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/AppCompatUtilsTest.kt @@ -21,7 +21,7 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.internal.R import com.android.wm.shell.ShellTestCase -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt new file mode 100644 index 000000000000..68d9bf9b926f --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt @@ -0,0 +1,146 @@ +/* + * 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. + */ + +package com.android.wm.shell.compatui.letterbox + +import android.content.Context +import android.testing.AndroidTestingRunner +import android.view.SurfaceControl +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn +import com.android.wm.shell.ShellTestCase +import java.util.function.Consumer +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.Mockito.verify +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.verification.VerificationMode + +/** + * Tests for [LetterboxSurfaceBuilder]. + * + * Build/Install/Run: + * atest WMShellUnitTests:LetterboxSurfaceBuilderTest + */ +@RunWith(AndroidTestingRunner::class) +@SmallTest +class LetterboxSurfaceBuilderTest : ShellTestCase() { + + @Test + fun `When surface is created mandatory methods are invoked`() { + runTestScenario { r -> + r.invokeBuilder() + + r.checkNameIsSet(expected = true) + r.checkCallSiteIsSet(expected = true) + r.checkSurfaceIsHidden(invoked = true, isHidden = true) + r.checkColorLayerIsSet(expected = true) + r.checkParentLeashIsSet(expected = true) + r.checkSetLayerIsInvoked(expected = true) + r.checkColorSpaceAgnosticIsSet(expected = true, value = true) + r.checkColorIsSetFromLetterboxConfiguration(expected = true) + } + } + + /** + * Runs a test scenario providing a Robot. + */ + fun runTestScenario(consumer: Consumer<LetterboxSurfaceBuilderRobotTest>) { + val robot = LetterboxSurfaceBuilderRobotTest(mContext) + consumer.accept(robot) + } + + class LetterboxSurfaceBuilderRobotTest(val ctx: Context) { + + private val letterboxConfiguration: LetterboxConfiguration + private val letterboxSurfaceBuilder: LetterboxSurfaceBuilder + private val tx: SurfaceControl.Transaction + private val parentLeash: SurfaceControl + private val surfaceBuilder: SurfaceControl.Builder + + companion object { + @JvmStatic + val TEST_SURFACE_NAME = "SurfaceForTest" + + @JvmStatic + val TEST_SURFACE_CALL_SITE = "CallSiteForTest" + } + + init { + letterboxConfiguration = LetterboxConfiguration(ctx) + letterboxSurfaceBuilder = LetterboxSurfaceBuilder(letterboxConfiguration) + tx = org.mockito.kotlin.mock<SurfaceControl.Transaction>() + doReturn(tx).`when`(tx).setLayer(anyOrNull(), anyOrNull()) + doReturn(tx).`when`(tx).setColorSpaceAgnostic(anyOrNull(), anyOrNull()) + parentLeash = org.mockito.kotlin.mock<SurfaceControl>() + surfaceBuilder = SurfaceControl.Builder() + spyOn(surfaceBuilder) + } + + fun invokeBuilder() { + letterboxSurfaceBuilder.createSurface( + tx, + parentLeash, + TEST_SURFACE_NAME, + TEST_SURFACE_CALL_SITE, + surfaceBuilder + ) + } + + fun checkNameIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()) + .setName(TEST_SURFACE_NAME) + } + + fun checkCallSiteIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()) + .setCallsite(TEST_SURFACE_CALL_SITE) + } + + fun checkSurfaceIsHidden(invoked: Boolean, isHidden: Boolean) { + verify(surfaceBuilder, invoked.asMode()) + .setHidden(isHidden) + } + + fun checkColorLayerIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()).setColorLayer() + } + + fun checkParentLeashIsSet(expected: Boolean) { + verify(surfaceBuilder, expected.asMode()).setParent(parentLeash) + } + + fun checkSetLayerIsInvoked(expected: Boolean) { + verify(tx, expected.asMode()).setLayer(anyOrNull(), ArgumentMatchers.anyInt()) + } + + fun checkColorSpaceAgnosticIsSet(expected: Boolean, value: Boolean) { + verify(tx, expected.asMode()).setColorSpaceAgnostic(anyOrNull(), eq(value)) + } + + fun checkColorIsSetFromLetterboxConfiguration(expected: Boolean) { + val components = letterboxConfiguration.getBackgroundColorRgbArray() + verify(tx, expected.asMode()).setColor(anyOrNull(), eq(components)) + } + + private fun Boolean.asMode(): VerificationMode = if (this) times(1) else never() + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt index 1ae1c3fc4563..9c6afcb8be63 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt @@ -16,10 +16,13 @@ package com.android.wm.shell.compatui.letterbox +import android.graphics.Point +import android.graphics.Rect import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner +import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CLOSE import androidx.test.filters.SmallTest import com.android.window.flags.Flags @@ -33,12 +36,12 @@ import java.util.function.Consumer import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mockito import org.mockito.kotlin.any import org.mockito.kotlin.eq +import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times -import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.verify import org.mockito.verification.VerificationMode /** @@ -93,7 +96,7 @@ class LetterboxTransitionObserverTest : ShellTestCase() { r.creationEventDetected(expected = false) r.visibilityEventDetected(expected = false) r.destroyEventDetected(expected = false) - r.boundsEventDetected(expected = false) + r.updateSurfaceBoundsEventDetected(expected = false) } } } @@ -107,14 +110,23 @@ class LetterboxTransitionObserverTest : ShellTestCase() { inputBuilder { buildTransitionInfo() - r.createTopActivityChange(inputBuilder = this, isLetterboxed = true) + r.createTopActivityChange( + inputBuilder = this, + isLetterboxed = true, + taskPosition = Point(20, 30), + taskWidth = 200, + taskHeight = 300 + ) } validateOutput { r.creationEventDetected(expected = true) r.visibilityEventDetected(expected = true, visible = true) r.destroyEventDetected(expected = false) - r.boundsEventDetected(expected = true) + r.updateSurfaceBoundsEventDetected( + expected = true, + taskBounds = Rect(20, 30, 200, 300) + ) } } } @@ -135,7 +147,7 @@ class LetterboxTransitionObserverTest : ShellTestCase() { r.creationEventDetected(expected = false) r.visibilityEventDetected(expected = true, visible = false) r.destroyEventDetected(expected = false) - r.boundsEventDetected(expected = false) + r.updateSurfaceBoundsEventDetected(expected = false) } } } @@ -156,7 +168,7 @@ class LetterboxTransitionObserverTest : ShellTestCase() { r.destroyEventDetected(expected = true) r.creationEventDetected(expected = false) r.visibilityEventDetected(expected = false, visible = false) - r.boundsEventDetected(expected = false) + r.updateSurfaceBoundsEventDetected(expected = false) } } } @@ -189,10 +201,10 @@ class LetterboxTransitionObserverTest : ShellTestCase() { val observerFactory: () -> LetterboxTransitionObserver init { - executor = Mockito.mock(ShellExecutor::class.java) + executor = mock<ShellExecutor>() shellInit = ShellInit(executor) - transitions = Mockito.mock(Transitions::class.java) - letterboxController = Mockito.mock(LetterboxController::class.java) + transitions = mock<Transitions>() + letterboxController = mock<LetterboxController>() letterboxObserver = LetterboxTransitionObserver(shellInit, transitions, letterboxController) observerFactory = { letterboxObserver } @@ -203,67 +215,78 @@ class LetterboxTransitionObserverTest : ShellTestCase() { fun observer() = letterboxObserver fun checkObservableIsRegistered(expected: Boolean) { - Mockito.verify(transitions, expected.asMode()).registerObserver(observer()) + verify(transitions, expected.asMode()).registerObserver(observer()) } fun creationEventDetected( expected: Boolean, displayId: Int = DISPLAY_ID, taskId: Int = TASK_ID - ) { - Mockito.verify(letterboxController, expected.asMode()).createLetterboxSurface( - toLetterboxKeyMatcher(displayId, taskId), - anyOrNull(), - anyOrNull() - ) - } + ) = verify( + letterboxController, + expected.asMode() + ).createLetterboxSurface( + eq(LetterboxKey(displayId, taskId)), + any<SurfaceControl.Transaction>(), + any<SurfaceControl>() + ) fun visibilityEventDetected( expected: Boolean, + visible: Boolean = true, displayId: Int = DISPLAY_ID, - taskId: Int = TASK_ID, - visible: Boolean? = null - ) { - Mockito.verify(letterboxController, expected.asMode()).updateLetterboxSurfaceVisibility( - toLetterboxKeyMatcher(displayId, taskId), - anyOrNull(), - visible.asMatcher() - ) - } + taskId: Int = TASK_ID + ) = verify(letterboxController, expected.asMode()).updateLetterboxSurfaceVisibility( + eq(LetterboxKey(displayId, taskId)), + any<SurfaceControl.Transaction>(), + eq(visible) + ) fun destroyEventDetected( expected: Boolean, displayId: Int = DISPLAY_ID, taskId: Int = TASK_ID - ) { - Mockito.verify(letterboxController, expected.asMode()).destroyLetterboxSurface( - toLetterboxKeyMatcher(displayId, taskId), - anyOrNull() - ) - } - - fun boundsEventDetected( + ) = verify( + letterboxController, + expected.asMode() + ).destroyLetterboxSurface( + eq(LetterboxKey(displayId, taskId)), + any<SurfaceControl.Transaction>() + ) + + fun updateSurfaceBoundsEventDetected( expected: Boolean, displayId: Int = DISPLAY_ID, - taskId: Int = TASK_ID - ) { - Mockito.verify(letterboxController, expected.asMode()).updateLetterboxSurfaceBounds( - toLetterboxKeyMatcher(displayId, taskId), - anyOrNull(), - anyOrNull() - ) - } + taskId: Int = TASK_ID, + taskBounds: Rect = Rect() + ) = verify( + letterboxController, + expected.asMode() + ).updateLetterboxSurfaceBounds( + eq(LetterboxKey(displayId, taskId)), + any<SurfaceControl.Transaction>(), + eq(taskBounds) + ) fun createTopActivityChange( inputBuilder: TransitionObserverInputBuilder, isLetterboxed: Boolean = true, displayId: Int = DISPLAY_ID, - taskId: Int = TASK_ID + taskId: Int = TASK_ID, + taskPosition: Point = Point(), + taskWidth: Int = 0, + taskHeight: Int = 0 ) { - inputBuilder.addChange(changeTaskInfo = inputBuilder.createTaskInfo().apply { - appCompatTaskInfo.isTopActivityLetterboxed = isLetterboxed - this.taskId = taskId - this.displayId = displayId + inputBuilder.addChange(inputBuilder.createChange( + changeTaskInfo = inputBuilder.createTaskInfo().apply { + appCompatTaskInfo.isTopActivityLetterboxed = isLetterboxed + this.taskId = taskId + this.displayId = displayId + } + ).apply { + endRelOffset.x = taskPosition.x + endRelOffset.y = taskPosition.y + endAbsBounds.set(Rect(0, 0, taskWidth, taskHeight)) }) } @@ -279,16 +302,5 @@ class LetterboxTransitionObserverTest : ShellTestCase() { } private fun Boolean.asMode(): VerificationMode = if (this) times(1) else never() - - private fun Boolean?.asMatcher(): Boolean = - if (this != null) eq(this) else any() - - private fun toLetterboxKeyMatcher(displayId: Int, taskId: Int): LetterboxKey { - if (displayId < 0 || taskId < 0) { - return any() - } else { - return eq(LetterboxKey(displayId, taskId)) - } - } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt index aabd973fce90..2ea0379e3bf7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt @@ -39,8 +39,8 @@ import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.TaskStackListenerImpl -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask import com.android.wm.shell.desktopmode.persistence.Desktop import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt index 4666276c2fae..b57c55c4c45a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt @@ -43,7 +43,7 @@ import com.android.wm.shell.TestShellExecutor import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitReason.USER_INTERACTION -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions import com.android.wm.shell.util.StubTransaction diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt index 7adc339c010d..19397c23af48 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt @@ -38,7 +38,7 @@ import com.android.wm.shell.MockToken import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask import com.android.wm.shell.transition.FocusTransitionObserver import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -134,6 +134,12 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { null }.whenever(inputManager).registerKeyGestureEventHandler(any()) shellInit.init() + + desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( + context, + Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), + inputManager, shellTaskOrganizer, focusTransitionObserver, testExecutor + ) } @After @@ -151,11 +157,6 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { FLAG_USE_KEY_GESTURE_EVENT_HANDLER ) fun keyGestureMoveToNextDisplay_shouldMoveToNextDisplay() { - desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( - context, - Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), - inputManager, shellTaskOrganizer, focusTransitionObserver - ) // Set up two display ids whenever(rootTaskDisplayAreaOrganizer.displayIds) .thenReturn(intArrayOf(DEFAULT_DISPLAY, SECOND_DISPLAY)) @@ -187,11 +188,6 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) fun keyGestureSnapLeft_shouldSnapResizeTaskToLeft() { - desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( - context, - Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), - inputManager, shellTaskOrganizer, focusTransitionObserver - ) val task = setUpFreeformTask() task.isFocused = true whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task)) @@ -206,7 +202,10 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { assertThat(result).isTrue() verify(desktopModeWindowDecorViewModel).onSnapResize( - task.taskId, true, DesktopModeEventLogger.Companion.InputMethod.KEYBOARD + task.taskId, + true, + DesktopModeEventLogger.Companion.InputMethod.KEYBOARD, + /* fromMenu= */ false ) } @@ -216,11 +215,6 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) fun keyGestureSnapRight_shouldSnapResizeTaskToRight() { - desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( - context, - Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), - inputManager, shellTaskOrganizer, focusTransitionObserver - ) val task = setUpFreeformTask() task.isFocused = true whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task)) @@ -235,7 +229,10 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { assertThat(result).isTrue() verify(desktopModeWindowDecorViewModel).onSnapResize( - task.taskId, false, DesktopModeEventLogger.Companion.InputMethod.KEYBOARD + task.taskId, + false, + DesktopModeEventLogger.Companion.InputMethod.KEYBOARD, + /* fromMenu= */ false ) } @@ -245,11 +242,6 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) fun keyGestureToggleFreeformWindowSize_shouldToggleTaskSize() { - desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( - context, - Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), - inputManager, shellTaskOrganizer, focusTransitionObserver - ) val task = setUpFreeformTask() task.isFocused = true whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task)) @@ -266,7 +258,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { verify(desktopTasksController).toggleDesktopTaskSize( task, ResizeTrigger.MAXIMIZE_MENU, - null + DesktopModeEventLogger.Companion.InputMethod.KEYBOARD ) } @@ -276,11 +268,6 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) fun keyGestureMinimizeFreeformWindow_shouldMinimizeTask() { - desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler( - context, - Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController), - inputManager, shellTaskOrganizer, focusTransitionObserver - ) val task = setUpFreeformTask() task.isFocused = true whenever(shellTaskOrganizer.getRunningTasks()).thenReturn(arrayListOf(task)) @@ -294,7 +281,6 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { val result = keyGestureEventHandler.handleKeyGestureEvent(event, null) assertThat(result).isTrue() - verify(desktopTasksController).minimizeTask(task) } private fun setUpFreeformTask( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt index 51b291c0b7a4..94698e2fc0fb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt @@ -17,17 +17,22 @@ package com.android.wm.shell.desktopmode +import android.content.ComponentName +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.internal.logging.InstanceId -import com.android.internal.logging.InstanceIdSequence import com.android.internal.logging.testing.UiEventLoggerFake import com.android.wm.shell.ShellTestCase -import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.Companion.DesktopUiEventEnum.DESKTOP_WINDOW_EDGE_DRAG_RESIZE +import com.android.wm.shell.TestRunningTaskInfoBuilder +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.DESKTOP_WINDOW_EDGE_DRAG_RESIZE import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever /** * Test class for [DesktopModeUiEventLogger] @@ -39,13 +44,13 @@ import org.junit.runner.RunWith class DesktopModeUiEventLoggerTest : ShellTestCase() { private lateinit var uiEventLoggerFake: UiEventLoggerFake private lateinit var logger: DesktopModeUiEventLogger - private val instanceIdSequence = InstanceIdSequence(/* instanceIdMax */ 1 shl 20) + private val mockPackageManager: PackageManager = mock<PackageManager>() @Before fun setUp() { uiEventLoggerFake = UiEventLoggerFake() - logger = DesktopModeUiEventLogger(uiEventLoggerFake, instanceIdSequence) + logger = DesktopModeUiEventLogger(uiEventLoggerFake, mockPackageManager) } @Test @@ -102,10 +107,28 @@ class DesktopModeUiEventLoggerTest : ShellTestCase() { assertThat(uiEventLoggerFake[0].packageName).isEqualTo(PACKAGE_NAME) } + @Test + fun logWithTaskInfo_eventLogged() { + val event = + DESKTOP_WINDOW_EDGE_DRAG_RESIZE + val taskInfo = TestRunningTaskInfoBuilder() + .setUserId(USER_ID) + .setBaseActivity(ComponentName(PACKAGE_NAME, "test")) + .build() + whenever(mockPackageManager.getApplicationInfoAsUser(PACKAGE_NAME, /* flags= */ 0, USER_ID)) + .thenReturn(ApplicationInfo().apply { uid = UID }) + logger.log(taskInfo, event) + assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1) + assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id) + assertThat(uiEventLoggerFake[0].instanceId).isNull() + assertThat(uiEventLoggerFake[0].uid).isEqualTo(UID) + assertThat(uiEventLoggerFake[0].packageName).isEqualTo(PACKAGE_NAME) + } companion object { private val INSTANCE_ID = InstanceId.fakeInstanceId(0) private const val UID = 10 + private const val USER_ID = 2 private const val PACKAGE_NAME = "com.foo" } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt index e977966a45fe..8e323acc4e66 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt @@ -23,8 +23,8 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION import com.android.wm.shell.ShellTestCase -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask import org.junit.Before import org.junit.Rule import org.junit.Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index a8cfef7c1262..5c0027220ec9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -99,10 +99,10 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeT import com.android.wm.shell.desktopmode.DesktopTasksController.DesktopModeEntryExitTransitionListener import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition import com.android.wm.shell.desktopmode.DesktopTasksController.TaskbarDesktopTaskListener -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createHomeTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSplitScreenTask import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler @@ -220,6 +220,7 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock private lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter @Mock private lateinit var mockHandler: Handler @Mock private lateinit var desktopModeEventLogger: DesktopModeEventLogger + @Mock private lateinit var desktopModeUiEventLogger: DesktopModeUiEventLogger @Mock lateinit var persistentRepository: DesktopPersistentRepository @Mock lateinit var motionEvent: MotionEvent @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer @@ -346,6 +347,7 @@ class DesktopTasksControllerTest : ShellTestCase() { mockInteractionJankMonitor, mockHandler, desktopModeEventLogger, + desktopModeUiEventLogger, desktopTilingDecorViewModel, ) } @@ -375,12 +377,12 @@ class DesktopTasksControllerTest : ShellTestCase() { val task1 = setUpFreeformTask() val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java) - controller.toggleDesktopTaskSize(task1, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task1, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( ResizeTrigger.MAXIMIZE_BUTTON, - InputMethod.UNKNOWN_INPUT_METHOD, + InputMethod.TOUCH, task1, STABLE_BOUNDS.width(), STABLE_BOUNDS.height(), @@ -402,12 +404,12 @@ class DesktopTasksControllerTest : ShellTestCase() { val task1 = setUpFreeformTask(bounds = stableBounds, active = true) val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java) - controller.toggleDesktopTaskSize(task1, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task1, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture()) verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( ResizeTrigger.MAXIMIZE_BUTTON, - InputMethod.UNKNOWN_INPUT_METHOD, + InputMethod.TOUCH, task1, 0, 0, @@ -3357,14 +3359,14 @@ class DesktopTasksControllerTest : ShellTestCase() { val bounds = Rect(0, 0, 100, 100) val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds) - controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) // Assert bounds set to stable bounds val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS) verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( ResizeTrigger.MAXIMIZE_BUTTON, - InputMethod.UNKNOWN_INPUT_METHOD, + InputMethod.TOUCH, task, STABLE_BOUNDS.width(), STABLE_BOUNDS.height(), @@ -3582,14 +3584,14 @@ class DesktopTasksControllerTest : ShellTestCase() { // Bounds should be 1000 x 500, vertically centered in the 1000 x 1000 stable bounds val expectedBounds = Rect(STABLE_BOUNDS.left, 250, STABLE_BOUNDS.right, 750) - controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) // Assert bounds set to stable bounds val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds) verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( ResizeTrigger.MAXIMIZE_BUTTON, - InputMethod.UNKNOWN_INPUT_METHOD, + InputMethod.TOUCH, task, expectedBounds.width(), expectedBounds.height(), @@ -3602,7 +3604,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val bounds = Rect(0, 0, 100, 100) val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds) - controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isEqualTo(bounds) verify(desktopModeEventLogger, never()).logTaskResizingEnded( any(), any(), any(), any(), @@ -3616,18 +3618,18 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize) // Maximize - controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS) // Restore - controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) // Assert bounds set to last bounds before maximize val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize) verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( ResizeTrigger.MAXIMIZE_BUTTON, - InputMethod.UNKNOWN_INPUT_METHOD, + InputMethod.TOUCH, task, boundsBeforeMaximize.width(), boundsBeforeMaximize.height(), @@ -3643,19 +3645,19 @@ class DesktopTasksControllerTest : ShellTestCase() { } // Maximize - controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS.left, boundsBeforeMaximize.top, STABLE_BOUNDS.right, boundsBeforeMaximize.bottom) // Restore - controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) // Assert bounds set to last bounds before maximize val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize) verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( ResizeTrigger.MAXIMIZE_BUTTON, - InputMethod.UNKNOWN_INPUT_METHOD, + InputMethod.TOUCH, task, boundsBeforeMaximize.width(), boundsBeforeMaximize.height(), @@ -3671,19 +3673,19 @@ class DesktopTasksControllerTest : ShellTestCase() { } // Maximize - controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) task.configuration.windowConfiguration.bounds.set(boundsBeforeMaximize.left, STABLE_BOUNDS.top, boundsBeforeMaximize.right, STABLE_BOUNDS.bottom) // Restore - controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) // Assert bounds set to last bounds before maximize val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize) verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( ResizeTrigger.MAXIMIZE_BUTTON, - InputMethod.UNKNOWN_INPUT_METHOD, + InputMethod.TOUCH, task, boundsBeforeMaximize.width(), boundsBeforeMaximize.height(), @@ -3697,17 +3699,17 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize) // Maximize - controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS) // Restore - controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, motionEvent) + controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH) // Assert last bounds before maximize removed after use assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull() verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( ResizeTrigger.MAXIMIZE_BUTTON, - InputMethod.UNKNOWN_INPUT_METHOD, + InputMethod.TOUCH, task, boundsBeforeMaximize.width(), boundsBeforeMaximize.height(), @@ -3749,26 +3751,6 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun toggleImmersive_enter_movesToImmersive() { - val task = setUpFreeformTask(DEFAULT_DISPLAY) - taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, false /* immersive */) - - controller.toggleDesktopTaskFullImmersiveState(task) - - verify(mMockDesktopImmersiveController).moveTaskToImmersive(task) - } - - @Test - fun toggleImmersive_exit_movesToNonImmersive() { - val task = setUpFreeformTask(DEFAULT_DISPLAY) - taskRepository.setTaskInFullImmersiveState(DEFAULT_DISPLAY, task.taskId, true /* immersive */) - - controller.toggleDesktopTaskFullImmersiveState(task) - - verify(mMockDesktopImmersiveController).moveTaskToNonImmersive(eq(task), any()) - } - - @Test @EnableFlags(FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) fun onTaskInfoChanged_inImmersiveUnrequestsImmersive_exits() { val task = setUpFreeformTask(DEFAULT_DISPLAY) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt index 456b50da095b..797b12505ef2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt @@ -42,7 +42,7 @@ import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGAT import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.ShellExecutor -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer import com.android.wm.shell.shared.desktopmode.DesktopModeStatus diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt index 2134f3bb35cc..866d1b3880b0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt @@ -27,78 +27,56 @@ import android.view.Display.DEFAULT_DISPLAY import com.android.wm.shell.MockToken import com.android.wm.shell.TestRunningTaskInfoBuilder -class DesktopTestHelpers { - companion object { - /** Create a task that has windowing mode set to [WINDOWING_MODE_FREEFORM] */ - @JvmStatic - @JvmOverloads - fun createFreeformTask( - displayId: Int = DEFAULT_DISPLAY, - bounds: Rect? = null - ): RunningTaskInfo { - return TestRunningTaskInfoBuilder() - .setDisplayId(displayId) - .setToken(MockToken().token()) - .setActivityType(ACTIVITY_TYPE_STANDARD) - .setWindowingMode(WINDOWING_MODE_FREEFORM) - .setLastActiveTime(100) - .apply { bounds?.let { setBounds(it) }} - .build() - } +object DesktopTestHelpers { + /** Create a task that has windowing mode set to [WINDOWING_MODE_FREEFORM] */ + fun createFreeformTask( + displayId: Int = DEFAULT_DISPLAY, + bounds: Rect? = null, + ): RunningTaskInfo = + TestRunningTaskInfoBuilder() + .setDisplayId(displayId) + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(WINDOWING_MODE_FREEFORM) + .setLastActiveTime(100) + .apply { bounds?.let { setBounds(it) } } + .build() - /** Create a task builder that has windowing mode set to [WINDOWING_MODE_FULLSCREEN] */ - @JvmStatic - @JvmOverloads - fun createFullscreenTaskBuilder(displayId: Int = DEFAULT_DISPLAY): TestRunningTaskInfoBuilder { - return TestRunningTaskInfoBuilder() - .setDisplayId(displayId) - .setToken(MockToken().token()) - .setActivityType(ACTIVITY_TYPE_STANDARD) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .setLastActiveTime(100) - } + fun createFullscreenTaskBuilder(displayId: Int = DEFAULT_DISPLAY): TestRunningTaskInfoBuilder = + TestRunningTaskInfoBuilder() + .setDisplayId(displayId) + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .setLastActiveTime(100) - /** Create a task that has windowing mode set to [WINDOWING_MODE_FULLSCREEN] */ - @JvmStatic - @JvmOverloads - fun createFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { - return createFullscreenTaskBuilder(displayId).build() - } + /** Create a task that has windowing mode set to [WINDOWING_MODE_FULLSCREEN] */ + fun createFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo = + createFullscreenTaskBuilder(displayId).build() - /** Create a task that has windowing mode set to [WINDOWING_MODE_MULTI_WINDOW] */ - @JvmStatic - @JvmOverloads - fun createSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { - return TestRunningTaskInfoBuilder() - .setDisplayId(displayId) - .setToken(MockToken().token()) - .setActivityType(ACTIVITY_TYPE_STANDARD) - .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW) - .setLastActiveTime(100) - .build() - } + /** Create a task that has windowing mode set to [WINDOWING_MODE_MULTI_WINDOW] */ + fun createSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo = + TestRunningTaskInfoBuilder() + .setDisplayId(displayId) + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW) + .setLastActiveTime(100) + .build() - /** Create a new home task */ - @JvmStatic - @JvmOverloads - fun createHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { - return TestRunningTaskInfoBuilder() - .setDisplayId(displayId) - .setToken(MockToken().token()) - .setActivityType(ACTIVITY_TYPE_HOME) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .setLastActiveTime(100) - .build() - } + fun createHomeTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo = + TestRunningTaskInfoBuilder() + .setDisplayId(displayId) + .setToken(MockToken().token()) + .setActivityType(ACTIVITY_TYPE_HOME) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .setLastActiveTime(100) + .build() - /** Create a new System Modal task, i.e. a task with a single transparent activity. */ - @JvmStatic - @JvmOverloads - fun createSystemModalTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { - return createFullscreenTaskBuilder(displayId) - .setTopActivityTransparent(true) - .setNumActivities(1) - .build() - } - } -}
\ No newline at end of file + /** Create a new System Modal task, i.e. a task with a single transparent activity. */ + fun createSystemModalTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo = + createFullscreenTaskBuilder(displayId) + .setTopActivityTransparent(true) + .setNumActivities(1) + .build() +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt index 79d90838cecc..226e974d2875 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt @@ -26,9 +26,9 @@ import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.desktopmode.DesktopRepository -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTaskBuilder -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSystemModalTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTaskBuilder +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSystemModalTask import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.TransitionInfoBuilder import com.android.wm.shell.transition.Transitions diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt index d94186c8284e..9c00c0cee8b1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt @@ -175,13 +175,13 @@ class AppHandleEducationControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) - fun init_educationViewedAlready_shouldNotCallShowEducationTooltip() = + fun init_appHandleHintViewedAlready_shouldNotCallShowEducationTooltip() = testScope.runTest { - // App handle is visible but education has been viewed before. Should not show education - // tooltip. - // Mark education viewed. + // App handle is visible but app handle hint has been viewed before, + // should not show education tooltip. + // Mark app handle hint viewed. testDataStoreFlow.value = - createWindowingEducationProto(educationViewedTimestampMillis = 123L) + createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L) setShouldShowAppHandleEducation(true) // Simulate app handle visible. @@ -194,13 +194,14 @@ class AppHandleEducationControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) - fun overridePrerequisite_educationViewedAlready_shouldCallShowEducationTooltip() = + fun overridePrerequisite_appHandleHintViewedAlready_shouldCallShowEducationTooltip() = testScope.runTest { - // App handle is visible but education has been viewed before. But as we are overriding - // prerequisite conditions, we should show education tooltip. - // Mark education viewed. + // App handle is visible but app handle hint has been viewed before. + // But as we are overriding prerequisite conditions, we should show app + // handle tooltip. + // Mark app handle hint viewed. testDataStoreFlow.value = - createWindowingEducationProto(educationViewedTimestampMillis = 123L) + createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L) val systemPropertiesKey = "persist.desktop_windowing_app_handle_education_override_conditions" whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean())) @@ -217,7 +218,7 @@ class AppHandleEducationControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) - fun init_appHandleExpanded_shouldMarkFeatureViewed() = + fun init_appHandleExpanded_shouldMarkAppHandleHintUsed() = testScope.runTest { setShouldShowAppHandleEducation(false) @@ -226,12 +227,12 @@ class AppHandleEducationControllerTest : ShellTestCase() { // Wait for some time before verifying waitForBufferDelay() - verify(mockDataStoreRepository, times(1)).updateFeatureUsedTimestampMillis(eq(true)) + verify(mockDataStoreRepository, times(1)).updateAppHandleHintUsedTimestampMillis(eq(true)) } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) - fun init_showFirstTooltip_shouldMarkEducationViewed() = + fun init_showFirstTooltip_shouldMarkAppHandleHintViewed() = testScope.runTest { // App handle is visible. Should show education tooltip. setShouldShowAppHandleEducation(true) @@ -241,7 +242,7 @@ class AppHandleEducationControllerTest : ShellTestCase() { // Wait for first tooltip to showup. waitForBufferDelay() - verify(mockDataStoreRepository, times(1)).updateEducationViewedTimestampMillis(eq(true)) + verify(mockDataStoreRepository, times(1)).updateAppHandleHintViewedTimestampMillis(eq(true)) } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt index c2865441d7a6..963890d1caa4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt @@ -81,8 +81,8 @@ class AppHandleEducationDatastoreRepositoryTest { runTest(StandardTestDispatcher()) { val windowingEducationProto = createWindowingEducationProto( - educationViewedTimestampMillis = 123L, - featureUsedTimestampMillis = 124L, + appHandleHintViewedTimestampMillis = 123L, + appHandleHintUsedTimestampMillis = 124L, appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2), appUsageStatsLastUpdateTimestampMillis = 125L) testDatastore.updateData { windowingEducationProto } @@ -110,20 +110,20 @@ class AppHandleEducationDatastoreRepositoryTest { } @Test - fun updateEducationViewedTimestampMillis_updatesDatastoreProto() = + fun updateAppHandleHintViewedTimestampMillis_updatesDatastoreProto() = runTest(StandardTestDispatcher()) { - datastoreRepository.updateEducationViewedTimestampMillis(true) + datastoreRepository.updateAppHandleHintViewedTimestampMillis(true) - val result = testDatastore.data.first().hasEducationViewedTimestampMillis() + val result = testDatastore.data.first().hasAppHandleHintViewedTimestampMillis() assertThat(result).isEqualTo(true) } @Test - fun updateFeatureUsedTimestampMillis_updatesDatastoreProto() = + fun updateAppHandleHintUsedTimestampMillis_updatesDatastoreProto() = runTest(StandardTestDispatcher()) { - datastoreRepository.updateFeatureUsedTimestampMillis(true) + datastoreRepository.updateAppHandleHintUsedTimestampMillis(true) - val result = testDatastore.data.first().hasFeatureUsedTimestampMillis() + val result = testDatastore.data.first().hasAppHandleHintUsedTimestampMillis() assertThat(result).isEqualTo(true) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt index a3e74e8aed5d..e5edd69155b5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt @@ -134,12 +134,12 @@ class AppHandleEducationFilterTest : ShellTestCase() { } @Test - fun shouldShowAppHandleEducation_educationViewedBefore_returnsFalse() = runTest { - // Education has been viewed before, hence #shouldShowAppHandleEducation should return false + fun shouldShowAppHandleEducation_appHandleHintViewedBefore_returnsFalse() = runTest { + // App handle hint has been viewed before, hence #shouldShowAppHandleEducation should return false val windowingEducationProto = createWindowingEducationProto( appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4), - educationViewedTimestampMillis = 123L, + appHandleHintViewedTimestampMillis = 123L, appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) @@ -149,12 +149,12 @@ class AppHandleEducationFilterTest : ShellTestCase() { } @Test - fun shouldShowAppHandleEducation_featureUsedBefore_returnsFalse() = runTest { - // Feature has been used before, hence #shouldShowAppHandleEducation should return false + fun shouldShowAppHandleEducation_appHandleHintUsedBefore_returnsFalse() = runTest { + // App handle hint has been used before, hence #shouldShowAppHandleEducation should return false val windowingEducationProto = createWindowingEducationProto( appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4), - featureUsedTimestampMillis = 123L, + appHandleHintUsedTimestampMillis = 123L, appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index 12c397868f5a..68c8aab8849d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.launcher3.Flags.FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL; import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50; @@ -51,6 +52,7 @@ import android.app.ActivityTaskManager; import android.app.KeyguardManager; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Point; import android.graphics.Rect; @@ -72,6 +74,7 @@ import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.desktopmode.DesktopRepository; +import com.android.wm.shell.desktopmode.DesktopWallpaperActivity; import com.android.wm.shell.shared.GroupedTaskInfo; import com.android.wm.shell.shared.ShellSharedConstants; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; @@ -237,6 +240,19 @@ public class RecentTasksControllerTest extends ShellTestCase { t3.taskId, -1); } + @EnableFlags(FLAG_ENABLE_REFACTOR_TASK_THUMBNAIL) + @Test + public void testGetRecentTasks_removesDesktopWallpaperActivity() { + RecentTaskInfo t1 = makeTaskInfo(1); + RecentTaskInfo desktopWallpaperTaskInfo = makeDesktopWallpaperTaskInfo(2); + RecentTaskInfo t3 = makeTaskInfo(3); + setRawList(t1, desktopWallpaperTaskInfo, t3); + + ArrayList<GroupedTaskInfo> recentTasks = + mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0); + assertGroupedTasksListEquals(recentTasks, t1.taskId, -1, t3.taskId, -1); + } + @Test public void testGetRecentTasks_withPairs() { RecentTaskInfo t1 = makeTaskInfo(1); @@ -691,11 +707,25 @@ public class RecentTasksControllerTest extends ShellTestCase { private RecentTaskInfo makeTaskInfo(int taskId) { RecentTaskInfo info = new RecentTaskInfo(); info.taskId = taskId; + + Intent intent = new Intent(); + intent.setComponent(new ComponentName("com." + taskId, "Activity" + taskId)); + info.baseIntent = intent; + info.lastNonFullscreenBounds = new Rect(); return info; } /** + * Helper to create a desktop wallpaper activity with a given task id. + */ + private RecentTaskInfo makeDesktopWallpaperTaskInfo(int taskId) { + RecentTaskInfo info = makeTaskInfo(taskId); + info.baseIntent.setComponent(DesktopWallpaperActivity.getWallpaperActivityComponent()); + return info; + } + + /** * Helper to create a running task with a given task id. */ private ActivityManager.RunningTaskInfo makeRunningTaskInfo(int taskId) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/TransitionObserverTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/TransitionObserverTestUtils.kt index 3e26ee0deed0..0e15668a05a7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/TransitionObserverTestUtils.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/TransitionObserverTestUtils.kt @@ -36,7 +36,6 @@ import android.window.TransitionInfo.TransitionMode import android.window.WindowContainerToken import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn import com.android.wm.shell.transition.Transitions.TransitionObserver -import org.mockito.Mockito import org.mockito.kotlin.mock @DslMarker @@ -93,10 +92,10 @@ class TransitionObserverTestContext : TransitionObserverTestStep { */ class TransitionObserverInputBuilder : TransitionObserverTestStep { - private val transition = Mockito.mock(IBinder::class.java) + private val transition = mock<IBinder>() private var transitionInfo: TransitionInfo? = null - private val startTransaction = Mockito.mock(Transaction::class.java) - private val finishTransaction = Mockito.mock(Transaction::class.java) + private val startTransaction = mock<Transaction>() + private val finishTransaction = mock<Transaction>() fun buildTransitionInfo( @TransitionType type: Int = TRANSIT_NONE, @@ -143,7 +142,7 @@ class TransitionObserverInputBuilder : TransitionObserverTestStep { taskId = id displayId = DEFAULT_DISPLAY configuration.windowConfiguration.windowingMode = windowingMode - token = WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java)) + token = WindowContainerToken(mock<IWindowContainerToken>()) baseIntent = Intent().apply { component = ComponentName("package", "component.name") } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt index 99e82959fcd6..b9d91e7895db 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt @@ -78,18 +78,18 @@ fun createTaskInfo( * Any fields without corresponding parameters will retain their default values. */ fun createWindowingEducationProto( - educationViewedTimestampMillis: Long? = null, - featureUsedTimestampMillis: Long? = null, + appHandleHintViewedTimestampMillis: Long? = null, + appHandleHintUsedTimestampMillis: Long? = null, appUsageStats: Map<String, Int>? = null, appUsageStatsLastUpdateTimestampMillis: Long? = null ): WindowingEducationProto = WindowingEducationProto.newBuilder() .apply { - if (educationViewedTimestampMillis != null) { - setEducationViewedTimestampMillis(educationViewedTimestampMillis) + if (appHandleHintViewedTimestampMillis != null) { + setAppHandleHintViewedTimestampMillis(appHandleHintViewedTimestampMillis) } - if (featureUsedTimestampMillis != null) { - setFeatureUsedTimestampMillis(featureUsedTimestampMillis) + if (appHandleHintUsedTimestampMillis != null) { + setAppHandleHintUsedTimestampMillis(appHandleHintUsedTimestampMillis) } setAppHandleEducation( createAppHandleEducationProto(appUsageStats, appUsageStatsLastUpdateTimestampMillis)) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 7bb8e891e33b..6c910f56f069 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -59,6 +59,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.window.flags.Flags import com.android.wm.shell.R +import com.android.wm.shell.desktopmode.DesktopImmersiveController import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition @@ -399,7 +400,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest verify(mockDesktopTasksController).toggleDesktopTaskSize( decor.mTaskInfo, ResizeTrigger.MAXIMIZE_MENU, - null + InputMethod.UNKNOWN_INPUT_METHOD ) } @@ -1000,7 +1001,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) - fun testMaximizeButtonClick_requestingImmersive_togglesDesktopImmersiveState() { + fun testImmersiveButtonClick_entersImmersiveMode() { val onClickListenerCaptor = forClass(View.OnClickListener::class.java) as ArgumentCaptor<View.OnClickListener> val decor = createOpenTaskDecoration( @@ -1010,11 +1011,35 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest ) val view = mock(View::class.java) whenever(view.id).thenReturn(R.id.maximize_window) + whenever(mockDesktopRepository.isTaskInFullImmersiveState(decor.mTaskInfo.taskId)) + .thenReturn(false) onClickListenerCaptor.value.onClick(view) - verify(mockDesktopTasksController) - .toggleDesktopTaskFullImmersiveState(decor.mTaskInfo) + verify(mockDesktopImmersiveController).moveTaskToImmersive(decor.mTaskInfo) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun testImmersiveRestoreButtonClick_exitsImmersiveMode() { + val onClickListenerCaptor = forClass(View.OnClickListener::class.java) + as ArgumentCaptor<View.OnClickListener> + val decor = createOpenTaskDecoration( + windowingMode = WINDOWING_MODE_FREEFORM, + onCaptionButtonClickListener = onClickListenerCaptor, + requestingImmersive = true, + ) + val view = mock(View::class.java) + whenever(view.id).thenReturn(R.id.maximize_window) + whenever(mockDesktopRepository.isTaskInFullImmersiveState(decor.mTaskInfo.taskId)) + .thenReturn(true) + + onClickListenerCaptor.value.onClick(view) + + verify(mockDesktopImmersiveController).moveTaskToNonImmersive( + decor.mTaskInfo, + DesktopImmersiveController.ExitReason.USER_INTERACTION + ) } @Test @@ -1033,23 +1058,28 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest onClickListenerCaptor.value.onClick(view) verify(mockDesktopTasksController) - .toggleDesktopTaskSize(decor.mTaskInfo, ResizeTrigger.MAXIMIZE_BUTTON, null) + .toggleDesktopTaskSize( + decor.mTaskInfo, + ResizeTrigger.MAXIMIZE_BUTTON, + InputMethod.UNKNOWN_INPUT_METHOD + ) } @Test @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) - fun testImmersiveClick_togglesImmersiveState() { + fun testImmersiveMenuOptionClick_entersImmersiveMode() { val onImmersiveClickCaptor = argumentCaptor<() -> Unit>() val decor = createOpenTaskDecoration( windowingMode = WINDOWING_MODE_FREEFORM, onImmersiveOrRestoreListenerCaptor = onImmersiveClickCaptor, requestingImmersive = true, ) + whenever(mockDesktopRepository.isTaskInFullImmersiveState(decor.mTaskInfo.taskId)) + .thenReturn(false) onImmersiveClickCaptor.firstValue() - verify(mockDesktopTasksController) - .toggleDesktopTaskFullImmersiveState(decor.mTaskInfo) + verify(mockDesktopImmersiveController).moveTaskToImmersive(decor.mTaskInfo) } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt index 91eaadaf1f18..080f496593cf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt @@ -54,7 +54,9 @@ import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.MultiInstanceHelper import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler +import com.android.wm.shell.desktopmode.DesktopImmersiveController import com.android.wm.shell.desktopmode.DesktopModeEventLogger +import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger import com.android.wm.shell.desktopmode.DesktopRepository import com.android.wm.shell.desktopmode.DesktopTasksController import com.android.wm.shell.desktopmode.DesktopTasksLimiter @@ -111,6 +113,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { protected val displayInsetsController = mock<DisplayInsetsController>() protected val mockSyncQueue = mock<SyncTransactionQueue>() protected val mockDesktopTasksController = mock<DesktopTasksController>() + protected val mockDesktopImmersiveController = mock<DesktopImmersiveController>() protected val mockInputMonitor = mock<InputMonitor>() protected val mockTransitions = mock<Transitions>() internal val mockInputMonitorFactory = @@ -182,6 +185,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { mockSyncQueue, mockTransitions, Optional.of(mockDesktopTasksController), + mockDesktopImmersiveController, mockGenericLinksParser, mockAssistContentRequester, mockMultiInstanceHelper, @@ -199,7 +203,8 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() { Optional.of(mockActivityOrientationChangeHandler), mockTaskPositionerFactory, mockFocusTransitionObserver, - desktopModeEventLogger + desktopModeEventLogger, + mock<DesktopModeUiEventLogger>() ) desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController) whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java index e7d328e28297..479f1567ed31 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java @@ -63,6 +63,7 @@ public class DragResizeWindowGeometryTests extends ShellTestCase { private static final int EDGE_RESIZE_HANDLE_INSET = 4; private static final int FINE_CORNER_SIZE = EDGE_RESIZE_THICKNESS * 2 + 10; private static final int LARGE_CORNER_SIZE = FINE_CORNER_SIZE + 10; + private static final int SMALL_OFFSET = 10; private static final DragResizeWindowGeometry GEOMETRY = new DragResizeWindowGeometry( TASK_CORNER_RADIUS, TASK_SIZE, EDGE_RESIZE_THICKNESS, EDGE_RESIZE_HANDLE_INSET, FINE_CORNER_SIZE, LARGE_CORNER_SIZE, DragResizeWindowGeometry.DisabledEdge.NONE); @@ -147,15 +148,19 @@ public class DragResizeWindowGeometryTests extends ShellTestCase { assertThat(region.contains(point.x + EDGE_RESIZE_THICKNESS, point.y)).isTrue(); assertThat(region.contains(point.x - EDGE_RESIZE_THICKNESS, point.y)).isTrue(); // Vertically along the edge is not contained. - assertThat(region.contains(point.x, point.y - EDGE_RESIZE_THICKNESS)).isFalse(); - assertThat(region.contains(point.x, point.y + EDGE_RESIZE_THICKNESS + 10)).isFalse(); + assertThat( + region.contains(point.x, point.y - EDGE_RESIZE_THICKNESS - SMALL_OFFSET)).isFalse(); + assertThat( + region.contains(point.x, point.y + EDGE_RESIZE_THICKNESS + SMALL_OFFSET)).isFalse(); } private static void verifyVerticalEdge(@NonNull Region region, @NonNull Point point) { assertThat(region.contains(point.x, point.y)).isTrue(); // Horizontally along the edge is not contained. - assertThat(region.contains(point.x + EDGE_RESIZE_THICKNESS, point.y)).isFalse(); - assertThat(region.contains(point.x - EDGE_RESIZE_THICKNESS, point.y)).isFalse(); + assertThat( + region.contains(point.x + EDGE_RESIZE_THICKNESS + SMALL_OFFSET, point.y)).isFalse(); + assertThat( + region.contains(point.x - EDGE_RESIZE_THICKNESS - SMALL_OFFSET, point.y)).isFalse(); // Vertically along the edge is contained. assertThat(region.contains(point.x, point.y - EDGE_RESIZE_THICKNESS)).isTrue(); assertThat(region.contains(point.x, point.y + EDGE_RESIZE_THICKNESS)).isTrue(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt index d8c1a11de452..d29002199f9d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt @@ -27,7 +27,7 @@ import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopModeEventLogger import com.android.wm.shell.desktopmode.DesktopRepository import com.android.wm.shell.desktopmode.DesktopTasksController -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler import com.android.wm.shell.transition.Transitions diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt index 3143946fa828..121e0e915d08 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManagerTest.kt @@ -122,6 +122,6 @@ class DesktopTilingDividerWindowManagerTest : ShellTestCase() { companion object { private val BOUNDS = Rect(1, 2, 3, 4) - private val CORNER_RADIUS = 28 + private const val CORNER_RADIUS = 28 } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt index ad6fdf4d5c59..3b39f1e4a25a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt @@ -38,7 +38,7 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.desktopmode.DesktopRepository import com.android.wm.shell.desktopmode.DesktopTasksController -import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask +import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler import com.android.wm.shell.transition.Transitions @@ -124,7 +124,7 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { @Test fun taskTiled_toCorrectBounds_leftTile() { - val task1 = createFreeformTask() + val task1 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> @@ -151,7 +151,7 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { @Test fun taskTiled_toCorrectBounds_rightTile() { // Setup - val task1 = createFreeformTask() + val task1 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> @@ -177,7 +177,7 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { @Test fun taskTiled_notAnimated_whenTilingPositionNotChange() { - val task1 = createFreeformTask() + val task1 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> @@ -213,8 +213,8 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { @Test fun taskNotTiled_notBroughtToFront_tilingNotInitialised() { - val task1 = createFreeformTask() - val task2 = createFreeformTask() + val task1 = createVisibleTask() + val task2 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> @@ -236,9 +236,9 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { @Test fun taskNotTiled_notBroughtToFront_taskNotTiled() { - val task1 = createFreeformTask() - val task2 = createFreeformTask() - val task3 = createFreeformTask() + val task1 = createVisibleTask() + val task2 = createVisibleTask() + val task3 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> @@ -265,9 +265,9 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { } @Test - fun taskTiled_broughtToFront_alreadyInFrontNoAction() { - val task1 = createFreeformTask() - val task2 = createFreeformTask() + fun taskTiled_broughtToFront_alreadyInFrontStillReorder() { + val task1 = createVisibleTask() + val task2 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> @@ -275,6 +275,8 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { } whenever(context.resources).thenReturn(resources) whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width) + whenever(desktopRepository.isVisibleTask(eq(task1.taskId))).thenReturn(true) + whenever(desktopRepository.isVisibleTask(eq(task2.taskId))).thenReturn(true) tilingDecoration.onAppTiled( task1, @@ -290,15 +292,15 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { ) task1.isFocused = true - assertThat(tilingDecoration.moveTiledPairToFront(task1)).isFalse() - verify(transitions, never()).startTransition(any(), any(), any()) + assertThat(tilingDecoration.moveTiledPairToFront(task1, isTaskFocused = true)).isTrue() + verify(transitions, times(1)).startTransition(eq(TRANSIT_TO_FRONT), any(), eq(null)) } @Test fun taskTiled_broughtToFront_bringToFront() { - val task1 = createFreeformTask() - val task2 = createFreeformTask() - val task3 = createFreeformTask() + val task1 = createVisibleTask() + val task2 = createVisibleTask() + val task3 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> (i.arguments.first() as Rect).set(stableBounds) @@ -329,9 +331,9 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { @Test fun taskTiled_broughtToFront_taskInfoNotUpdated_bringToFront() { - val task1 = createFreeformTask() - val task2 = createFreeformTask() - val task3 = createFreeformTask() + val task1 = createVisibleTask() + val task2 = createVisibleTask() + val task3 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> (i.arguments.first() as Rect).set(stableBounds) @@ -361,8 +363,8 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { @Test fun taskTiledTasks_NotResized_BeforeTouchEndArrival() { // Setup - val task1 = createFreeformTask() - val task2 = createFreeformTask() + val task1 = createVisibleTask() + val task2 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> @@ -428,8 +430,8 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { @Test fun tiledTasksResizedUsingDividerHandle_shouldLogResizingEvents() { // Setup - val task1 = createFreeformTask() - val task2 = createFreeformTask() + val task1 = createVisibleTask() + val task2 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> @@ -487,7 +489,7 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { @Test fun taskTiled_shouldBeRemoved_whenTileBroken() { - val task1 = createFreeformTask() + val task1 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> @@ -516,8 +518,8 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { @Test fun taskNotTiled_shouldNotBeRemoved_whenNotTiled() { - val task1 = createFreeformTask() - val task2 = createFreeformTask() + val task1 = createVisibleTask() + val task2 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> @@ -545,8 +547,8 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { @Test fun tasksTiled_shouldBeRemoved_whenSessionDestroyed() { - val task1 = createFreeformTask() - val task2 = createFreeformTask() + val task1 = createVisibleTask() + val task2 = createVisibleTask() val stableBounds = STABLE_BOUNDS_MOCK whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> @@ -610,6 +612,11 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { return Rect(stableBounds.left, stableBounds.top, rightBound, stableBounds.bottom) } + private fun createVisibleTask() = + createFreeformTask().also { + whenever(desktopRepository.isVisibleTask(eq(it.taskId))).thenReturn(true) + } + companion object { private val NON_STABLE_BOUNDS_MOCK = Rect(50, 55, 100, 100) private val STABLE_BOUNDS_MOCK = Rect(0, 0, 100, 100) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt index 734815cdd915..9a9d05a72442 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/TilingDividerViewTest.kt @@ -19,9 +19,12 @@ package com.android.wm.shell.windowdecor.tiling import android.graphics.Rect import android.os.SystemClock import android.testing.AndroidTestingRunner +import android.util.Size +import android.view.Display import android.view.InputDevice import android.view.LayoutInflater import android.view.MotionEvent +import android.view.RoundedCorner import android.view.View import androidx.test.annotation.UiThreadTest import androidx.test.filters.SmallTest @@ -46,16 +49,19 @@ class TilingDividerViewTest : ShellTestCase() { private val dividerMoveCallbackMock = mock<DividerMoveCallback>() private val viewMock = mock<View>() + private val display = mock<Display>() + private val roundedCorner = mock<RoundedCorner>() @Before @UiThreadTest fun setUp() { + whenever(display.getRoundedCorner(any())).thenReturn(roundedCorner) + whenever(roundedCorner.radius).thenReturn(CORNER_RADIUS) tilingDividerView = LayoutInflater.from(mContext).inflate(R.layout.tiling_split_divider, /* root= */ null) as TilingDividerView - tilingDividerView.setup(dividerMoveCallbackMock, BOUNDS) - tilingDividerView.handleStartY = 0 - tilingDividerView.handleEndY = 1500 + tilingDividerView.setup(dividerMoveCallbackMock, DIVIDER_BOUNDS, HANDLE_SIZE) + tilingDividerView.handleY = 0..1500 } @Test @@ -130,6 +136,8 @@ class TilingDividerViewTest : ShellTestCase() { } companion object { - private val BOUNDS = Rect(0, 0, 1500, 1500) + private val DIVIDER_BOUNDS = Rect(15, 0, 35, 1500) + private val HANDLE_SIZE = Size(800, 300) + private const val CORNER_RADIUS = 15 } } diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp index 20301d2c76ec..1c6d886f18b7 100644 --- a/libs/hwui/jni/ColorFilter.cpp +++ b/libs/hwui/jni/ColorFilter.cpp @@ -163,6 +163,20 @@ public: filter->updateChild(env, name.c_str(), child); } } + + static void RuntimeColorFilter_updateInputColorFilter(JNIEnv* env, jobject, + jlong colorFilterPtr, jstring childName, + jlong childFilterPtr) { + auto* filter = reinterpret_cast<RuntimeColorFilter*>(colorFilterPtr); + ScopedUtfChars name(env, childName); + auto* child = reinterpret_cast<ColorFilter*>(childFilterPtr); + if (filter && child) { + auto childInput = child->getInstance(); + if (childInput) { + filter->updateChild(env, name.c_str(), childInput.release()); + } + } + } }; static const JNINativeMethod colorfilter_methods[] = { @@ -193,7 +207,9 @@ static const JNINativeMethod runtime_color_filter_methods[] = { {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsInts}, {"nativeUpdateChild", "(JLjava/lang/String;J)V", - (void*)ColorFilterGlue::RuntimeColorFilter_updateChild}}; + (void*)ColorFilterGlue::RuntimeColorFilter_updateChild}, + {"nativeUpdateInputColorFilter", "(JLjava/lang/String;J)V", + (void*)ColorFilterGlue::RuntimeColorFilter_updateInputColorFilter}}; int register_android_graphics_ColorFilter(JNIEnv* env) { android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods, diff --git a/libs/hwui/jni/RuntimeXfermode.cpp b/libs/hwui/jni/RuntimeXfermode.cpp index c1c8964bf5eb..17bee8fb8b15 100644 --- a/libs/hwui/jni/RuntimeXfermode.cpp +++ b/libs/hwui/jni/RuntimeXfermode.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "ColorFilter.h" #include "GraphicsJNI.h" #include "RuntimeEffectUtils.h" #include "SkBlender.h" @@ -93,6 +94,19 @@ static void RuntimeXfermode_updateChild(JNIEnv* env, jobject, jlong builderPtr, } } +static void RuntimeXfermode_updateColorFilter(JNIEnv* env, jobject, jlong builderPtr, + jstring childName, jlong colorFilterPtr) { + auto* builder = reinterpret_cast<SkRuntimeEffectBuilder*>(builderPtr); + ScopedUtfChars name(env, childName); + auto* child = reinterpret_cast<ColorFilter*>(colorFilterPtr); + if (child) { + auto childInput = child->getInstance(); + if (childInput) { + UpdateChild(env, builder, name.c_str(), childInput.release()); + } + } +} + static const JNINativeMethod gRuntimeXfermodeMethods[] = { {"nativeGetFinalizer", "()J", (void*)RuntimeXfermode_getNativeFinalizer}, {"nativeCreateBlenderBuilder", "(Ljava/lang/String;)J", @@ -107,6 +121,8 @@ static const JNINativeMethod gRuntimeXfermodeMethods[] = { {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)RuntimeXfermode_updateIntUniforms}, {"nativeUpdateChild", "(JLjava/lang/String;J)V", (void*)RuntimeXfermode_updateChild}, + {"nativeUpdateColorFilter", "(JLjava/lang/String;J)V", + (void*)RuntimeXfermode_updateColorFilter}, }; int register_android_graphics_RuntimeXfermode(JNIEnv* env) { diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index 018c2b1374d0..eadb9dea566f 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -1,5 +1,6 @@ #include <vector> +#include "ColorFilter.h" #include "Gainmap.h" #include "GraphicsJNI.h" #include "RuntimeEffectUtils.h" @@ -331,6 +332,15 @@ static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder builder->child(name.c_str()) = sk_ref_sp(shader); } +static void RuntimeShader_updateColorFilter(JNIEnv* env, jobject, jlong shaderBuilder, + jstring jUniformName, jlong colorFilterHandle) { + SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder); + ScopedUtfChars name(env, jUniformName); + auto* childEffect = reinterpret_cast<ColorFilter*>(colorFilterHandle); + + UpdateChild(env, builder, name.c_str(), childEffect->getInstance().release()); +} + static void RuntimeShader_updateChild(JNIEnv* env, jobject, jlong shaderBuilder, jstring jUniformName, jlong childHandle) { SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder); @@ -380,6 +390,8 @@ static const JNINativeMethod gRuntimeShaderMethods[] = { {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)RuntimeShader_updateIntUniforms}, {"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader}, + {"nativeUpdateColorFilter", "(JLjava/lang/String;J)V", + (void*)RuntimeShader_updateColorFilter}, {"nativeUpdateChild", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateChild}, }; diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp index d9dc8eb8c1b1..a0291a9e806f 100644 --- a/libs/hwui/jni/android_graphics_RenderNode.cpp +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -594,14 +594,12 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, Matrix4 transform; SkIRect clipBounds; uirenderer::Rect initialClipBounds; - const auto clipFlags = props.getClippingFlags(); if (enableClip) { - if (clipFlags) { - props.getClippingRectForFlags(clipFlags, &initialClipBounds); - } else { - // Works for RenderNode::damageSelf() - initialClipBounds.set(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX); - } + // SurfaceView never draws beyond its bounds regardless of if it can or not, + // so if clip-to-bounds is disabled just use the bounds as the starting point + // regardless + const auto clipFlags = props.getClippingFlags(); + props.getClippingRectForFlags(clipFlags | CLIP_TO_BOUNDS, &initialClipBounds); clipBounds = info.damageAccumulator ->computeClipAndTransform(initialClipBounds.toSkRect(), &transform) diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java index c085b8985783..dba9cc95d902 100644 --- a/media/java/android/media/AudioPlaybackConfiguration.java +++ b/media/java/android/media/AudioPlaybackConfiguration.java @@ -283,8 +283,19 @@ public final class AudioPlaybackConfiguration implements Parcelable { * Flag used when playback is muted by AppOpsManager#OP_PLAY_AUDIO. */ @SystemApi + @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API) + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public static final int MUTED_BY_OP_PLAY_AUDIO = (1 << 3); + /** + * @hide + * Flag used when playback is muted by AppOpsManager#OP_PLAY_AUDIO. + * @deprecated see {@link MUTED_BY_OP_PLAY_AUDIO} + */ + @SystemApi + @Deprecated + @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - public static final int MUTED_BY_APP_OPS = (1 << 3); + public static final int MUTED_BY_APP_OPS = MUTED_BY_OP_PLAY_AUDIO; /** * @hide * Flag used when muted by client volume. @@ -311,12 +322,21 @@ public final class AudioPlaybackConfiguration implements Parcelable { @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_PORT_VOLUME = (1 << 6); + /** + * @hide + * Flag used when playback is muted by AppOpsManager#OP_CONTROL_AUDIO. + */ + @SystemApi + @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API) + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public static final int MUTED_BY_OP_CONTROL_AUDIO = (1 << 7); + /** @hide */ @IntDef( flag = true, value = {MUTED_BY_MASTER, MUTED_BY_STREAM_VOLUME, MUTED_BY_STREAM_MUTED, - MUTED_BY_APP_OPS, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER, - MUTED_BY_PORT_VOLUME}) + MUTED_BY_OP_PLAY_AUDIO, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER, + MUTED_BY_PORT_VOLUME, MUTED_BY_OP_CONTROL_AUDIO}) @Retention(RetentionPolicy.SOURCE) public @interface PlayerMuteEvent { } @@ -761,7 +781,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { private boolean isMuteAffectingActiveState() { return (mMutedState & MUTED_BY_CLIENT_VOLUME) != 0 || (mMutedState & MUTED_BY_VOLUME_SHAPER) != 0 - || (mMutedState & MUTED_BY_APP_OPS) != 0; + || (mMutedState & MUTED_BY_OP_PLAY_AUDIO) != 0; } /** @@ -902,8 +922,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { if ((mMutedState & MUTED_BY_STREAM_MUTED) != 0) { apcToString.append("streamMute "); } - if ((mMutedState & MUTED_BY_APP_OPS) != 0) { - apcToString.append("appOps "); + if ((mMutedState & MUTED_BY_OP_PLAY_AUDIO) != 0) { + apcToString.append("opPlayAudio "); } if ((mMutedState & MUTED_BY_CLIENT_VOLUME) != 0) { apcToString.append("clientVolume "); @@ -914,6 +934,9 @@ public final class AudioPlaybackConfiguration implements Parcelable { if ((mMutedState & MUTED_BY_PORT_VOLUME) != 0) { apcToString.append("portVolume "); } + if ((mMutedState & MUTED_BY_OP_CONTROL_AUDIO) != 0) { + apcToString.append("opControlAudio "); + } } apcToString.append(" ").append(mFormatInfo); } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 08b0dd3fb11c..54a87ad7fd39 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -640,6 +640,9 @@ interface IAudioService { boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af); + /* Returns a List<Integer> */ + List getSpatializedChannelMasks(); + void registerSpatializerCallback(in ISpatializerCallback cb); void unregisterSpatializerCallback(in ISpatializerCallback cb); diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 4f94c3e984db..50387548b4ab 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -16,12 +16,12 @@ package android.media; - import static android.media.audio.Flags.FLAG_IAMF_DEFINITIONS_API; +import static android.media.codec.Flags.FLAG_APV_SUPPORT; import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC; import static android.media.codec.Flags.FLAG_NUM_INPUT_SLOTS; import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST; -import static android.media.codec.Flags.FLAG_APV_SUPPORT; +import static android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES; import static com.android.media.codec.flags.Flags.FLAG_CODEC_IMPORTANCE; import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME; @@ -1796,6 +1796,27 @@ public final class MediaFormat { public static final String KEY_NUM_SLOTS = "num-slots"; /** + * A key describing the picture profile ID to be applied to {@link MediaCodec}. + * <p> + * The associated value is a string. + * <p> + * @see {@link android.media.quality.PictureProfile} + * @see {@link android.media.quality.PictureProfile#getProfileId} + */ + @FlaggedApi(FLAG_APPLY_PICTURE_PROFILES) + public static final String KEY_PICTURE_PROFILE_ID = "picture-profile-id"; + + /** + * A key describing the picture profile instance to be applied to {@link MediaCodec}. + * <p> + * The associated value is an instance of {@link android.media.quality.PictureProfile}. + * <p> + * @see {@link android.media.quality.PictureProfile} + */ + @FlaggedApi(FLAG_APPLY_PICTURE_PROFILES) + public static final String KEY_PICTURE_PROFILE_INSTANCE = "picture-profile-instance"; + + /** * QpOffsetRect constitutes the metadata required for encoding a region of interest in an * image or a video frame. The region of interest is represented by a rectangle. The four * integer coordinates of the rectangle are stored in fields left, top, right, bottom. diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java index 99fcaf29688c..95c4ad3c3a7f 100644 --- a/media/java/android/media/Spatializer.java +++ b/media/java/android/media/Spatializer.java @@ -16,7 +16,10 @@ package android.media; +import static android.media.audio.Flags.FLAG_SPATIALIZER_CAPABILITIES; + import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -35,6 +38,7 @@ import com.android.internal.annotations.GuardedBy; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; @@ -527,6 +531,28 @@ public class Spatializer { } /** + * Returns a list of channel masks that represent the widest channel masks the spatializer + * is capable of rendering with individual channel positions. + * For instance a spatializer may only support virtual speaker positions for 5.1, it would + * therefore return {@link AudioFormat#CHANNEL_OUT_5POINT1}. But it would still return + * <code>true</code> when querying {@link #canBeSpatialized(AudioAttributes, AudioFormat)} it + * with a channel mask of {@link AudioFormat#CHANNEL_OUT_7POINT1POINT2}: the sound present + * in each channel would still be heard, but the sounds from the rear, side and top pairs would + * be mixed together, and be spatialized at the same location. + * @return a list of channel masks following the <code>CHANNEL_OUT_*</code> output channel + * definitions found in {@link AudioFormat}. + */ + @FlaggedApi(FLAG_SPATIALIZER_CAPABILITIES) + public @NonNull List<Integer> getSpatializedChannelMasks() { + try { + return mAm.getService().getSpatializedChannelMasks(); + } catch (RemoteException e) { + Log.e(TAG, "Error querying getSpatializedChannelMasks", e); + return Collections.emptyList(); + } + } + + /** * Adds a listener to be notified of changes to the enabled state of the * {@code Spatializer}. * @param executor the {@code Executor} handling the callback diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig index d8a8c8b0ee2a..bbe8e4ed7b34 100644 --- a/media/java/android/media/flags/media_better_together.aconfig +++ b/media/java/android/media/flags/media_better_together.aconfig @@ -141,6 +141,14 @@ flag { } flag { + name: "enable_route_visibility_control_api" + namespace: "media_better_together" + description: "API changes to allow more control over route visibility by route providers" + bug: "367799834" + is_exported: true +} + +flag { name: "enable_screen_off_scanning" is_exported: true namespace: "media_solutions" diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl index 8ee966ddc377..dacd1bd06cc0 100644 --- a/media/java/android/media/projection/IMediaProjection.aidl +++ b/media/java/android/media/projection/IMediaProjection.aidl @@ -17,13 +17,14 @@ package android.media.projection; import android.media.projection.IMediaProjectionCallback; +import android.media.projection.StopReason; import android.os.IBinder; import android.app.ActivityOptions.LaunchCookie; /** {@hide} */ interface IMediaProjection { void start(IMediaProjectionCallback callback); - void stop(); + void stop(StopReason stopReason); boolean canProjectAudio(); boolean canProjectVideo(); diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl index b104972572b9..1d92eabb726d 100644 --- a/media/java/android/media/projection/IMediaProjectionManager.aidl +++ b/media/java/android/media/projection/IMediaProjectionManager.aidl @@ -16,11 +16,13 @@ package android.media.projection; +import android.graphics.Rect; import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionCallback; import android.media.projection.IMediaProjectionWatcherCallback; import android.media.projection.MediaProjectionInfo; import android.media.projection.ReviewGrantedConsentResult; +import android.media.projection.StopReason; import android.os.IBinder; import android.view.ContentRecordingSession; @@ -107,12 +109,7 @@ interface IMediaProjectionManager { @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - void stopActiveProjection(); - - @EnforcePermission("MANAGE_MEDIA_PROJECTION") - @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" - + ".permission.MANAGE_MEDIA_PROJECTION)") - void notifyActiveProjectionCapturedContentResized(int width, int height); + void stopActiveProjection(in StopReason stopReason); @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" @@ -227,5 +224,11 @@ interface IMediaProjectionManager { @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - void notifyWindowingModeChanged(int contentToRecord, int targetProcessUid, int windowingMode); + oneway void notifyWindowingModeChanged(int contentToRecord, int targetProcessUid, int windowingMode); + + @EnforcePermission("MANAGE_MEDIA_PROJECTION") + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MANAGE_MEDIA_PROJECTION)") + oneway void notifyCaptureBoundsChanged(int contentToRecord, int targetProcessUid, + in Rect captureBounds); } diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java index 4114f5359ace..f7f10df5786a 100644 --- a/media/java/android/media/projection/MediaProjection.java +++ b/media/java/android/media/projection/MediaProjection.java @@ -317,7 +317,7 @@ public final class MediaProjection { public void stop() { try { Log.d(TAG, "Content Recording: stopping projection"); - mImpl.stop(); + mImpl.stop(StopReason.STOP_HOST_APP); } catch (RemoteException e) { Log.e(TAG, "Unable to stop projection", e); } diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java index dc55e41fb74c..9cc2cca441a4 100644 --- a/media/java/android/media/projection/MediaProjectionManager.java +++ b/media/java/android/media/projection/MediaProjectionManager.java @@ -297,10 +297,10 @@ public final class MediaProjectionManager { * Stop the current projection if there is one. * @hide */ - public void stopActiveProjection() { + public void stopActiveProjection(@StopReason int stopReason) { try { Log.d(TAG, "Content Recording: stopping active projection"); - mService.stopActiveProjection(); + mService.stopActiveProjection(stopReason); } catch (RemoteException e) { Log.e(TAG, "Unable to stop the currently active media projection", e); } diff --git a/media/java/android/media/projection/StopReason.aidl b/media/java/android/media/projection/StopReason.aidl new file mode 100644 index 000000000000..8611def3d2b9 --- /dev/null +++ b/media/java/android/media/projection/StopReason.aidl @@ -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.media.projection; + +/** + * Identifies the reason for a MediaProjection being stopped (for metric logging purposes) + * @hide + */ +@Backing(type="int") +enum StopReason { + STOP_UNKNOWN = 0, + STOP_HOST_APP = 1, + STOP_TARGET_REMOVED = 2, + STOP_DEVICE_LOCKED = 3, + STOP_PRIVACY_CHIP = 4, + STOP_QS_TILE = 5, + STOP_USER_SWITCH = 6, + STOP_FOREGROUND_SERVICE_CHANGE = 7, + STOP_NEW_PROJECTION = 8, + STOP_NEW_MEDIA_ROUTE = 9, + STOP_ERROR = 10, +} diff --git a/media/java/android/media/tv/extension/analog/IAnalogAttributeInterface.aidl b/media/java/android/media/tv/extension/analog/IAnalogAttributeInterface.aidl new file mode 100644 index 000000000000..550acba8ed65 --- /dev/null +++ b/media/java/android/media/tv/extension/analog/IAnalogAttributeInterface.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.analog; + +/** + * @hide + */ +interface IAnalogAttributeInterface { + int getVersion(); + void setColorSystemCapability(in String[] list); + String[] getColorSystemCapability(); +} diff --git a/media/java/android/media/tv/extension/cam/ICamAppInfoListener.aidl b/media/java/android/media/tv/extension/cam/ICamAppInfoListener.aidl new file mode 100644 index 000000000000..73ae209062d6 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamAppInfoListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ICamAppInfoListener { + void onCamAppInfoChanged(int slotId, in Bundle appInfo); +} diff --git a/media/java/android/media/tv/extension/cam/ICamAppInfoService.aidl b/media/java/android/media/tv/extension/cam/ICamAppInfoService.aidl new file mode 100644 index 000000000000..d3a03bc80056 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamAppInfoService.aidl @@ -0,0 +1,32 @@ +/* + * 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.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamAppInfoListener; +import android.os.Bundle; + +/** + * @hide + */ +interface ICamAppInfoService { + // Register ICamAppInfoListener to get CICAM Application Information updates. + void addCamAppInfoListener(ICamAppInfoListener listener); + // Unregister ICamAppInfoListener and stop get Application Information notify. + void removeCamAppInfoListener(ICamAppInfoListener listener); + // Get the Application Information of the CICAM. + int getCamAppInfo(int slotId, out Bundle appInfo); +} diff --git a/media/java/android/media/tv/extension/cam/ICamDrmInfoListener.aidl b/media/java/android/media/tv/extension/cam/ICamDrmInfoListener.aidl new file mode 100644 index 000000000000..c727837ed07e --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamDrmInfoListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ICamDrmInfoListener { + void onCamDrmInfoChanged(int slotId, in Bundle camDrmInfo); +} diff --git a/media/java/android/media/tv/extension/cam/ICamHostControlAskReleaseReplyCallback.aidl b/media/java/android/media/tv/extension/cam/ICamHostControlAskReleaseReplyCallback.aidl new file mode 100644 index 000000000000..83f2c01e4681 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamHostControlAskReleaseReplyCallback.aidl @@ -0,0 +1,24 @@ +/* + * 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.media.tv.extension.cam; + +/** + * @hide + */ +oneway interface ICamHostControlAskReleaseReplyCallback { + void onAskReleaseReply(String sessionToken, int replyStatus); +} diff --git a/media/java/android/media/tv/extension/cam/ICamHostControlInfoListener.aidl b/media/java/android/media/tv/extension/cam/ICamHostControlInfoListener.aidl new file mode 100644 index 000000000000..f48ca57cccdc --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamHostControlInfoListener.aidl @@ -0,0 +1,24 @@ +/* + * 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.media.tv.extension.cam; + +/** + * @hide + */ +oneway interface ICamHostControlInfoListener { + void onCamHostControlInfoChanged(String sessionToken, int sessionStatus); +} diff --git a/media/java/android/media/tv/extension/cam/ICamHostControlService.aidl b/media/java/android/media/tv/extension/cam/ICamHostControlService.aidl new file mode 100644 index 000000000000..6f6c376c1ffa --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamHostControlService.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.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamHostControlAskReleaseReplyCallback; +import android.media.tv.extension.cam.ICamHostControlInfoListener; + +/** + * @hide + */ +interface ICamHostControlService { + // Register the listener to monitor host control session updates. + void addCamHostcontrolInfoListener(ICamHostControlInfoListener listener); + // Unregister ICamHostControlInfoListener and stop monitoring. + void removeCamHostcontrolInfoListener(ICamHostControlInfoListener listener); + // Request CICAM to release the resource. + int sendCamHostControlAskRelease(String sessionToken, + ICamHostControlAskReleaseReplyCallback callback); + // Enable/disable the host control mode. + void setHostControlMode(String sessionToken, boolean enable); +} diff --git a/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlag.aidl b/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlag.aidl new file mode 100644 index 000000000000..25a78b99d659 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlag.aidl @@ -0,0 +1,32 @@ +/* + * 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.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamHostControlTuneQuietlyFlagListener; +import android.os.Bundle; + +/** + * @hide + */ +interface ICamHostControlTuneQuietlyFlag { + // Register listener to notify host control tune_quietly_flag. + void addHcTuneQuietlyFlagListener(ICamHostControlTuneQuietlyFlagListener listener); + // Remove listener and stop monitor host control tune_quietly_flag. + void removeHcTuneQuietlyFlagListener(ICamHostControlTuneQuietlyFlagListener listener); + // Returns host control tune_quietly_flag value. + Bundle getHcTuneQuietlyFlag(String sessionToken); +} diff --git a/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlagListener.aidl b/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlagListener.aidl new file mode 100644 index 000000000000..5967124e5202 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlagListener.aidl @@ -0,0 +1,24 @@ +/* + * 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.media.tv.extension.cam; + +/** + * @hide + */ +oneway interface ICamHostControlTuneQuietlyFlagListener { + void onHcTuneQuietlyFlagChanged(String sessionToken, int tuneQuietlyFlag); +} diff --git a/media/java/android/media/tv/extension/cam/ICamInfoListener.aidl b/media/java/android/media/tv/extension/cam/ICamInfoListener.aidl new file mode 100644 index 000000000000..5410bffaa92d --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamInfoListener.aidl @@ -0,0 +1,28 @@ +/* + * 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.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ICamInfoListener { + void onCamInfoChanged(int slotId, in Bundle updatedCamInfo); + void onSlotInfoChanged(int slotId, in Bundle updatedSlotInfo); + void onNewTypeCamInsert(int slotId, in Bundle newCamType); +} diff --git a/media/java/android/media/tv/extension/cam/ICamMonitoringService.aidl b/media/java/android/media/tv/extension/cam/ICamMonitoringService.aidl new file mode 100644 index 000000000000..7b8014c5880f --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamMonitoringService.aidl @@ -0,0 +1,38 @@ +/* + * 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.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamInfoListener; +import android.os.Bundle; + +/** + * @hide + */ +interface ICamMonitoringService { + // Register a listener for slot/CAM info updates. + void addCamInfoListener(in ICamInfoListener listener); + // Unregister a listener for slot/CAM info updates. + void removeCamInfoListener(in ICamInfoListener listener); + // Get CAM information for the specified slot. + Bundle getCamInfo(int slotId); + // Get slot information. + Bundle getSlotInfo(int slotId); + // Returns list of slot Ids. + int[] getSlotIds(); + // Check if the country supports CAM. + boolean isCamSupported(); +} diff --git a/media/java/android/media/tv/extension/cam/ICamPinCapabilityListener.aidl b/media/java/android/media/tv/extension/cam/ICamPinCapabilityListener.aidl new file mode 100644 index 000000000000..f92304af9ade --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamPinCapabilityListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ICamPinCapabilityListener { + void onCamPinCapabilityChanged(int slotId, in Bundle bundle); +} diff --git a/media/java/android/media/tv/extension/cam/ICamPinService.aidl b/media/java/android/media/tv/extension/cam/ICamPinService.aidl new file mode 100644 index 000000000000..3f6cb2813c74 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamPinService.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.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamPinCapabilityListener; +import android.media.tv.extension.cam.ICamPinStatusListener; +import android.os.Bundle; + +/** + * @hide + */ +interface ICamPinService { + // Register ICamPinCapabilityListener to get CICAM updates. + void addCamPinCapabilityListener(in ICamPinCapabilityListener listener); + // Unregister ICamPinCapabilityListener and stop monitor PIN status and PIN capability. + void removeCamPinCapabilityListener(in ICamPinCapabilityListener listener); + // Send the PinCode that needs to be validated by CICAM. + int requestCamPinValidation(int slotId, in int[] pinCode, in ICamPinStatusListener listener); + // Get the PIN capabilities of the CICAM. + int getCamPinCapability(int slotId, out Bundle camPinCapability); +} diff --git a/media/java/android/media/tv/extension/cam/ICamPinStatusListener.aidl b/media/java/android/media/tv/extension/cam/ICamPinStatusListener.aidl new file mode 100644 index 000000000000..efbc394897d3 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamPinStatusListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ICamPinStatusListener { + void onCamPinValidationReply(int slotId, in Bundle bundle); +} diff --git a/media/java/android/media/tv/extension/cam/ICamProfileInterface.aidl b/media/java/android/media/tv/extension/cam/ICamProfileInterface.aidl new file mode 100644 index 000000000000..3f1d40caa9c9 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamProfileInterface.aidl @@ -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 android.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +interface ICamProfileInterface { + // Get CAM service update information for special slot. + Bundle getCamServiceUpdateInfo(int slotNumber); + // Request CAM TIS resend cam info update broadcast message when APK boot up. + void requestResendProfileInfoBroadcastACON(); +} diff --git a/media/java/android/media/tv/extension/cam/IContentControlService.aidl b/media/java/android/media/tv/extension/cam/IContentControlService.aidl new file mode 100644 index 000000000000..6db79abacb76 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/IContentControlService.aidl @@ -0,0 +1,32 @@ +/* + * 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.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamDrmInfoListener; +import android.os.Bundle; + +/** + * @hide + */ +interface IContentControlService { + // Register the listener to notify the DRM info changed by the CICAM. + void addCamDrmInfoListener(in ICamDrmInfoListener listener); + // Unregister listener to stop monitor DRM Info. + void removeCamDrmInfoListener(in ICamDrmInfoListener listener); + // Get the DRM Info of current watching channel. + int getCamDrmInfo(int slotId, out Bundle camDrmInfo); +} diff --git a/media/java/android/media/tv/extension/cam/IEnterMenuErrorCallback.aidl b/media/java/android/media/tv/extension/cam/IEnterMenuErrorCallback.aidl new file mode 100644 index 000000000000..ff61ddcbcc2f --- /dev/null +++ b/media/java/android/media/tv/extension/cam/IEnterMenuErrorCallback.aidl @@ -0,0 +1,24 @@ +/* + * 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.media.tv.extension.cam; + +/** + * @hide + */ +oneway interface IEnterMenuErrorCallback { + void onAppInfoEnterMenuError(); +} diff --git a/media/java/android/media/tv/extension/cam/IMmiInterface.aidl b/media/java/android/media/tv/extension/cam/IMmiInterface.aidl new file mode 100644 index 000000000000..17a2a9c6771f --- /dev/null +++ b/media/java/android/media/tv/extension/cam/IMmiInterface.aidl @@ -0,0 +1,32 @@ +/* + * 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.media.tv.extension.cam; + +import android.media.tv.extension.cam.IEnterMenuErrorCallback; +import android.media.tv.extension.cam.IMmiSession; +import android.media.tv.extension.cam.IMmiStatusCallback; + + +/** + * @hide + */ +interface IMmiInterface { + // Open a session for MMI. + IMmiSession openSession(int slotId, IMmiStatusCallback callback); + // Request to display CI Module setup screen. + void appInfoEnterMenu(int slotId, IEnterMenuErrorCallback callback); +} diff --git a/media/java/android/media/tv/extension/cam/IMmiSession.aidl b/media/java/android/media/tv/extension/cam/IMmiSession.aidl new file mode 100644 index 000000000000..c4f276219c55 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/IMmiSession.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +/** + * @hide + */ +interface IMmiSession { + // Send user answers to CAM on the MMI Menu screen. + void setMenuListAnswer(int response); + // Send user answers to CAM on the MMI Enq screen. + void setEnquiryAnswer(int answerId, String answer); + // Send CloseMmi APDU to Cam. + void closeMmi(); + // Release MMI session. + void close(); +} diff --git a/media/java/android/media/tv/extension/cam/IMmiStatusCallback.aidl b/media/java/android/media/tv/extension/cam/IMmiStatusCallback.aidl new file mode 100644 index 000000000000..3e68ced6a1ce --- /dev/null +++ b/media/java/android/media/tv/extension/cam/IMmiStatusCallback.aidl @@ -0,0 +1,28 @@ +/* + * 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.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface IMmiStatusCallback { + void onMmiEnq(in Bundle request); + void onMmiListMenu(in Bundle request); + void onMmiClose(); +} diff --git a/media/java/android/media/tv/extension/clienttoken/IClientToken.aidl b/media/java/android/media/tv/extension/clienttoken/IClientToken.aidl new file mode 100644 index 000000000000..fa701b3c01ad --- /dev/null +++ b/media/java/android/media/tv/extension/clienttoken/IClientToken.aidl @@ -0,0 +1,24 @@ +/* + * 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.media.tv.extension.clienttoken; + +/** + * @hide + */ +interface IClientToken { + String generateClientToken(); +} diff --git a/media/java/android/media/tv/extension/event/IEventDownload.aidl b/media/java/android/media/tv/extension/event/IEventDownload.aidl new file mode 100644 index 000000000000..29c7553e1014 --- /dev/null +++ b/media/java/android/media/tv/extension/event/IEventDownload.aidl @@ -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 android.media.tv.extension.event; + +import android.media.tv.extension.event.IEventDownloadListener; +import android.os.Bundle; +import android.os.IBinder; + +/** + * @hide + */ +interface IEventDownload { + // Create an event download session and return it as a Ibinder for DVB/DTMB + IBinder createSession(in Bundle eventDownloadParams, in IEventDownloadListener listener); +} diff --git a/media/java/android/media/tv/extension/event/IEventDownloadListener.aidl b/media/java/android/media/tv/extension/event/IEventDownloadListener.aidl new file mode 100644 index 000000000000..6d7d61f15d27 --- /dev/null +++ b/media/java/android/media/tv/extension/event/IEventDownloadListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.event; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface IEventDownloadListener { + void onCompleted(in Bundle status); +} diff --git a/media/java/android/media/tv/extension/event/IEventDownloadSession.aidl b/media/java/android/media/tv/extension/event/IEventDownloadSession.aidl new file mode 100644 index 000000000000..fe7ee37b324b --- /dev/null +++ b/media/java/android/media/tv/extension/event/IEventDownloadSession.aidl @@ -0,0 +1,38 @@ +/* + * 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.media.tv.extension.event; + +import android.net.Uri; +import android.os.Bundle; + +/** + * @hide + */ +interface IEventDownloadSession { + // Determine to execute barker channel or silent tune flow for related service type + int isBarkerOrSequentialDownloadByServiceType(in Bundle eventDownloadParams); + // Determine whether to start barker channel or silent tune flow. + int isBarkerOrSequentialDownloadByServiceRecord(in Bundle eventDownloadParams); + // Start event download. + void startTuningMultiplex(in Uri channelUri); + // Set active window channels. + void setActiveWindowChannelInfo(in Uri[] activeWinChannelInfos); + // Cancel barker channel or silent tune flow. + void cancel(); + // Release barker channel or silent tune flow. + void release(); +} diff --git a/media/java/android/media/tv/extension/event/IEventMonitor.aidl b/media/java/android/media/tv/extension/event/IEventMonitor.aidl new file mode 100644 index 000000000000..f6e7bd134e78 --- /dev/null +++ b/media/java/android/media/tv/extension/event/IEventMonitor.aidl @@ -0,0 +1,43 @@ +/* + * 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.media.tv.extension.event; + +import android.media.tv.extension.event.IEventMonitorListener; +import android.net.Uri; +import android.os.Bundle; + +/** + * @hide + */ +interface IEventMonitor { + // Get present event information. + Bundle getPresentEventInfo(long channelDbId); + // Add present event information listener. + void addPresentEventInfoListener(in IEventMonitorListener listener); + // Remove present event information listener. + void removePresentEventInfoListener(in IEventMonitorListener listener); + // Get following event information. + Bundle getFollowingEventInfo(long channelDbId); + // Add following event information listener. + void addFollowingEventInfoListener(in IEventMonitorListener listener); + // Remove following event information listener. + void removeFollowingEventInfoListener(in IEventMonitorListener listener); + // Get SDT guidance information. + Bundle getSdtGuidanceInfo(long channelDbId); + // Set Event Background channel list info. + void setBgmTuneChannelInfo(in Uri[] tuneChannelInfos); +} diff --git a/media/java/android/media/tv/extension/event/IEventMonitorListener.aidl b/media/java/android/media/tv/extension/event/IEventMonitorListener.aidl new file mode 100644 index 000000000000..a00e5422a7e0 --- /dev/null +++ b/media/java/android/media/tv/extension/event/IEventMonitorListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.event; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface IEventMonitorListener { + void onInfoChanged(long channelDbId, in Bundle eventinfo); +} diff --git a/media/java/android/media/tv/extension/scan/IFavoriteNetwork.aidl b/media/java/android/media/tv/extension/scan/IFavoriteNetwork.aidl new file mode 100644 index 000000000000..ff78aa4be39c --- /dev/null +++ b/media/java/android/media/tv/extension/scan/IFavoriteNetwork.aidl @@ -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.media.tv.extension.scan; + +import android.media.tv.extension.scan.IFavoriteNetworkListener; +import android.os.Bundle; + +/** + * Country: Norway + * Broadcast Type: BROADCAST_TYPE_DVB_T + * (Operator: RiksTV) + * + * @hide + */ +interface IFavoriteNetwork { + // Get the favorite network information,If there are no conflicts, the array of Bundle is empty. + Bundle[] getFavoriteNetworks(); + // Select and set one of two or more favorite networks detected by the service scan. + int setFavoriteNetwork(in Bundle favoriteNetworkSettings); + // Set the listener to be invoked when two or more favorite networks are detected. + int setListener(in IFavoriteNetworkListener listener); +} diff --git a/media/java/android/media/tv/extension/scan/IFavoriteNetworkListener.aidl b/media/java/android/media/tv/extension/scan/IFavoriteNetworkListener.aidl new file mode 100644 index 000000000000..699422493dd6 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/IFavoriteNetworkListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.scan; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface IFavoriteNetworkListener { + void onDetectFavoriteNetwork(in Bundle detectFavoriteNetworks); +} diff --git a/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl b/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl new file mode 100644 index 000000000000..cdf6e23f4b47 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/IHDPlusInfo.aidl @@ -0,0 +1,25 @@ +/* + * 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.media.tv.extension.scan; + +/** + * @hide + */ +interface IHDPlusInfo { + // Specifying a HDPlusInfo and start a network scan. + int setHDPlusInfo(String isBlindScanContinue, String isHDMode); +} diff --git a/media/java/android/media/tv/extension/scan/ILcnConflict.aidl b/media/java/android/media/tv/extension/scan/ILcnConflict.aidl new file mode 100644 index 000000000000..5dff39eb5920 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/ILcnConflict.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.media.tv.extension.scan; + +import android.media.tv.extension.scan.ILcnConflictListener; +import android.os.Bundle; + +/** + * Country: Italy, France + * Broadcast Type: BROADCAST_TYPE_DVB_T + * + * @hide + */ +interface ILcnConflict { + // Get the LCN conflict groups information, If there are no conflicts, the array of Bundle is empty. + Bundle[] getLcnConflictGroups(); + // Resolve LCN conflicts caused by service scans. + int resolveLcnConflict(in Bundle[] lcnConflictSettings); + // Set the listener to be invoked the LCN conflict event. + int setListener(in ILcnConflictListener listener); +} diff --git a/media/java/android/media/tv/extension/scan/ILcnConflictListener.aidl b/media/java/android/media/tv/extension/scan/ILcnConflictListener.aidl new file mode 100644 index 000000000000..6bbbeb8e1e06 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/ILcnConflictListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.scan; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ILcnConflictListener { + void onDetectLcnConflict(in Bundle detectLcnConflicts); +} diff --git a/media/java/android/media/tv/extension/scan/ILcnV2ChannelList.aidl b/media/java/android/media/tv/extension/scan/ILcnV2ChannelList.aidl new file mode 100644 index 000000000000..f9a9d345a575 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/ILcnV2ChannelList.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.media.tv.extension.scan; + +import android.media.tv.extension.scan.ILcnV2ChannelListListener; +import android.os.Bundle; + +/** + * Country: (NorDig etc.) + * Broadcast Type: BROADCAST_TYPE_DVB_T, BROADCAST_TYPE_DVB_C + * + * @hide + */ +interface ILcnV2ChannelList { + // Get the LCN V2 channel list information. If there are no conflicts, the array of Bundle is empty. + Bundle[] getLcnV2ChannelLists(); + // Select and set one of two or more LCN V2 channel list detected by the service scan. + int setLcnV2ChannelList(in Bundle lcnV2ChannelListSettings); + // Set the listener to be invoked when two or more LCN V2 channel list are detected. + int setListener(in ILcnV2ChannelListListener listener); +} diff --git a/media/java/android/media/tv/extension/scan/ILcnV2ChannelListListener.aidl b/media/java/android/media/tv/extension/scan/ILcnV2ChannelListListener.aidl new file mode 100644 index 000000000000..cbdb83c656f4 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/ILcnV2ChannelListListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.scan; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ILcnV2ChannelListListener { + void onDetectLcnV2ChannelList(in Bundle detectLcnV2ChannelList); +} diff --git a/media/java/android/media/tv/extension/scan/IOperatorDetection.aidl b/media/java/android/media/tv/extension/scan/IOperatorDetection.aidl new file mode 100644 index 000000000000..770f8668983e --- /dev/null +++ b/media/java/android/media/tv/extension/scan/IOperatorDetection.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.media.tv.extension.scan; + +import android.media.tv.extension.scan.IOperatorDetectionListener; +import android.os.Bundle; + +/** + * Country: Any + * Broadcast Type: BROADCAST_TYPE_DVB_S + * (Operator: M7) + * + * @hide + */ +interface IOperatorDetection { + // Set the operator selected info for scanning. + int setOperatorDetection(in Bundle operatorSelected); + // Set the listener to be invoked when one or more operator detection has been detected by + // operator detection searches. + int setListener(in IOperatorDetectionListener listener); +} diff --git a/media/java/android/media/tv/extension/scan/IOperatorDetectionListener.aidl b/media/java/android/media/tv/extension/scan/IOperatorDetectionListener.aidl new file mode 100644 index 000000000000..7dcd46177c43 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/IOperatorDetectionListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.scan; + +import android.os.Bundle; + + +/** + * @hide + */ +oneway interface IOperatorDetectionListener { + void onDetectOperatorDetectionList(in Bundle[] detectOperatorDetectionList); +} diff --git a/media/java/android/media/tv/extension/scan/IRegionChannelList.aidl b/media/java/android/media/tv/extension/scan/IRegionChannelList.aidl new file mode 100644 index 000000000000..fe755f873110 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/IRegionChannelList.aidl @@ -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 android.media.tv.extension.scan; + +import android.media.tv.extension.scan.IRegionChannelListListener; + +/** + * @hide + */ +interface IRegionChannelList { + // Set the region channel list for scanning. + int setRegionChannelList(String regionChannelList); + // Set the listener to be invoked when one or more region channel list has been detected by + // region channel list searches. + int setListener(in IRegionChannelListListener listener); +} diff --git a/media/java/android/media/tv/extension/scan/IRegionChannelListListener.aidl b/media/java/android/media/tv/extension/scan/IRegionChannelListListener.aidl new file mode 100644 index 000000000000..06b0eb5537a2 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/IRegionChannelListListener.aidl @@ -0,0 +1,24 @@ +/* + * 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.media.tv.extension.scan; + +/** + * @hide + */ +oneway interface IRegionChannelListListener { + void onDetectRegionChannelList(in String[] detectRegionChannelList); +} diff --git a/media/java/android/media/tv/extension/scan/IScanInterface.aidl b/media/java/android/media/tv/extension/scan/IScanInterface.aidl new file mode 100644 index 000000000000..b44d1d243150 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/IScanInterface.aidl @@ -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 android.media.tv.extension.scan; + +import android.media.tv.extension.scan.IScanListener; +import android.os.Bundle; + +/** + * @hide + */ +interface IScanInterface { + IBinder createSession(int broadcastType, String countryCode, String operator, + in IScanListener listener); + Bundle getParameters(int broadcastType, String countryCode, String operator, + in Bundle params); +} diff --git a/media/java/android/media/tv/extension/scan/IScanListener.aidl b/media/java/android/media/tv/extension/scan/IScanListener.aidl new file mode 100644 index 000000000000..2c4807f97c58 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/IScanListener.aidl @@ -0,0 +1,33 @@ +/* + * 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.media.tv.extension.scan; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface IScanListener { + // notify events during scan. + void onEvent(in Bundle eventArgs); + // notify the scan progress. + void onScanProgress(String scanProgress, in Bundle scanProgressInfo); + // notify the scan completion. + void onScanCompleted(int scanResult); + // notify that the temporaily held channel list is stored. + void onStoreCompleted(int storeResult); +} diff --git a/media/java/android/media/tv/extension/scan/IScanSatSearch.aidl b/media/java/android/media/tv/extension/scan/IScanSatSearch.aidl new file mode 100644 index 000000000000..b8074fc4a9bd --- /dev/null +++ b/media/java/android/media/tv/extension/scan/IScanSatSearch.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.scan; + +/** + * For satellite search function. + * @hide + */ +interface IScanSatSearch { + // Set currecnt LNB as customized LNB, default LNB is universal LNB + int setCustomizedLnb(String customizedLnb); +} diff --git a/media/java/android/media/tv/extension/scan/IScanSession.aidl b/media/java/android/media/tv/extension/scan/IScanSession.aidl new file mode 100644 index 000000000000..d42eca1342b5 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/IScanSession.aidl @@ -0,0 +1,75 @@ +/* + * 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.media.tv.extension.scan; + +import android.os.Bundle; + +/** + * @hide + */ +interface IScanSession { + // Start a service scan. + int startScan(int broadcastType, String countryCode, String operator, in int[] frequency, + String scanType, String languageCode); + // Reset the scan information held in TIS. + int resetScan(); + // Cancel scan. + int cancelScan(); + + // Get available interface for created ScanExtension interface. + String[] getAvailableExtensionInterfaceNames(); + // Get extension interface for Scan. + IBinder getExtensionInterface(String name); + + // Clear the results of the service scan from the service database. + int clearServiceList(in Bundle optionalClearParams); + // Store the results of the service scan from the service database. + int storeServiceList(); + // Get a service information specified by the service information ID. + Bundle getServiceInfo(String serviceInfoId, in String[] keys); + // Get a service information ID list. + String[] getServiceInfoIdList(); + // Get a list of service info by the filter. + Bundle getServiceInfoList(in Bundle filterInfo, in String[] keys); + // Update the service information. + int updateServiceInfo(in Bundle serviceInfo); + // Updates the service information for the specified service information ID in array list. + int updateServiceInfoByList(in Bundle[] serviceInfo); + + /* DVBI specific functions */ + // Get all of the serviceLists, parsed from Local TV storage, Broadcast, USB file discovery. + Bundle getServiceLists(); + // Users choose one serviceList from the serviceLists, and install the services. + int setServiceList(int serviceListRecId); + // Get all of the packageData, parsed from the selected serviceList XML. + Bundle getPackageData(); + // Choose the package using package id and install the corresponding services. + int setPackage(String packageId); + // Get all of the countryRegionData, parsed from the selected serviceList XML. + Bundle getCountryRegionData(); + // Choose the countryRegion using countryRegion id, and install the corresponding services. + int setCountryRegion(String regionId); + // Get all of the regionData, parsed from the selected serviceList XML. + Bundle getRegionData(); + // Choose the region using the regionData id, and install the corresponding services. + int setRegion(String regionId); + + // Get unique session token for the scan. + String getSessionToken(); + // Release scan resource, the register listener will be released. + int release(); +} diff --git a/media/java/android/media/tv/extension/scan/ITargetRegion.aidl b/media/java/android/media/tv/extension/scan/ITargetRegion.aidl new file mode 100644 index 000000000000..417e12243b82 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/ITargetRegion.aidl @@ -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.media.tv.extension.scan; + +import android.media.tv.extension.scan.ITargetRegionListener; + +import android.os.Bundle; + +/** + * Country: U.K. + * Broadcast Type: BROADCAST_TYPE_DVB_T + * + * @hide + */ +interface ITargetRegion { + // Get the target regions information. If there are no conflicts, the array of Bundle is empty. + Bundle[] getTargetRegions(); + // Select and set one of two or more target region detected by the service scan. + int setTargetRegion(in Bundle targetRegionSettings); + // Set the listener to be invoked when two or more regions are detected. + int setListener(in ITargetRegionListener listener); +} diff --git a/media/java/android/media/tv/extension/scan/ITargetRegionListener.aidl b/media/java/android/media/tv/extension/scan/ITargetRegionListener.aidl new file mode 100644 index 000000000000..9d6aa8e8ea31 --- /dev/null +++ b/media/java/android/media/tv/extension/scan/ITargetRegionListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.scan; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ITargetRegionListener { + void onDetectTargetRegion(in Bundle detectTargetRegions); +} diff --git a/media/java/android/media/tv/extension/scan/ITkgsInfo.aidl b/media/java/android/media/tv/extension/scan/ITkgsInfo.aidl new file mode 100644 index 000000000000..f25952c1cbdc --- /dev/null +++ b/media/java/android/media/tv/extension/scan/ITkgsInfo.aidl @@ -0,0 +1,28 @@ +/* + * 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.media.tv.extension.scan; + +import android.media.tv.extension.scan.ITkgsInfoListener; +import android.os.Bundle; + +/** + * @hide + */ +interface ITkgsInfo { + int setPrefServiceList(String prefServiceList); + int setTkgsInfoListener(in ITkgsInfoListener listener); +} diff --git a/media/java/android/media/tv/extension/scan/ITkgsInfoListener.aidl b/media/java/android/media/tv/extension/scan/ITkgsInfoListener.aidl new file mode 100644 index 000000000000..e3dcf2d4c5ad --- /dev/null +++ b/media/java/android/media/tv/extension/scan/ITkgsInfoListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.scan; + +/** + * @hide + */ +oneway interface ITkgsInfoListener { + void onServiceList(in String[] serviceList); + void onTableVersionUpdate(int tableVersion); + void onUserMessage(String strMessage); +} diff --git a/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdate.aidl b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdate.aidl new file mode 100644 index 000000000000..bda60edc48c1 --- /dev/null +++ b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdate.aidl @@ -0,0 +1,32 @@ +/* + * 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.media.tv.extension.scanbsu; + +import android.media.tv.extension.scanbsu.IScanBackgroundServiceUpdateListener; + +/** + * @hide + */ +interface IScanBackgroundServiceUpdate { + // Set the listener for background service update + // receives notifications for svl/tsl/nwl update during background service update. + void addBackgroundServiceUpdateListener(String clientToken, + in IScanBackgroundServiceUpdateListener listener); + // Remove the listener for background service update to stop receiving notifications + // for svl/tsl/nwl update during background service update. + void removeBackgroundServiceUpdateListener(in IScanBackgroundServiceUpdateListener listener); +} diff --git a/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdateListener.aidl b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdateListener.aidl new file mode 100644 index 000000000000..d9bcbedb234a --- /dev/null +++ b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdateListener.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.scanbsu; + +import android.os.Bundle; + +/** + * @hide + */ +interface IScanBackgroundServiceUpdateListener { + // On background service update add/delete/update svl records. + void onChannelListUpdate(String sessionToken, out Bundle[] updateInfos); + // On background service update add/delete/update nwl records. + void onNetworkListUpdate(String sessionToken, out Bundle[] updateInfos); + // On background service update add/delete/update tsl records. + void onTransportStreamingListUpdate(String sessionToken, out Bundle[] updateInfos); +} diff --git a/media/java/android/media/tv/extension/screenmode/IScreenModeSettings.aidl b/media/java/android/media/tv/extension/screenmode/IScreenModeSettings.aidl new file mode 100644 index 000000000000..57f3b4ab5017 --- /dev/null +++ b/media/java/android/media/tv/extension/screenmode/IScreenModeSettings.aidl @@ -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 android.media.tv.extension.screenmode; + +/** + * @hide + */ +interface IScreenModeSettings { + // Set screen mode information using a JSON string. + void setScreenModeSettings(String sessionToken, String setting); + // Get the overscan index which TIS session is applied. + int getOverScanIndex(String sessionToken); + // Get status that TIS session is support overscan or not. + boolean getSupportApplyOverScan(String sessionToken); +} diff --git a/media/java/android/media/tv/extension/servicedb/IChannelListTransfer.aidl b/media/java/android/media/tv/extension/servicedb/IChannelListTransfer.aidl new file mode 100644 index 000000000000..cb6aeccb5b57 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IChannelListTransfer.aidl @@ -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 android.media.tv.extension.servicedb; + +import android.os.ParcelFileDescriptor; + +/** + * @hide + */ +interface IChannelListTransfer { + // Parse XML file and import Channels information. + void importChannelList(in ParcelFileDescriptor pfd); + // Get Channels information for export and create XML file. + void exportChannelList(in ParcelFileDescriptor pfd); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceList.aidl b/media/java/android/media/tv/extension/servicedb/IServiceList.aidl new file mode 100644 index 000000000000..51daa80ccbd6 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceList.aidl @@ -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 android.media.tv.extension.servicedb; + +import android.os.Bundle; + +/** + * @hide + */ +interface IServiceList { + // Get a list of the Service list IDs quivalent to COLUMN_CHANNEL_LIST_ID + // in the Channels table of TvProvider. + String[] getServiceListIds(); + // Get the information associated with the Service list. + Bundle getServiceListInfo(String serviceListId, in String[] keys); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl new file mode 100644 index 000000000000..1b1577ffc015 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl @@ -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 android.media.tv.extension.servicedb; + +import android.media.tv.extension.servicedb.IServiceListEditListener; +import android.os.Bundle; + +/** + * @hide + */ +interface IServiceListEdit { + // Open in edit mode. Must call close() after edit is done. + int open(IServiceListEditListener listener); + // Method to close in edit mode. + int close(); + // Method to commit changes made to service database. + int commit(); + // Method to commit and close the changes. + int userEditCommit(); + + // Get a service/transportStream/Network/Satellite record information specified by + // serviceInfoId and keys from tvdb. + Bundle getServiceInfoFromDatabase(String serviceInfoId, in String[] keys); + // Get a list of all service records' information specified by serviceListId and keys from tvdb. + Bundle getServiceInfoListFromDatabase(String serviceListId, in String[] keys); + // Get a list of all service info IDs in the service list of serviceListId from tvdb. + String[] getServiceInfoIdsFromDatabase(String inServiceListId); + // Update a service information by the contents of serviceInfo; + int updateServiceInfoFromDatabase(in Bundle updateServiceInfo); + // Update all service information by the contents of serviceInfoList. + int updateServiceInfoByListFromDatabase(in Bundle[] updateServiceInfoList); + // Remove a service information of the serviceInfoId from the service list. + int removeServiceInfoFromDatabase(String serviceInfoId); + // Remove all service information of the serviceInfoId from the service list. + int removeServiceInfoByListFromDatabase(in String[] serviceInfoIdList); + // Get a list of the Service list IDs which is equivalent to COLUMN_CHANNEL_LIST_ID + // in Channels table from tv db. + String[] getServiceListChannelIds(); + // Get the information associated with the Service list Channel id. + Bundle getServiceListInfoByChannelId(String serviceListChannelId, in String[] keys); + + // Get a list of transportStream records' information specified by serviceListId and keys. + Bundle getTransportStreamInfoList(String serviceListId, in String[] keys); + // Get a list of transportStream records' information specified by serviceListId and keys + // from work db. + Bundle getTransportStreamInfoListForce(String serviceListId, in String[] keys); + + // Get a list of network records' information specified by serviceListId and keys. + Bundle getNetworkInfoList(String serviceListId, in String[] keys); + // Get a list of satellite records' information specified by serviceListId and keys. + Bundle getSatelliteInfoList(String serviceListId, in String[] keys); + + // Decompress whole bundle value of single service/transportStream/Network/Satellite record. + // RecordInfoBundle:a single record got from database by getServiceInfoFromDatabase() + String toRecordInfoByType(in Bundle recordInfoBundle, String recordType); + // Set channels(tv.db) modified result to middleware database(SVL/TSL/NWL/SATL). + int putRecordIdList(String serviceListId, in Bundle recordIdListBundle, int optType); + + // Add predefined ServiceListInfo of Hotbird 13E in scan two satellite scene EU region + // following by commit(). + String addPredefinedServiceListInfo(int broadcastType, String serviceListType, + String serviceListPrefix, String countryCode, int operatorId); + // Add predefined channels of Hotbird 13E in scan two satellite scene EU region. + int addPredefinedChannelList(String serviceListId, in Bundle[] predefinedListBundle); + // Add predefined satellite info of Hotbird 13E in scan two satellite scene EU region. + int addPredefinedSatInfo(String serviceListId, in Bundle predefinedSatInfoBundle); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListEditListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListEditListener.aidl new file mode 100644 index 000000000000..e227eda47d4f --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListEditListener.aidl @@ -0,0 +1,24 @@ +/* + * 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.media.tv.extension.servicedb; + +/** + * @hide + */ +oneway interface IServiceListEditListener { + void onCompleted(int requestId, int result); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListExportListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListExportListener.aidl new file mode 100644 index 000000000000..c57e8f97ed4c --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListExportListener.aidl @@ -0,0 +1,24 @@ +/* + * 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.media.tv.extension.servicedb; + +/** + * @hide + */ +oneway interface IServiceListExportListener { + void onExported(int exportResult); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListExportSession.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListExportSession.aidl new file mode 100644 index 000000000000..fcde581548f6 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListExportSession.aidl @@ -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 android.media.tv.extension.servicedb; + +import android.os.Bundle; +import android.os.ParcelFileDescriptor; + +/** + * @hide + */ +interface IServiceListExportSession { + // Start export service list with reserved parameters. + int exportServiceList(in ParcelFileDescriptor pfd, in Bundle exportParams); + // Release export resources. + int release(); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl new file mode 100644 index 000000000000..abd8320df11d --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl @@ -0,0 +1,25 @@ +/* + * 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.media.tv.extension.servicedb; + +/** + * @hide + */ +interface IServiceListImportListener { + void onImported(int importResult); + void onPreloaded(int preloadResult); +}
\ No newline at end of file diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListImportSession.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListImportSession.aidl new file mode 100644 index 000000000000..1f1ae010a444 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListImportSession.aidl @@ -0,0 +1,32 @@ +/* + * 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.media.tv.extension.servicedb; + +import android.os.Bundle; +import android.os.ParcelFileDescriptor; + +/** + * @hide + */ +interface IServiceListImportSession { + // Start import service list. Should call after preload and before release. + int importServiceList(in ParcelFileDescriptor pfd, in Bundle importParams); + // Preparing for import. + int preload(in ParcelFileDescriptor pfd); + // Release import resources. + int release(); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListListener.aidl new file mode 100644 index 000000000000..7c9c5c8a8048 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListListener.aidl @@ -0,0 +1,24 @@ +/* + * 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.media.tv.extension.servicedb; + +/** + * @hide + */ +oneway interface IServiceListSetChannelListListener { + void onCompleted(int setChannelListResult); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListSession.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListSession.aidl new file mode 100644 index 000000000000..b0527b3709b7 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListSession.aidl @@ -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 android.media.tv.extension.servicedb; + +import android.os.Bundle; + +/** + * @hide + */ +interface IServiceListSetChannelListSession { + // Set channelList with channelinfo bundles, serviceListInfo, and operation type. + int setChannelList(in Bundle[] channelsInfo, in Bundle ServiceListInfoBundle, int optType); + // Release set channellist resources. + int release(); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListTransferInterface.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListTransferInterface.aidl new file mode 100644 index 000000000000..91fb15728b06 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListTransferInterface.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +import android.media.tv.extension.servicedb.IServiceListExportListener; +import android.media.tv.extension.servicedb.IServiceListImportListener; +import android.media.tv.extension.servicedb.IServiceListSetChannelListListener; +import android.os.IBinder; + +/** + * @hide + */ +interface IServiceListTransferInterface { + IBinder createExportSession(in IServiceListExportListener listener); + IBinder createImportSession(in IServiceListImportListener listener); + IBinder createSetChannelListSession(in IServiceListSetChannelListListener listener); +} diff --git a/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfo.aidl b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfo.aidl new file mode 100644 index 000000000000..a3725e4e25db --- /dev/null +++ b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfo.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.media.tv.extension.teletext; + +import android.media.tv.extension.teletext.IDataServiceSignalInfoListener; +import android.os.Bundle; + + +/** + * @hide + */ +interface IDataServiceSignalInfo { + // Get Teletext data service signal information. + Bundle getDataServiceSignalInfo(String sessionToken); + // Add a listener that receives notifications of teletext running information. + void addDataServiceSignalInfoListener(String clientToken, + IDataServiceSignalInfoListener listener); + // Remove a listener that receives notifications of Teletext running information. + void removeDataServiceSignalInfoListener(String clientToken, + IDataServiceSignalInfoListener listener); +} diff --git a/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfoListener.aidl b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfoListener.aidl new file mode 100644 index 000000000000..0e99bf5cc955 --- /dev/null +++ b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfoListener.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.teletext; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface IDataServiceSignalInfoListener { + void onDataServiceSignalInfoChanged (String sessionToken, in Bundle changedSignalInfo); +} diff --git a/media/java/android/media/tv/extension/teletext/ITeletextPageSubCode.aidl b/media/java/android/media/tv/extension/teletext/ITeletextPageSubCode.aidl new file mode 100644 index 000000000000..c96ffc02f438 --- /dev/null +++ b/media/java/android/media/tv/extension/teletext/ITeletextPageSubCode.aidl @@ -0,0 +1,41 @@ +/* + * 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.media.tv.extension.teletext; + +import android.os.Bundle; + +/** + * @hide + */ +interface ITeletextPageSubCode { + // Get Teletext page number + Bundle getTeletextPageNumber(String sessionToken); + // Set Teletext page number. + void setTeleltextPageNumber(String sessionToken, int pageNumber); + // Get Teletext sub page number. + Bundle getTeletextPageSubCode(String sessionToken); + // Set Teletext sub page number. + void setTeletextPageSubCode(String sessionToken, int pageSubCode); + // Get Teletext TopInfo. + Bundle getTeletextHasTopInfo(String sessionToken); + // Get Teletext TopBlockList. + Bundle getTeletextTopBlockList(String sessionToken); + // Get Teletext TopGroupList. + Bundle getTeletextTopGroupList(String sessionToken, int indexGroup); + // Get Teletext TopPageList. + Bundle getTeletextTopPageList(String sessionToken, int indexPage); +} diff --git a/media/java/android/media/tv/extension/tune/IChannelTunedInterface.aidl b/media/java/android/media/tv/extension/tune/IChannelTunedInterface.aidl new file mode 100644 index 000000000000..88e50844f998 --- /dev/null +++ b/media/java/android/media/tv/extension/tune/IChannelTunedInterface.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.tune; + +import android.media.tv.extension.tune.IChannelTunedListener; + +/* +* @hide +*/ +interface IChannelTunedInterface { + void addChannelTunedListener(in IChannelTunedListener listener); + void removeChannelTunedListener(in IChannelTunedListener listener); +} diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/media/java/android/media/tv/extension/tune/IChannelTunedListener.aidl index 7d3d8b9f7974..46875463402d 100644 --- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java +++ b/media/java/android/media/tv/extension/tune/IChannelTunedListener.aidl @@ -13,10 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.platform.test.ravenwood; -/** Stub class. The actual implementation is in junit-impl-src. */ -public class RavenwoodConfigState { - public RavenwoodConfigState(RavenwoodConfig config) { - } +package android.media.tv.extension.tune; + +import android.os.Bundle; + +/* +* @hide +*/ +oneway interface IChannelTunedListener { + void onChannelTuned(String sessionToken, in Bundle channelTunedInfo); } diff --git a/media/java/android/media/tv/extension/tune/IMuxTune.aidl b/media/java/android/media/tv/extension/tune/IMuxTune.aidl new file mode 100644 index 000000000000..4e9dbdae6643 --- /dev/null +++ b/media/java/android/media/tv/extension/tune/IMuxTune.aidl @@ -0,0 +1,26 @@ +/* + * 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.media.tv.extension.tune; + +import android.media.tv.extension.tune.IMuxTuneSession; + +/** +* @hide +*/ +interface IMuxTune { + IMuxTuneSession createSession(int broadcastType, String clientToken); +} diff --git a/media/java/android/media/tv/extension/tune/IMuxTuneSession.aidl b/media/java/android/media/tv/extension/tune/IMuxTuneSession.aidl new file mode 100644 index 000000000000..5ad72b48b50b --- /dev/null +++ b/media/java/android/media/tv/extension/tune/IMuxTuneSession.aidl @@ -0,0 +1,33 @@ +/* + * 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.media.tv.extension.tune; + +import android.os.Bundle; + +/** +* @hide +*/ +interface IMuxTuneSession { + // Start mux tune with tune params. + void start(int broadcastType, int frequency, int brandwith, in Bundle muxTuneParams); + // Stop mux tune. + void stop(); + // Release muxtune resources. + void release(); + // Get the session token created by TIS to identify different sessions. + String getSessionToken(); +} diff --git a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java index c9807e626429..58aa56babb64 100644 --- a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java +++ b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java @@ -46,7 +46,7 @@ public final class FakeIMediaProjection extends IMediaProjection.Stub { } @Override - public void stop() throws RemoteException { + public void stop(@StopReason int stopReason) throws RemoteException { // Pass along to the client's callback wrapper. mIMediaProjectionCallback.onStop(); } diff --git a/native/android/Android.bp b/native/android/Android.bp index da29c49f9d7b..cd6de5a5c8f0 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -55,6 +55,7 @@ cc_library_shared { "surface_control_input_receiver.cpp", "choreographer.cpp", "configuration.cpp", + "display_luts.cpp", "dynamic_instrumentation_manager.cpp", "hardware_buffer_jni.cpp", "input.cpp", diff --git a/native/android/display_luts.cpp b/native/android/display_luts.cpp new file mode 100644 index 000000000000..179a32bd1c03 --- /dev/null +++ b/native/android/display_luts.cpp @@ -0,0 +1,135 @@ +/* + * 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. + */ +#define LOG_TAG "DisplayLuts" + +#include <android/display_luts.h> +#include <display_luts_private.h> +#include <utils/Log.h> + +#include <cmath> + +#define ADISPLAYLUTS_BUFFER_LENGTH_LIMIT (100000) + +#define CHECK_NOT_NULL(name) \ + LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument"); + +ADisplayLutsEntry* ADisplayLutsEntry_createEntry(float* buffer, int32_t length, int32_t dimension, + int32_t key) { + CHECK_NOT_NULL(buffer); + LOG_ALWAYS_FATAL_IF(length >= ADISPLAYLUTS_BUFFER_LENGTH_LIMIT, + "the lut raw buffer length is too big to handle"); + if (dimension != ADISPLAYLUTS_ONE_DIMENSION && dimension != ADISPLAYLUTS_THREE_DIMENSION) { + LOG_ALWAYS_FATAL("the lut dimension is be either 1 or 3"); + } + int32_t size = 0; + if (dimension == ADISPLAYLUTS_THREE_DIMENSION) { + LOG_ALWAYS_FATAL_IF(length % 3 != 0, "the 3d lut raw buffer is not divisible by 3"); + int32_t lengthPerChannel = length / 3; + float sizeForDim = std::cbrt(static_cast<float>(lengthPerChannel)); + LOG_ALWAYS_FATAL_IF(sizeForDim != (int)(sizeForDim), + "the 3d lut buffer length is incorrect"); + size = (int)sizeForDim; + } else { + size = length; + } + LOG_ALWAYS_FATAL_IF(size < 2, "the lut size for each dimension is too small"); + + ADisplayLutsEntry* entry = new ADisplayLutsEntry(); + entry->buffer.data.resize(length); + std::copy(buffer, buffer + length, entry->buffer.data.begin()); + entry->properties = {dimension, size, key}; + + entry->incStrong((void*)ADisplayLutsEntry_createEntry); + return static_cast<ADisplayLutsEntry*>(entry); +} + +void ADisplayLutsEntry_destroy(ADisplayLutsEntry* entry) { + if (entry != NULL) { + entry->decStrong((void*)ADisplayLutsEntry_createEntry); + } +} + +ADisplayLuts_Dimension ADisplayLutsEntry_getDimension(const ADisplayLutsEntry* entry) { + CHECK_NOT_NULL(entry); + return static_cast<ADisplayLuts_Dimension>(entry->properties.dimension); +} + +int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* entry) { + CHECK_NOT_NULL(entry); + return entry->properties.size; +} + +ADisplayLuts_SamplingKey ADisplayLutsEntry_getSamplingKey(const ADisplayLutsEntry* entry) { + CHECK_NOT_NULL(entry); + return static_cast<ADisplayLuts_SamplingKey>(entry->properties.samplingKey); +} + +const float* ADisplayLutsEntry_getBuffer(const ADisplayLutsEntry* _Nonnull entry) { + CHECK_NOT_NULL(entry); + return entry->buffer.data.data(); +} + +ADisplayLuts* ADisplayLuts_create() { + ADisplayLuts* luts = new ADisplayLuts(); + if (luts == NULL) { + delete luts; + return NULL; + } + luts->incStrong((void*)ADisplayLuts_create); + return static_cast<ADisplayLuts*>(luts); +} + +void ADisplayLuts_clearLuts(ADisplayLuts* luts) { + for (auto& entry : luts->entries) { + entry->decStrong((void*)ADisplayLuts_setEntries); // Decrement ref count + } + luts->entries.clear(); + luts->offsets.clear(); + luts->totalBufferSize = 0; +} + +void ADisplayLuts_destroy(ADisplayLuts* luts) { + if (luts != NULL) { + ADisplayLuts_clearLuts(luts); + luts->decStrong((void*)ADisplayLuts_create); + } +} + +void ADisplayLuts_setEntries(ADisplayLuts* luts, ADisplayLutsEntry** entries, int32_t numEntries) { + CHECK_NOT_NULL(luts); + // always clear the previously set lut(s) + ADisplayLuts_clearLuts(luts); + + // do nothing + if (!entries || numEntries == 0) { + return; + } + + LOG_ALWAYS_FATAL_IF(numEntries > 2, "The number of entries should be not over 2!"); + if (numEntries == 2 && entries[0]->properties.dimension != ADISPLAYLUTS_ONE_DIMENSION && + entries[1]->properties.dimension != ADISPLAYLUTS_THREE_DIMENSION) { + LOG_ALWAYS_FATAL("The entries should be 1D and 3D in order!"); + } + + luts->offsets.reserve(numEntries); + luts->entries.reserve(numEntries); + for (int32_t i = 0; i < numEntries; i++) { + luts->offsets.emplace_back(luts->totalBufferSize); + luts->totalBufferSize += entries[i]->buffer.data.size(); + luts->entries.emplace_back(entries[i]); + luts->entries.back()->incStrong((void*)ADisplayLuts_setEntries); + } +}
\ No newline at end of file diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 9da7becba332..7f555a868615 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -95,6 +95,15 @@ LIBANDROID { AConfiguration_setTouchscreen; AConfiguration_setUiModeNight; AConfiguration_setUiModeType; + ADisplayLuts_create; # introduced=36 + ADisplayLuts_setEntries; # introduced=36 + ADisplayLuts_destroy; # introduced=36 + ADisplayLutsEntry_createEntry; # introduced=36 + ADisplayLutsEntry_getDimension; # introduced=36 + ADisplayLutsEntry_getSize; # introduced=36 + ADisplayLutsEntry_getSamplingKey; # introduced=36 + ADisplayLutsEntry_getBuffer; # introduced=36 + ADisplayLutsEntry_destroy; # introduced=36 AInputEvent_getDeviceId; AInputEvent_getSource; AInputEvent_getType; @@ -300,6 +309,7 @@ LIBANDROID { ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29 ASurfaceTransaction_setExtendedRangeBrightness; # introduced=UpsideDownCake ASurfaceTransaction_setDesiredHdrHeadroom; # introduced=VanillaIceCream + ASurfaceTransaction_setLuts; # introduced=36 ASurfaceTransaction_setOnComplete; # introduced=29 ASurfaceTransaction_setOnCommit; # introduced=31 ASurfaceTransaction_setPosition; # introduced=31 diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp index c67e93c99e52..883e139cca0a 100644 --- a/native/android/performance_hint.cpp +++ b/native/android/performance_hint.cpp @@ -881,6 +881,8 @@ void APerformanceHintSession::traceModes(const std::vector<hal::SessionMode>& mo case hal::SessionMode::GRAPHICS_PIPELINE: traceGraphicsPipeline(isEnabled); break; + default: + break; } } } diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index 698bc84a78b9..fc64e9b48f6d 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -14,12 +14,15 @@ * limitations under the License. */ +#include <android/gui/LutProperties.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/native_window.h> #include <android/surface_control.h> #include <android/surface_control_jni.h> #include <android_runtime/android_view_SurfaceControl.h> #include <configstore/Utils.h> +#include <cutils/ashmem.h> +#include <display_luts_private.h> #include <gui/HdrMetadata.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> @@ -53,6 +56,14 @@ static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPA static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3)); static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ)); +static_assert(static_cast<int>(ADISPLAYLUTS_ONE_DIMENSION) == + static_cast<int>(android::gui::LutProperties::Dimension::ONE_D)); +static_assert(static_cast<int>(ADISPLAYLUTS_THREE_DIMENSION) == + static_cast<int>(android::gui::LutProperties::Dimension::THREE_D)); +static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_RGB) == + static_cast<int>(android::gui::LutProperties::SamplingKey::RGB)); +static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB) == + static_cast<int>(android::gui::LutProperties::SamplingKey::MAX_RGB)); Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) { return reinterpret_cast<Transaction*>(aSurfaceTransaction); @@ -693,6 +704,58 @@ void ASurfaceTransaction_setDesiredHdrHeadroom(ASurfaceTransaction* aSurfaceTran transaction->setDesiredHdrHeadroom(surfaceControl, desiredRatio); } +void ASurfaceTransaction_setLuts(ASurfaceTransaction* aSurfaceTransaction, + ASurfaceControl* aSurfaceControl, + const struct ADisplayLuts* luts) { + CHECK_NOT_NULL(aSurfaceTransaction); + CHECK_NOT_NULL(aSurfaceControl); + + sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + + int fd = -1; + std::vector<int32_t> offsets; + std::vector<int32_t> dimensions; + std::vector<int32_t> sizes; + std::vector<int32_t> samplingKeys; + + if (luts) { + std::vector<float> buffer(luts->totalBufferSize); + int32_t count = luts->offsets.size(); + offsets = luts->offsets; + + dimensions.reserve(count); + sizes.reserve(count); + samplingKeys.reserve(count); + for (int32_t i = 0; i < count; i++) { + dimensions.emplace_back(luts->entries[i]->properties.dimension); + sizes.emplace_back(luts->entries[i]->properties.size); + samplingKeys.emplace_back(luts->entries[i]->properties.samplingKey); + std::copy(luts->entries[i]->buffer.data.begin(), luts->entries[i]->buffer.data.end(), + buffer.begin() + offsets[i]); + } + + // mmap + fd = ashmem_create_region("lut_shared_mem", luts->totalBufferSize * sizeof(float)); + if (fd < 0) { + LOG_ALWAYS_FATAL("setLuts, ashmem_create_region() failed"); + return; + } + void* ptr = mmap(nullptr, luts->totalBufferSize * sizeof(float), PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + LOG_ALWAYS_FATAL("setLuts, Failed to map the shared memory"); + return; + } + + memcpy(ptr, buffer.data(), luts->totalBufferSize * sizeof(float)); + munmap(ptr, luts->totalBufferSize * sizeof(float)); + } + + transaction->setLuts(surfaceControl, base::unique_fd(fd), offsets, dimensions, sizes, + samplingKeys); +} + void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl, float r, float g, float b, float alpha, diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp index c5fb80840b84..b00658052bde 100644 --- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp +++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp @@ -73,13 +73,13 @@ public: MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override)); MOCK_METHOD(ScopedAStatus, getCpuHeadroom, (const ::aidl::android::os::CpuHeadroomParamsInternal& in_params, - std::vector<float>* _aidl_return), + std::optional<hal::CpuHeadroomResult>* _aidl_return), (override)); MOCK_METHOD(ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* _aidl_return), (override)); MOCK_METHOD(ScopedAStatus, getGpuHeadroom, (const ::aidl::android::os::GpuHeadroomParamsInternal& in_params, - float* _aidl_return), + std::optional<hal::GpuHeadroomResult>* _aidl_return), (override)); MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return), (override)); diff --git a/nfc/api/current.txt b/nfc/api/current.txt index 7ae2eafaf461..0ee81cbb7a73 100644 --- a/nfc/api/current.txt +++ b/nfc/api/current.txt @@ -202,6 +202,7 @@ package android.nfc.cardemulation { method public boolean categoryAllowsForegroundPreference(String); method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public java.util.List<java.lang.String> getAidsForPreferredPaymentService(); method public java.util.List<java.lang.String> getAidsForService(android.content.ComponentName, String); + method @FlaggedApi("android.nfc.enable_card_emulation_euicc") public int getDefaultNfcSubscriptionId(); method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public CharSequence getDescriptionForPreferredPaymentService(); method public static android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter); method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getRouteDestinationForPreferredPaymentService(); @@ -232,6 +233,7 @@ package android.nfc.cardemulation { field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_NFC_CRASH_RESTART = 1; // 0x1 field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_NFC_HARDWARE_ERROR = 2; // 0x2 field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_UNKNOWN = 0; // 0x0 + field @FlaggedApi("android.nfc.nfc_associated_role_services") public static final String PROPERTY_ALLOW_SHARED_ROLE_PRIORITY = "android.nfc.cardemulation.PROPERTY_ALLOW_SHARED_ROLE_PRIORITY"; field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DEFAULT = 3; // 0x3 field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DH = 0; // 0x0 field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE = 1; // 0x1 diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 15814edcd86a..3ed9b7667be7 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -57,17 +57,19 @@ package android.nfc { @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension { method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int forceRoutingTableCommit(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.List<java.lang.String> getActiveNfceeList(); + method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getActiveNfceeList(); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public long getMaxPausePollingTimeoutMills(); method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.nfc.RoutingStatus getRoutingStatus(); method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.List<android.nfc.NfcRoutingTableEntry> getRoutingTable(); + method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public android.nfc.T4tNdefNfcee getT4tNdefNfcee(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean hasUserEnabledNfc(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isAutoChangeEnabled(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagPresent(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void overwriteRoutingTable(int, int, int, int); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void pausePolling(int); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int pausePolling(long); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void resumePolling(); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int resumePolling(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAutoChangeEnabled(boolean); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void setControllerAlwaysOnMode(int); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState(); @@ -83,6 +85,8 @@ package android.nfc { field public static final int HCE_ACTIVATE = 1; // 0x1 field public static final int HCE_DATA_TRANSFERRED = 2; // 0x2 field public static final int HCE_DEACTIVATE = 3; // 0x3 + field public static final int POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE = 2; // 0x2 + field public static final int POLLING_STATE_CHANGE_SUCCEEDED = 1; // 0x1 field public static final int STATUS_OK = 0; // 0x0 field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1 } @@ -120,6 +124,11 @@ package android.nfc { @FlaggedApi("android.nfc.nfc_oem_extension") public abstract class NfcRoutingTableEntry { method public int getNfceeId(); + method public int getType(); + field public static final int TYPE_AID = 0; // 0x0 + field public static final int TYPE_PROTOCOL = 1; // 0x1 + field public static final int TYPE_SYSTEM_CODE = 3; // 0x3 + field public static final int TYPE_TECHNOLOGY = 2; // 0x2 } @FlaggedApi("android.nfc.nfc_oem_extension") public final class OemLogItems implements android.os.Parcelable { @@ -179,6 +188,47 @@ package android.nfc { field @FlaggedApi("android.nfc.nfc_oem_extension") public static final int TECHNOLOGY_V = 3; // 0x3 } + @FlaggedApi("android.nfc.nfc_oem_extension") public final class T4tNdefNfcee { + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public int clearData(); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isOperationOngoing(); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isSupported(); + method @Nullable @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public android.nfc.T4tNdefNfceeCcFileInfo readCcfile(); + method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public byte[] readData(@IntRange(from=0, to=65535) int); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @WorkerThread public int writeData(@IntRange(from=0, to=65535) int, @NonNull byte[]); + field public static final int CLEAR_DATA_FAILED_INTERNAL = 0; // 0x0 + field public static final int CLEAR_DATA_SUCCESS = 1; // 0x1 + field public static final int WRITE_DATA_ERROR_CONNECTION_FAILED = -6; // 0xfffffffa + field public static final int WRITE_DATA_ERROR_EMPTY_PAYLOAD = -7; // 0xfffffff9 + field public static final int WRITE_DATA_ERROR_INTERNAL = -1; // 0xffffffff + field public static final int WRITE_DATA_ERROR_INVALID_FILE_ID = -4; // 0xfffffffc + field public static final int WRITE_DATA_ERROR_INVALID_LENGTH = -5; // 0xfffffffb + field public static final int WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED = -8; // 0xfffffff8 + field public static final int WRITE_DATA_ERROR_NFC_NOT_ON = -3; // 0xfffffffd + field public static final int WRITE_DATA_ERROR_RF_ACTIVATED = -2; // 0xfffffffe + field public static final int WRITE_DATA_SUCCESS = 0; // 0x0 + } + + @FlaggedApi("android.nfc.nfc_oem_extension") public final class T4tNdefNfceeCcFileInfo implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=15, to=32767) public int getCcFileLength(); + method @IntRange(from=0xffffffff, to=65535) public int getFileId(); + method @IntRange(from=15, to=65535) public int getMaxReadLength(); + method @IntRange(from=5, to=32767) public int getMaxSize(); + method @IntRange(from=13, to=65535) public int getMaxWriteLength(); + method public int getReadAccess(); + method public int getVersion(); + method public int getWriteAccess(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.nfc.T4tNdefNfceeCcFileInfo> CREATOR; + field public static final int READ_ACCESS_GRANTED_RESTRICTED = 128; // 0x80 + field public static final int READ_ACCESS_GRANTED_UNRESTRICTED = 0; // 0x0 + field public static final int VERSION_2_0 = 32; // 0x20 + field public static final int VERSION_3_0 = 48; // 0x30 + field public static final int WRITE_ACCESS_GRANTED_RESTRICTED = 128; // 0x80 + field public static final int WRITE_ACCESS_GRANTED_UNRESTRICTED = 0; // 0x0 + field public static final int WRITE_ACCESS_NOT_GRANTED = 255; // 0xff + } + } package android.nfc.cardemulation { @@ -186,14 +236,19 @@ package android.nfc.cardemulation { public final class CardEmulation { method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static android.content.ComponentName getPreferredPaymentService(@NonNull android.content.Context); method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int); - method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void overrideRoutingTable(@NonNull android.app.Activity, int, int); - method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void recoverRoutingTable(@NonNull android.app.Activity); + method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void overrideRoutingTable(@NonNull android.app.Activity, int, int); + method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void recoverRoutingTable(@NonNull android.app.Activity); + method @FlaggedApi("android.nfc.enable_card_emulation_euicc") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setDefaultNfcSubscriptionId(int); method @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setServiceEnabledForCategoryOther(@NonNull android.content.ComponentName, boolean); field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET = 3; // 0x3 field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_FEATURE_UNSUPPORTED = 1; // 0x1 field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE = 2; // 0x2 field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR = 4; // 0x4 field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_OK = 0; // 0x0 + field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR = 2; // 0x2 + field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID = 1; // 0x1 + field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED = 3; // 0x3 + field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_SUCCESS = 0; // 0x0 } } diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl index a08b55fe86b8..ac0a5aaaa195 100644 --- a/nfc/java/android/nfc/INfcAdapter.aidl +++ b/nfc/java/android/nfc/INfcAdapter.aidl @@ -31,6 +31,7 @@ import android.nfc.INfcCardEmulation; import android.nfc.INfcFCardEmulation; import android.nfc.INfcOemExtensionCallback; import android.nfc.INfcUnlockHandler; +import android.nfc.IT4tNdefNfcee; import android.nfc.ITagRemovedCallback; import android.nfc.INfcDta; import android.nfc.INfcWlcStateListener; @@ -52,8 +53,8 @@ interface INfcAdapter int getState(); boolean disable(boolean saveState, in String pkg); boolean enable(in String pkg); - void pausePolling(int timeoutInMs); - void resumePolling(); + int pausePolling(long timeoutInMs); + int resumePolling(); void setForegroundDispatch(in PendingIntent intent, in IntentFilter[] filters, in TechListParcel techLists); @@ -114,7 +115,7 @@ interface INfcAdapter void clearPreference(); void setScreenState(); void checkFirmware(); - List<String> fetchActiveNfceeList(); + Map fetchActiveNfceeList(); void triggerInitialization(); boolean getSettingStatus(); boolean isTagPresent(); @@ -122,4 +123,6 @@ interface INfcAdapter void indicateDataMigration(boolean inProgress, String pkg); int commitRouting(); boolean isTagIntentAllowed(in String pkg, in int Userid); + IT4tNdefNfcee getT4tNdefNfceeInterface(); + long getMaxPausePollingTimeoutMs(); } diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl index 633d8bfbbb67..bb9fe959dc06 100644 --- a/nfc/java/android/nfc/INfcCardEmulation.aidl +++ b/nfc/java/android/nfc/INfcCardEmulation.aidl @@ -53,6 +53,8 @@ interface INfcCardEmulation void overrideRoutingTable(int userHandle, String protocol, String technology, in String pkg); void recoverRoutingTable(int userHandle); boolean isEuiccSupported(); + int getDefaultNfcSubscriptionId(in String pkg); + int setDefaultNfcSubscriptionId(int subscriptionId, in String pkg); void setAutoChangeStatus(boolean state); boolean isAutoChangeEnabled(); List<String> getRoutingStatus(); diff --git a/nfc/java/android/nfc/IT4tNdefNfcee.aidl b/nfc/java/android/nfc/IT4tNdefNfcee.aidl new file mode 100644 index 000000000000..b4cda5b022fb --- /dev/null +++ b/nfc/java/android/nfc/IT4tNdefNfcee.aidl @@ -0,0 +1,33 @@ +/****************************************************************************** + * + * 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.nfc; + +import android.nfc.T4tNdefNfceeCcFileInfo; + +/** + * @hide + */ +interface IT4tNdefNfcee { + int writeData(in int fileId, in byte[] data); + byte[] readData(in int fileId); + int clearNdefData(); + boolean isNdefOperationOngoing(); + boolean isNdefNfceeEmulationSupported(); + T4tNdefNfceeCcFileInfo readCcfile(); +} diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java index 056844f38f3c..89ce4239cd4d 100644 --- a/nfc/java/android/nfc/NfcAdapter.java +++ b/nfc/java/android/nfc/NfcAdapter.java @@ -589,6 +589,7 @@ public final class NfcAdapter { static INfcTag sTagService; static INfcCardEmulation sCardEmulationService; static INfcFCardEmulation sNfcFCardEmulationService; + static IT4tNdefNfcee sNdefNfceeService; /** * The NfcAdapter object for each application context. @@ -827,7 +828,13 @@ public final class NfcAdapter { throw new UnsupportedOperationException(); } } - + try { + sNdefNfceeService = sService.getT4tNdefNfceeInterface(); + } catch (RemoteException e) { + sNdefNfceeService = null; + Log.e(TAG, "could not retrieve NDEF NFCEE service"); + throw new UnsupportedOperationException(); + } sIsInitialized = true; } NfcAdapter adapter = sNfcAdapters.get(context); diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index 9ed678fe6014..f78161e7cad0 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -39,6 +39,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; import android.os.ResultReceiver; +import android.se.omapi.Reader; import android.util.Log; import java.lang.annotation.Retention; @@ -148,6 +149,48 @@ public final class NfcOemExtension { public @interface ControllerMode{} /** + * Technology Type for {@link #getActiveNfceeList()}. + * @hide + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + public static final int NFCEE_TECH_NONE = 0; + + /** + * Technology Type for {@link #getActiveNfceeList()}. + * @hide + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + public static final int NFCEE_TECH_A = 1; + + /** + * Technology Type for {@link #getActiveNfceeList()}. + * @hide + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + public static final int NFCEE_TECH_B = 1 << 1; + + /** + * Technology Type for {@link #getActiveNfceeList()}. + * @hide + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + public static final int NFCEE_TECH_F = 1 << 2; + + /** + * Nfc technology flags for {@link #getActiveNfceeList()}. + * + * @hide + */ + @IntDef(flag = true, value = { + NFCEE_TECH_NONE, + NFCEE_TECH_A, + NFCEE_TECH_B, + NFCEE_TECH_F, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NfceeTechnology {} + + /** * Event that Host Card Emulation is activated. */ public static final int HCE_ACTIVATE = 1; @@ -173,6 +216,31 @@ public final class NfcOemExtension { public @interface HostCardEmulationAction {} /** + * Status code returned when the polling state change request succeeded. + * @see #pausePolling() + * @see #resumePolling() + */ + public static final int POLLING_STATE_CHANGE_SUCCEEDED = 1; + /** + * Status code returned when the polling state change request is already in + * required state. + * @see #pausePolling() + * @see #resumePolling() + */ + public static final int POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE = 2; + /** + * Possible status codes for {@link #pausePolling()} and + * {@link #resumePolling()}. + * @hide + */ + @IntDef(value = { + POLLING_STATE_CHANGE_SUCCEEDED, + POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PollingStateChangeStatusCode {} + + /** * Status OK */ public static final int STATUS_OK = 0; @@ -467,6 +535,28 @@ public final class NfcOemExtension { } /** + * Get an instance of {@link T4tNdefNfcee} object for performing T4T (Type-4 Tag) + * NDEF (NFC Data Exchange Format) NFCEE (NFC Execution Environment) operations. + * This can be used to write NDEF data to emulate a T4T tag in an NFCEE + * (NFC Execution Environment - eSE, SIM, etc). Refer to the NFC forum specification + * "NFCForum-TS-NCI-2.3 section 10.4" and "NFCForum-TS-T4T-1.1 section 4.2" for more details. + * + * This is a singleton object which shall be used by OEM extension module to do NDEF-NFCEE + * read/write operations. + * + * <p>Returns {@link T4tNdefNfcee} + * <p>Does not cause any RF activity and does not block. + * @return NFC Data Exchange Format (NDEF) NFC Execution Environment (NFCEE) object + * @hide + */ + @SystemApi + @NonNull + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + public T4tNdefNfcee getT4tNdefNfcee() { + return T4tNdefNfcee.getInstance(); + } + + /** * Register an {@link Callback} to listen for NFC oem extension callbacks * Multiple clients can register and callbacks will be invoked asynchronously. * @@ -580,14 +670,18 @@ public final class NfcOemExtension { /** * Get the Active NFCEE (NFC Execution Environment) List * - * @return List of activated secure elements on success - * which can contain "eSE" and "UICC", otherwise empty list. + * @see Reader#getName() for the list of possible NFCEE names. + * + * @return Map< String, @NfceeTechnology Integer > + * A HashMap where keys are activated secure elements and + * the values are bitmap of technologies supported by each secure element + * on success keys can contain "eSE" and "UICC", otherwise empty map. */ @NonNull @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public List<String> getActiveNfceeList() { + public Map<String, Integer> getActiveNfceeList() { return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sService.fetchActiveNfceeList(), new ArrayList<String>()); + NfcAdapter.sService.fetchActiveNfceeList(), new HashMap<String, Integer>()); } /** @@ -653,24 +747,45 @@ public final class NfcOemExtension { /** * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. - * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely - * use {@link #resumePolling()} to resume the polling. - * @param timeoutInMs the pause polling duration in millisecond, ranging from 0 to 40000. + * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely. + * Use {@link #resumePolling()} to resume the polling. + * Use {@link #getMaxPausePollingTimeoutMs()} to check the max timeout value. + * @param timeoutInMs the pause polling duration in millisecond. + * @return status of the operation + * @throws IllegalArgumentException if timeoutInMs value is invalid + * (0 < timeoutInMs < max). */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void pausePolling(@DurationMillisLong int timeoutInMs) { - NfcAdapter.callService(() -> NfcAdapter.sService.pausePolling(timeoutInMs)); + public @PollingStateChangeStatusCode int pausePolling(@DurationMillisLong long timeoutInMs) { + return NfcAdapter.callServiceReturn(() -> + NfcAdapter.sService.pausePolling(timeoutInMs), + POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE); } /** * Resumes default NFC tag reader mode polling for the current device state if polling is * paused. Calling this while already in polling is a no-op. + * @return status of the operation + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public @PollingStateChangeStatusCode int resumePolling() { + return NfcAdapter.callServiceReturn(() -> + NfcAdapter.sService.resumePolling(), + POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE); + } + + /** + * Gets the max pause polling timeout value in millisecond. + * @return long integer representing the max timeout */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void resumePolling() { - NfcAdapter.callService(() -> NfcAdapter.sService.resumePolling()); + @DurationMillisLong + public long getMaxPausePollingTimeoutMills() { + return NfcAdapter.callServiceReturn(() -> + NfcAdapter.sService.getMaxPausePollingTimeoutMs(), 0L); } /** diff --git a/nfc/java/android/nfc/NfcRoutingTableEntry.java b/nfc/java/android/nfc/NfcRoutingTableEntry.java index 4e913776a030..c2cbbede9b75 100644 --- a/nfc/java/android/nfc/NfcRoutingTableEntry.java +++ b/nfc/java/android/nfc/NfcRoutingTableEntry.java @@ -17,8 +17,12 @@ package android.nfc; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.SystemApi; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Class to represent an entry of routing table. This class is abstract and extended by * {@link RoutingTableTechnologyEntry}, {@link RoutingTableProtocolEntry}, @@ -30,10 +34,42 @@ import android.annotation.SystemApi; @SystemApi public abstract class NfcRoutingTableEntry { private final int mNfceeId; + private final int mType; + + /** + * AID routing table type. + */ + public static final int TYPE_AID = 0; + /** + * Protocol routing table type. + */ + public static final int TYPE_PROTOCOL = 1; + /** + * Technology routing table type. + */ + public static final int TYPE_TECHNOLOGY = 2; + /** + * System Code routing table type. + */ + public static final int TYPE_SYSTEM_CODE = 3; + + /** + * Possible type of this routing table entry. + * @hide + */ + @IntDef(prefix = "TYPE_", value = { + TYPE_AID, + TYPE_PROTOCOL, + TYPE_TECHNOLOGY, + TYPE_SYSTEM_CODE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RoutingTableType {} /** @hide */ - protected NfcRoutingTableEntry(int nfceeId) { + protected NfcRoutingTableEntry(int nfceeId, @RoutingTableType int type) { mNfceeId = nfceeId; + mType = type; } /** @@ -43,4 +79,13 @@ public abstract class NfcRoutingTableEntry { public int getNfceeId() { return mNfceeId; } + + /** + * Get the type of this entry. + * @return an integer defined in {@link RoutingTableType} + */ + @RoutingTableType + public int getType() { + return mType; + } } diff --git a/nfc/java/android/nfc/RoutingTableAidEntry.java b/nfc/java/android/nfc/RoutingTableAidEntry.java index 7634fe342ab9..bf697d662bd6 100644 --- a/nfc/java/android/nfc/RoutingTableAidEntry.java +++ b/nfc/java/android/nfc/RoutingTableAidEntry.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.SystemApi; /** - * Represents an AID entry in current routing table. + * Represents an Application ID (AID) entry in current routing table. * @hide */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @@ -30,7 +30,7 @@ public class RoutingTableAidEntry extends NfcRoutingTableEntry { /** @hide */ public RoutingTableAidEntry(int nfceeId, String value) { - super(nfceeId); + super(nfceeId, TYPE_AID); this.mValue = value; } diff --git a/nfc/java/android/nfc/RoutingTableProtocolEntry.java b/nfc/java/android/nfc/RoutingTableProtocolEntry.java index 0c5be7dd9d97..536de4d7430e 100644 --- a/nfc/java/android/nfc/RoutingTableProtocolEntry.java +++ b/nfc/java/android/nfc/RoutingTableProtocolEntry.java @@ -97,7 +97,7 @@ public class RoutingTableProtocolEntry extends NfcRoutingTableEntry { /** @hide */ public RoutingTableProtocolEntry(int nfceeId, @ProtocolValue int value) { - super(nfceeId); + super(nfceeId, TYPE_PROTOCOL); this.mValue = value; } diff --git a/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java b/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java index f87ad5f95195..f61892d31668 100644 --- a/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java +++ b/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java @@ -20,7 +20,9 @@ import android.annotation.NonNull; import android.annotation.SystemApi; /** - * Represents a system code entry in current routing table. + * Represents a system code entry in current routing table, where system codes are two-byte values + * used in NFC-F technology (a type of NFC communication) to identify specific + * device configurations. * @hide */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @@ -30,7 +32,7 @@ public class RoutingTableSystemCodeEntry extends NfcRoutingTableEntry { /** @hide */ public RoutingTableSystemCodeEntry(int nfceeId, byte[] value) { - super(nfceeId); + super(nfceeId, TYPE_SYSTEM_CODE); this.mValue = value; } diff --git a/nfc/java/android/nfc/RoutingTableTechnologyEntry.java b/nfc/java/android/nfc/RoutingTableTechnologyEntry.java index f51a5299be11..2dbc94232b0b 100644 --- a/nfc/java/android/nfc/RoutingTableTechnologyEntry.java +++ b/nfc/java/android/nfc/RoutingTableTechnologyEntry.java @@ -30,22 +30,27 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class RoutingTableTechnologyEntry extends NfcRoutingTableEntry { /** - * Technology-A + * Technology-A. + * <p>Tech-A is mostly used for payment and ticketing applications. It supports various + * Tag platforms including Type 1, Type 2 and Type 4A tags. </p> */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) public static final int TECHNOLOGY_A = 0; /** - * Technology-B + * Technology-B which is based on ISO/IEC 14443-3 standard. */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) public static final int TECHNOLOGY_B = 1; /** - * Technology-F + * Technology-F. + * <p>Tech-F is a standard which supports Type 3 Tags and NFC-DEP protocol etc.</p> */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) public static final int TECHNOLOGY_F = 2; /** - * Technology-V + * Technology-V. + * <p>Tech-V is an NFC technology used for communication with passive tags that operate + * at a longer range than other NFC technologies. </p> */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) public static final int TECHNOLOGY_V = 3; @@ -73,7 +78,7 @@ public class RoutingTableTechnologyEntry extends NfcRoutingTableEntry { /** @hide */ public RoutingTableTechnologyEntry(int nfceeId, @TechnologyValue int value) { - super(nfceeId); + super(nfceeId, TYPE_TECHNOLOGY); this.mValue = value; } diff --git a/nfc/java/android/nfc/T4tNdefNfcee.java b/nfc/java/android/nfc/T4tNdefNfcee.java new file mode 100644 index 000000000000..06d02c54eb2e --- /dev/null +++ b/nfc/java/android/nfc/T4tNdefNfcee.java @@ -0,0 +1,258 @@ +/* + * 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.nfc; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.WorkerThread; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This class is used for performing T4T (Type-4 Tag) NDEF (NFC Data Exchange Format) + * NFCEE (NFC Execution Environment) operations. + * This can be used to write NDEF data to emulate a T4T tag in an NFCEE + * (NFC Execution Environment - eSE, SIM, etc). Refer to the NFC forum specification + * "NFCForum-TS-NCI-2.3 section 10.4" and "NFCForum-TS-T4T-1.1 section 4.2" for more details. + * @hide + */ +@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) +@SystemApi +public final class T4tNdefNfcee { + private static final String TAG = "NdefNfcee"; + static T4tNdefNfcee sNdefNfcee; + + private T4tNdefNfcee() { + } + + /** + * Helper to get an instance of this class. + * + * @return + * @hide + */ + @NonNull + public static T4tNdefNfcee getInstance() { + if (sNdefNfcee == null) { + sNdefNfcee = new T4tNdefNfcee(); + } + return sNdefNfcee; + } + + /** + * Return flag for {@link #writeData(int, byte[])}. + * It indicates write data is successful. + */ + public static final int WRITE_DATA_SUCCESS = 0; + /** + * Return flag for {@link #writeData(int, byte[])}. + * It indicates write data fail due to unknown reasons. + */ + public static final int WRITE_DATA_ERROR_INTERNAL = -1; + /** + * Return flag for {@link #writeData(int, byte[])}. + * It indicates write data fail due to ongoing rf activity. + */ + public static final int WRITE_DATA_ERROR_RF_ACTIVATED = -2; + /** + * Return flag for {@link #writeData(int, byte[])}. + * It indicates write data fail due to Nfc off. + */ + public static final int WRITE_DATA_ERROR_NFC_NOT_ON = -3; + /** + * Return flag for {@link #writeData(int, byte[])}. + * It indicates write data fail due to invalid file id. + */ + public static final int WRITE_DATA_ERROR_INVALID_FILE_ID = -4; + /** + * Return flag for {@link #writeData(int, byte[])}. + * It indicates write data fail due to invalid length. + */ + public static final int WRITE_DATA_ERROR_INVALID_LENGTH = -5; + /** + * Return flag for {@link #writeData(int, byte[])}. + * It indicates write data fail due to core connection create failure. + */ + public static final int WRITE_DATA_ERROR_CONNECTION_FAILED = -6; + /** + * Return flag for {@link #writeData(int, byte[])}. + * It indicates write data fail due to empty payload. + */ + public static final int WRITE_DATA_ERROR_EMPTY_PAYLOAD = -7; + /** + * Returns flag for {@link #writeData(int, byte[])}. + * It idicates write data fail due to invalid ndef format. + */ + public static final int WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED = -8; + + /** + * Possible return values for {@link #writeData(int, byte[])}. + * + * @hide + */ + @IntDef(prefix = { "WRITE_DATA_" }, value = { + WRITE_DATA_SUCCESS, + WRITE_DATA_ERROR_INTERNAL, + WRITE_DATA_ERROR_RF_ACTIVATED, + WRITE_DATA_ERROR_NFC_NOT_ON, + WRITE_DATA_ERROR_INVALID_FILE_ID, + WRITE_DATA_ERROR_INVALID_LENGTH, + WRITE_DATA_ERROR_CONNECTION_FAILED, + WRITE_DATA_ERROR_EMPTY_PAYLOAD, + WRITE_DATA_ERROR_NDEF_VALIDATION_FAILED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WriteDataStatus{} + + /** + * This API performs writes of T4T data to NFCEE. + * + * <p>This is an I/O operation and will block until complete. It must + * not be called from the main application thread.</p> + * + * @param fileId File id (Refer NFC Forum Type 4 Tag Specification + * Section 4.2 File Identifiers and Access Conditions + * for more information) to which to write. + * @param data This should be valid Ndef Message format. + * Refer to Nfc forum NDEF specification NDEF Message section + * @return status of the operation. + * @hide + */ + @SystemApi + @WorkerThread + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public @WriteDataStatus int writeData(@IntRange(from = 0, to = 65535) int fileId, + @NonNull byte[] data) { + return NfcAdapter.callServiceReturn(() -> + NfcAdapter.sNdefNfceeService.writeData(fileId, data), WRITE_DATA_ERROR_INTERNAL); + } + + /** + * This API performs reading of T4T content of Nfcee. + * + * <p>This is an I/O operation and will block until complete. It must + * not be called from the main application thread.</p> + * + * @param fileId File Id (Refer + * Section 4.2 File Identifiers and Access Conditions + * for more information) from which to read. + * @return - Returns Ndef message if success + * Refer to Nfc forum NDEF specification NDEF Message section + * @throws IllegalStateException if read fails because the fileId is invalid. + * @hide + */ + @SystemApi + @WorkerThread + @NonNull + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public byte[] readData(@IntRange(from = 0, to = 65535) int fileId) { + return NfcAdapter.callServiceReturn(() -> + NfcAdapter.sNdefNfceeService.readData(fileId), null); + } + + /** + * Return flag for {@link #clearNdefData()}. + * It indicates clear data is successful. + */ + public static final int CLEAR_DATA_SUCCESS = 1; + /** + * Return flag for {@link #clearNdefData()}. + * It indicates clear data failed due to internal error while processing the clear. + */ + public static final int CLEAR_DATA_FAILED_INTERNAL = 0; + + /** + * Possible return values for {@link #clearNdefData()}. + * + * @hide + */ + @IntDef(prefix = { "CLEAR_DATA_" }, value = { + CLEAR_DATA_SUCCESS, + CLEAR_DATA_FAILED_INTERNAL, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ClearDataStatus{} + + /** + * This API will set all the T4T NDEF NFCEE data to zero. + * + * <p>This is an I/O operation and will block until complete. It must + * not be called from the main application thread. + * + * <p>This API can be called regardless of NDEF file lock state. + * </p> + * @return status of the operation + * + * @hide + */ + @SystemApi + @WorkerThread + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public @ClearDataStatus int clearData() { + return NfcAdapter.callServiceReturn(() -> + NfcAdapter.sNdefNfceeService.clearNdefData(), CLEAR_DATA_FAILED_INTERNAL); + } + + /** + * Returns whether NDEF NFCEE operation is ongoing or not. + * + * @return true if NDEF NFCEE operation is ongoing, else false. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public boolean isOperationOngoing() { + return NfcAdapter.callServiceReturn(() -> + NfcAdapter.sNdefNfceeService.isNdefOperationOngoing(), false); + } + + /** + * This Api is to check the status of NDEF NFCEE emulation feature is + * supported or not. + * + * @return true if NDEF NFCEE emulation feature is supported, else false. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public boolean isSupported() { + return NfcAdapter.callServiceReturn(() -> + NfcAdapter.sNdefNfceeService.isNdefNfceeEmulationSupported(), false); + } + + /** + * This API performs reading of T4T NDEF NFCEE CC file content. + * + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details. + * + * @return Returns CC file content if success or null if failed to read. + * @hide + */ + @SystemApi + @WorkerThread + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + @Nullable + public T4tNdefNfceeCcFileInfo readCcfile() { + return NfcAdapter.callServiceReturn(() -> + NfcAdapter.sNdefNfceeService.readCcfile(), null); + } +} diff --git a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.aidl b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.aidl new file mode 100644 index 000000000000..f72f74e8b3b9 --- /dev/null +++ b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.aidl @@ -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. + */ + +package android.nfc; + +parcelable T4tNdefNfceeCcFileInfo; + diff --git a/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java new file mode 100644 index 000000000000..5fca0529124e --- /dev/null +++ b/nfc/java/android/nfc/T4tNdefNfceeCcFileInfo.java @@ -0,0 +1,293 @@ +/* + * 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.nfc; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This class is used to represence T4T (Type-4 Tag) NDEF (NFC Data Exchange Format) + * NFCEE (NFC Execution Environment) CC (Capability Container) File data. + * The CC file stores metadata about the T4T tag being emulated. + * + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details. + * @hide + */ +@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) +@SystemApi +public final class T4tNdefNfceeCcFileInfo implements Parcelable { + /** + * Indicates the size of this capability container (called “CC File”)<p> + */ + private int mCcLength; + /** + * Indicates the mapping specification version<p> + */ + private int mVersion; + /** + * Indicates the max data size by a single ReadBinary<p> + */ + private int mMaxReadLength; + /** + * Indicates the max data size by a single UpdateBinary<p> + */ + private int mMaxWriteLength; + /** + * Indicates the NDEF File Identifier<p> + */ + private int mFileId; + /** + * Indicates the maximum Max NDEF file size<p> + */ + private int mMaxSize; + /** + * Indicates the read access condition<p> + */ + private int mReadAccess; + /** + * Indicates the write access condition<p> + */ + private int mWriteAccess; + + /** + * Constructor to be used by NFC service and internal classes. + * @hide + */ + public T4tNdefNfceeCcFileInfo(int cclen, int version, int maxLe, int maxLc, + int ndefFileId, int ndefMaxSize, + int ndefReadAccess, int ndefWriteAccess) { + mCcLength = cclen; + mVersion = version; + mMaxWriteLength = maxLc; + mMaxReadLength = maxLe; + mFileId = ndefFileId; + mMaxSize = ndefMaxSize; + mReadAccess = ndefReadAccess; + mWriteAccess = ndefWriteAccess; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + + dest.writeInt(mCcLength); + dest.writeInt(mVersion); + dest.writeInt(mMaxWriteLength); + dest.writeInt(mMaxReadLength); + dest.writeInt(mFileId); + dest.writeInt(mMaxSize); + dest.writeInt(mReadAccess); + dest.writeInt(mWriteAccess); + } + + /** + * Indicates the size of this capability container (called “CC File”). + * + * @return length of the CC file. + */ + @IntRange(from = 0xf, to = 0x7fff) + public int getCcFileLength() { + return mCcLength; + } + + /** + * T4T tag mapping version 2.0. + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details. + */ + public static final int VERSION_2_0 = 0x20; + /** + * T4T tag mapping version 2.0. + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" for more details. + */ + public static final int VERSION_3_0 = 0x30; + + /** + * Possible return values for {@link #getVersion()}. + * @hide + */ + @IntDef(prefix = { "VERSION_" }, value = { + VERSION_2_0, + VERSION_3_0, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Version{} + + /** + * Indicates the mapping version of the T4T tag supported. + * + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.5" for more details. + * + * @return version of the specification + */ + @Version + public int getVersion() { + return mVersion; + } + + /** + * Indicates the max data size that can be read by a single invocation of + * {@link T4tNdefNfcee#readData(int)}. + * + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" MLe. + * @return max size of read (in bytes). + */ + @IntRange(from = 0xf, to = 0xffff) + public int getMaxReadLength() { + return mMaxReadLength; + } + + /** + * Indicates the max data size that can be written by a single invocation of + * {@link T4tNdefNfcee#writeData(int, byte[])} + * + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.4" MLc. + * @return max size of write (in bytes). + */ + @IntRange(from = 0xd, to = 0xffff) + public int getMaxWriteLength() { + return mMaxWriteLength; + } + + /** + * Indicates the NDEF File Identifier. This is the identifier used in the last invocation of + * {@link T4tNdefNfcee#writeData(int, byte[])} + * + * @return FileId of the data stored or -1 if no data is present. + */ + @IntRange(from = -1, to = 65535) + public int getFileId() { + return mFileId; + } + + /** + * Indicates the maximum size of T4T NDEF data that can be written to the NFCEE. + * + * @return max size of the contents. + */ + @IntRange(from = 0x5, to = 0x7fff) + public int getMaxSize() { + return mMaxSize; + } + + /** + * T4T tag read access granted without any security. + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details. + */ + public static final int READ_ACCESS_GRANTED_UNRESTRICTED = 0x0; + /** + * T4T tag read access granted with limited proprietary access only. + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details. + */ + public static final int READ_ACCESS_GRANTED_RESTRICTED = 0x80; + + /** + * Possible return values for {@link #getVersion()}. + * @hide + */ + @IntDef(prefix = { "READ_ACCESS_GRANTED_" }, value = { + READ_ACCESS_GRANTED_RESTRICTED, + READ_ACCESS_GRANTED_UNRESTRICTED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ReadAccess {} + + /** + * Indicates the read access condition. + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details. + * @return read access restriction + */ + @ReadAccess + public int getReadAccess() { + return mReadAccess; + } + + /** + * T4T tag write access granted without any security. + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details. + */ + public static final int WRITE_ACCESS_GRANTED_UNRESTRICTED = 0x0; + /** + * T4T tag write access granted with limited proprietary access only. + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details. + */ + public static final int WRITE_ACCESS_GRANTED_RESTRICTED = 0x80; + /** + * T4T tag write access not granted. + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details. + */ + public static final int WRITE_ACCESS_NOT_GRANTED = 0xFF; + + /** + * Possible return values for {@link #getVersion()}. + * @hide + */ + @IntDef(prefix = { "READ_ACCESS_GRANTED_" }, value = { + WRITE_ACCESS_GRANTED_RESTRICTED, + WRITE_ACCESS_GRANTED_UNRESTRICTED, + WRITE_ACCESS_NOT_GRANTED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WriteAccess {} + + /** + * Indicates the write access condition. + * Refer to the NFC forum specification "NFCForum-TS-T4T-1.1 section 4.2" for more details. + * @return write access restriction + */ + @WriteAccess + public int getWriteAccess() { + return mWriteAccess; + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Parcelable.Creator<T4tNdefNfceeCcFileInfo> CREATOR = + new Parcelable.Creator<>() { + @Override + public T4tNdefNfceeCcFileInfo createFromParcel(Parcel in) { + + // NdefNfceeCcFileInfo fields + int cclen = in.readInt(); + int version = in.readInt(); + int maxLe = in.readInt(); + int maxLc = in.readInt(); + int ndefFileId = in.readInt(); + int ndefMaxSize = in.readInt(); + int ndefReadAccess = in.readInt(); + int ndefWriteAccess = in.readInt(); + + return new T4tNdefNfceeCcFileInfo(cclen, version, maxLe, maxLc, + ndefFileId, ndefMaxSize, + ndefReadAccess, ndefWriteAccess); + } + + @Override + public T4tNdefNfceeCcFileInfo[] newArray(int size) { + return new T4tNdefNfceeCcFileInfo[size]; + } + }; +} diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java index 9ff83fe77c9b..308b5d1831a6 100644 --- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -171,6 +171,12 @@ public final class ApduServiceInfo implements Parcelable { private boolean mShouldDefaultToObserveMode; /** + * Whether or not this service wants to share the same routing priority as the + * Wallet role owner. + */ + private boolean mShareRolePriority; + + /** * @hide */ @UnsupportedAppUsage @@ -307,6 +313,12 @@ public final class ApduServiceInfo implements Parcelable { mShouldDefaultToObserveMode = sa.getBoolean( R.styleable.HostApduService_shouldDefaultToObserveMode, false); + if (Flags.nfcAssociatedRoleServices()) { + mShareRolePriority = sa.getBoolean( + R.styleable.HostApduService_shareRolePriority, + false + ); + } sa.recycle(); } else { TypedArray sa = res.obtainAttributes(attrs, @@ -337,6 +349,12 @@ public final class ApduServiceInfo implements Parcelable { } } mStaticOffHostName = mOffHostName; + if (Flags.nfcAssociatedRoleServices()) { + mShareRolePriority = sa.getBoolean( + R.styleable.OffHostApduService_shareRolePriority, + false + ); + } sa.recycle(); } @@ -728,6 +746,17 @@ public final class ApduServiceInfo implements Parcelable { } /** + * Returns whether or not this service wants to share the Wallet role holder priority + * with other packages/services with the same signature. + * + * @return whether or not this service wants to share priority + */ + @FlaggedApi(Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) + public boolean shareRolePriority() { + return mShareRolePriority; + } + + /** * Returns description of service. * @return user readable description of service */ diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java index 24ff7ab9c2b6..803770218299 100644 --- a/nfc/java/android/nfc/cardemulation/CardEmulation.java +++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java @@ -22,6 +22,7 @@ import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -45,6 +46,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; +import android.telephony.SubscriptionManager; import android.util.ArrayMap; import android.util.Log; @@ -244,6 +246,25 @@ public final class CardEmulation { @Retention(RetentionPolicy.SOURCE) public @interface SetServiceEnabledStatusCode {} + /** + * Property name used to indicate that an application wants to allow associated services + * to share the same AID routing priority when this application is the role holder. + * <p> + * Example: + * <pre> + * {@code + * <application> + * ... + * <property android:name="android.nfc.cardemulation.PROPERTY_ALLOW_SHARED_ROLE_PRIORITY" + * android:value="true"/> + * </application> + * } + * </pre> + */ + @FlaggedApi(Flags.FLAG_NFC_ASSOCIATED_ROLE_SERVICES) + public static final String PROPERTY_ALLOW_SHARED_ROLE_PRIORITY = + "android.nfc.cardemulation.PROPERTY_ALLOW_SHARED_ROLE_PRIORITY"; + static boolean sIsInitialized = false; static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>(); static INfcCardEmulation sService; @@ -1010,6 +1031,7 @@ public final class CardEmulation { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) public void overrideRoutingTable( @NonNull Activity activity, @ProtocolAndTechnologyRoute int protocol, @@ -1037,6 +1059,7 @@ public final class CardEmulation { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) public void recoverRoutingTable(@NonNull Activity activity) { if (!activity.isResumed()) { @@ -1058,6 +1081,97 @@ public final class CardEmulation { } /** + * Setting the default subscription ID succeeded. + * @hide + */ + @SystemApi + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public static final int SET_SUBSCRIPTION_ID_STATUS_SUCCESS = 0; + + /** + * Setting the default subscription ID failed because the subscription ID is invalid. + * @hide + */ + @SystemApi + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID = 1; + + /** + * Setting the default subscription ID failed because there was an internal error processing + * the request. For ex: NFC service died in the middle of handling the API. + * @hide + */ + @SystemApi + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR = 2; + + /** + * Setting the default subscription ID failed because this feature is not supported on the + * device. + * @hide + */ + @SystemApi + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED = 3; + + /** @hide */ + @IntDef(prefix = "SET_SUBSCRIPTION_ID_STATUS_", + value = { + SET_SUBSCRIPTION_ID_STATUS_SUCCESS, + SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID, + SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR, + SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SetSubscriptionIdStatus {} + + /** + * Sets the system's default NFC subscription id. + * + * <p> For devices with multiple UICC/EUICC that is configured to be NFCEE, this sets the + * default UICC NFCEE that will handle NFC offhost CE transactoions </p> + * + * @param subscriptionId the default NFC subscription Id to set. + * @return status of the operation. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public @SetSubscriptionIdStatus int setDefaultNfcSubscriptionId(int subscriptionId) { + return callServiceReturn(() -> + sService.setDefaultNfcSubscriptionId( + subscriptionId, mContext.getPackageName()), + SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR); + } + + /** + * Returns the system's default NFC subscription id. + * + * <p> For devices with multiple UICC/EUICC that is configured to be NFCEE, this returns the + * default UICC NFCEE that will handle NFC offhost CE transactoions </p> + * <p> If the device has no UICC that can serve as NFCEE, this will return + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.</p> + * + * @return the default NFC subscription Id if set, + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public int getDefaultNfcSubscriptionId() { + return callServiceReturn(() -> + sService.getDefaultNfcSubscriptionId(mContext.getPackageName()), + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + } + + /** * Returns the value of {@link Settings.Secure#NFC_PAYMENT_DEFAULT_COMPONENT}. * * @param context A context diff --git a/nfc/tests/src/android/nfc/NdefMessageTest.java b/nfc/tests/src/android/nfc/NdefMessageTest.java new file mode 100644 index 000000000000..9ca295da75c3 --- /dev/null +++ b/nfc/tests/src/android/nfc/NdefMessageTest.java @@ -0,0 +1,59 @@ +/* + * 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.nfc; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class NdefMessageTest { + private NdefMessage mNdefMessage; + private NdefRecord mNdefRecord; + + @Before + public void setUp() { + mNdefRecord = NdefRecord.createUri("http://www.example.com"); + mNdefMessage = new NdefMessage(mNdefRecord); + } + + @After + public void tearDown() { + } + + @Test + public void testGetRecords() { + NdefRecord[] records = mNdefMessage.getRecords(); + assertThat(records).isNotNull(); + assertThat(records).hasLength(1); + assertThat(records[0]).isEqualTo(mNdefRecord); + } + + @Test + public void testToByteArray() throws FormatException { + byte[] bytes = mNdefMessage.toByteArray(); + assertThat(bytes).isNotNull(); + assertThat(bytes.length).isGreaterThan(0); + NdefMessage ndefMessage = new NdefMessage(bytes); + assertThat(ndefMessage).isNotNull(); + } +} diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp index 2beffda34b05..43d8a628837b 100644 --- a/packages/CrashRecovery/framework/Android.bp +++ b/packages/CrashRecovery/framework/Android.bp @@ -14,6 +14,7 @@ java_sdk_library { name: "framework-platformcrashrecovery", srcs: [":framework-crashrecovery-sources"], defaults: ["framework-non-updatable-unbundled-defaults"], + permitted_packages: ["android.service.watchdog"], aidl: { include_dirs: [ "frameworks/base/core/java", diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java index 8c15b093e48e..2ba93f15f7fc 100644 --- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java @@ -456,7 +456,7 @@ public class PackageWatchdog { * * <p>This method could be called frequently if there is a severe problem on the device. */ - public void onPackageFailure(@NonNull List<VersionedPackage> packages, + public void notifyPackageFailure(@NonNull List<VersionedPackage> packages, @FailureReasons int failureReason) { if (packages == null) { Slog.w(TAG, "Could not resolve a list of failing packages"); @@ -467,7 +467,7 @@ public class PackageWatchdog { if (Flags.recoverabilityDetection()) { if (now >= mLastMitigation && (now - mLastMitigation) < getMitigationWindowMs()) { - Slog.i(TAG, "Skipping onPackageFailure mitigation"); + Slog.i(TAG, "Skipping notifyPackageFailure mitigation"); return; } } @@ -494,7 +494,7 @@ public class PackageWatchdog { ObserverInternal observer = mAllObservers.valueAt(oIndex); PackageHealthObserver registeredObserver = observer.registeredObserver; if (registeredObserver != null - && observer.onPackageFailureLocked( + && observer.notifyPackageFailureLocked( versionedPackage.getPackageName())) { MonitoredPackage p = observer.getMonitoredPackage( versionedPackage.getPackageName()); @@ -693,7 +693,7 @@ public class PackageWatchdog { // Check if native watchdog reported a crash if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) { // We rollback all available low impact rollbacks when crash is unattributable - onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH); + notifyPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH); // we stop polling after an attempt to execute rollback, regardless of whether the // attempt succeeds or not } else { @@ -731,6 +731,25 @@ public class PackageWatchdog { } /** + * The minimum value that can be returned by any observer. + * It represents that no mitigations were available. + */ + public static final int LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT = + PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + + /** + * The mitigation impact beyond which the user will start noticing the mitigations. + */ + public static final int MEDIUM_USER_IMPACT_THRESHOLD = + PackageHealthObserverImpact.USER_IMPACT_LEVEL_20; + + /** + * The mitigation impact beyond which the user impact is severely high. + */ + public static final int HIGH_USER_IMPACT_THRESHOLD = + PackageHealthObserverImpact.USER_IMPACT_LEVEL_71; + + /** * Possible severity values of the user impact of a * {@link PackageHealthObserver#onExecuteHealthCheckMitigation}. * @hide @@ -773,6 +792,11 @@ public class PackageWatchdog { /** * Called when health check fails for the {@code versionedPackage}. * + * Note: if the returned user impact is higher than + * {@link #DEFAULT_HIGH_USER_IMPACT_THRESHOLD}, then + * {@link #onExecuteHealthCheckMitigation} would be called only in severe device conditions + * like boot-loop or network failure. + * * @param versionedPackage the package that is failing. This may be null if a native * service is crashing. * @param failureReason the type of failure that is occurring. @@ -780,8 +804,8 @@ public class PackageWatchdog { * (including this time). * * - * @return any one of {@link PackageHealthObserverImpact} to express the impact - * to the user on {@link #onExecuteHealthCheckMitigation} + * @return any value greater than {@link #LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT} to express + * the impact of mitigation on the user in {@link #onExecuteHealthCheckMitigation} */ @PackageHealthObserverImpact int onHealthCheckFailed( @Nullable VersionedPackage versionedPackage, @@ -790,9 +814,8 @@ public class PackageWatchdog { /** * This would be called after {@link #onHealthCheckFailed}. - * This is called only if current observer returned least - * {@link PackageHealthObserverImpact} mitigation for failed health - * check. + * This is called only if current observer returned least impact mitigation for failed + * health check. * * @param versionedPackage the package that is failing. This may be null if a native * service is crashing. @@ -811,6 +834,9 @@ public class PackageWatchdog { * * @param mitigationCount the number of times mitigation has been attempted for this * boot loop (including this time). + * + * @return any value greater than {@link #LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT} to express + * the impact of mitigation on the user in {@link #onExecuteBootLoopMitigation} */ default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) { return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; @@ -818,11 +844,13 @@ public class PackageWatchdog { /** * This would be called after {@link #onBootLoop}. - * This is called only if current observer returned least - * {@link PackageHealthObserverImpact} mitigation for fixing boot loop + * This is called only if current observer returned least impact mitigation for fixing + * boot loop. * * @param mitigationCount the number of times mitigation has been attempted for this * boot loop (including this time). + * + * @return {@code true} if action was executed successfully, {@code false} otherwise */ default boolean onExecuteBootLoopMitigation(int mitigationCount) { return false; @@ -916,7 +944,7 @@ public class PackageWatchdog { * effectively behave as if the explicit health check hasn't passed for {@code packageName}. * * <p> {@code packageName} can still be considered failed if reported by - * {@link #onPackageFailureLocked} before the package expires. + * {@link #notifyPackageFailureLocked} before the package expires. * * <p> Triggered by components outside the system server when they are fully functional after an * update. @@ -1317,7 +1345,6 @@ public class PackageWatchdog { * Check if we're currently attempting to reboot during mitigation. This method must return * true if triggered reboot early during a boot loop, since the device will not be fully booted * at this time. - * @hide */ public static boolean isRecoveryTriggeredReboot() { return isFactoryResetPropertySet() || isRebootPropertySet(); @@ -1461,7 +1488,7 @@ public class PackageWatchdog { * @hide */ @GuardedBy("sLock") - public boolean onPackageFailureLocked(String packageName) { + public boolean notifyPackageFailureLocked(String packageName) { if (getMonitoredPackage(packageName) == null && registeredObserver.isPersistent() && registeredObserver.mayObservePackage(packageName)) { putMonitoredPackage(sPackageWatchdog.newMonitoredPackage( diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java index 129e47f22f38..88fe36cda395 100644 --- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java @@ -477,7 +477,7 @@ public class PackageWatchdog { * * <p>This method could be called frequently if there is a severe problem on the device. */ - public void onPackageFailure(@NonNull List<VersionedPackage> packages, + public void notifyPackageFailure(@NonNull List<VersionedPackage> packages, @FailureReasons int failureReason) { if (packages == null) { Slog.w(TAG, "Could not resolve a list of failing packages"); @@ -488,7 +488,7 @@ public class PackageWatchdog { if (Flags.recoverabilityDetection()) { if (now >= mLastMitigation && (now - mLastMitigation) < getMitigationWindowMs()) { - Slog.i(TAG, "Skipping onPackageFailure mitigation"); + Slog.i(TAG, "Skipping notifyPackageFailure mitigation"); return; } } @@ -515,7 +515,7 @@ public class PackageWatchdog { ObserverInternal observer = mAllObservers.valueAt(oIndex); PackageHealthObserver registeredObserver = observer.registeredObserver; if (registeredObserver != null - && observer.onPackageFailureLocked( + && observer.notifyPackageFailureLocked( versionedPackage.getPackageName())) { MonitoredPackage p = observer.getMonitoredPackage( versionedPackage.getPackageName()); @@ -714,7 +714,7 @@ public class PackageWatchdog { // Check if native watchdog reported a crash if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) { // We rollback all available low impact rollbacks when crash is unattributable - onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH); + notifyPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH); // we stop polling after an attempt to execute rollback, regardless of whether the // attempt succeeds or not } else { @@ -926,7 +926,7 @@ public class PackageWatchdog { * effectively behave as if the explicit health check hasn't passed for {@code packageName}. * * <p> {@code packageName} can still be considered failed if reported by - * {@link #onPackageFailureLocked} before the package expires. + * {@link #notifyPackageFailureLocked} before the package expires. * * <p> Triggered by components outside the system server when they are fully functional after an * update. @@ -1253,7 +1253,7 @@ public class PackageWatchdog { return; } final List<VersionedPackage> pkgList = Collections.singletonList(pkg); - onPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK); + notifyPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK); }); } @@ -1467,7 +1467,7 @@ public class PackageWatchdog { * @hide */ @GuardedBy("mLock") - public boolean onPackageFailureLocked(String packageName) { + public boolean notifyPackageFailureLocked(String packageName) { if (getMonitoredPackage(packageName) == null && registeredObserver.isPersistent() && registeredObserver.mayObservePackage(packageName)) { putMonitoredPackage(sPackageWatchdog.newMonitoredPackage( diff --git a/packages/NeuralNetworks/OWNERS b/packages/NeuralNetworks/OWNERS new file mode 100644 index 000000000000..6b391503b5c4 --- /dev/null +++ b/packages/NeuralNetworks/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 195575 + +sandeepbandaru@google.com +shivanker@google.com +shiqing@google.com
\ No newline at end of file diff --git a/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt b/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt index 0cdfc6610bbd..f9931cf3238d 100644 --- a/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt +++ b/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt @@ -31,7 +31,7 @@ class IntroPreference @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 -) : Preference(context, attrs, defStyleAttr, defStyleRes) { +) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin { private var isCollapsable: Boolean = false private var minLines: Int = 2 @@ -68,6 +68,7 @@ class IntroPreference @JvmOverloads constructor( (holder.findViewById(R.id.collapsable_summary) as? CollapsableTextView)?.apply { setCollapsable(isCollapsable) setMinLines(minLines) + visibility = if (summary.isNullOrEmpty()) View.GONE else View.VISIBLE setText(summary.toString()) if (hyperlinkListener != null) { setHyperlinkListener(hyperlinkListener) diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt index c2728b4ad012..7601b9a31041 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceDataStoreAdapter.kt @@ -20,7 +20,7 @@ import androidx.preference.PreferenceDataStore import com.android.settingslib.datastore.KeyValueStore /** Adapter to translate [KeyValueStore] into [PreferenceDataStore]. */ -class PreferenceDataStoreAdapter(private val keyValueStore: KeyValueStore) : PreferenceDataStore() { +class PreferenceDataStoreAdapter(val keyValueStore: KeyValueStore) : PreferenceDataStore() { override fun getBoolean(key: String, defValue: Boolean): Boolean = keyValueStore.getValue(key, Boolean::class.javaObjectType) ?: defValue diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt index 62ac3ade62a4..7cec59c9090c 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt @@ -74,16 +74,12 @@ class PreferenceScreenBindingHelper( private val preferences: ImmutableMap<String, PreferenceHierarchyNode> private val dependencies: ImmutableMultimap<String, String> private val lifecycleAwarePreferences: Array<PreferenceLifecycleProvider> - private val storages = mutableSetOf<KeyedObservable<String>>() + private val storages = mutableMapOf<String, KeyedObservable<String>>() private val preferenceObserver: KeyedObserver<String?> private val storageObserver = - KeyedObserver<String?> { key, _ -> - if (key != null) { - notifyChange(key, CHANGE_REASON_VALUE) - } - } + KeyedObserver<String> { key, _ -> notifyChange(key, CHANGE_REASON_VALUE) } init { val preferencesBuilder = ImmutableMap.builder<String, PreferenceHierarchyNode>() @@ -98,7 +94,6 @@ class PreferenceScreenBindingHelper( preferencesBuilder.put(it.key, this) it.dependencyOfEnabledState(context)?.addDependency(it) if (it is PreferenceLifecycleProvider) lifecycleAwarePreferences.add(it) - if (it is PersistentPreference<*>) storages.add(it.storage(context)) } } @@ -120,7 +115,16 @@ class PreferenceScreenBindingHelper( preferenceObserver = KeyedObserver { key, reason -> onPreferenceChange(key, reason) } addObserver(preferenceObserver, mainExecutor) - for (storage in storages) storage.addObserver(storageObserver, mainExecutor) + + preferenceScreen.forEachRecursively { + val preferenceDataStore = it.preferenceDataStore + if (preferenceDataStore is PreferenceDataStoreAdapter) { + val key = it.key + val keyValueStore = preferenceDataStore.keyValueStore + storages[key] = keyValueStore + keyValueStore.addObserver(key, storageObserver, mainExecutor) + } + } } private fun onPreferenceChange(key: String?, reason: Int) { @@ -181,7 +185,7 @@ class PreferenceScreenBindingHelper( fun onDestroy() { removeObserver(preferenceObserver) - for (storage in storages) storage.removeObserver(storageObserver) + for ((key, storage) in storages) storage.removeObserver(key, storageObserver) for (preference in lifecycleAwarePreferences) { preference.onDestroy(preferenceLifecycleContext) } diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt new file mode 100644 index 000000000000..2e7221bd4d7f --- /dev/null +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/Utils.kt @@ -0,0 +1,33 @@ +/* + * 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.preference + +import androidx.preference.Preference +import androidx.preference.PreferenceGroup + +/** Traversals preference hierarchy recursively and applies an action. */ +fun PreferenceGroup.forEachRecursively(action: (Preference) -> Unit) { + action.invoke(this) + for (index in 0 until preferenceCount) { + val preference = getPreference(index) + if (preference is PreferenceGroup) { + preference.forEachRecursively(action) + } else { + action.invoke(preference) + } + } +} diff --git a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml index ea033a39f766..7d366f3091bc 100644 --- a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml +++ b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml @@ -22,9 +22,9 @@ android:layout_height="wrap_content" android:gravity="center_vertical" android:orientation="vertical" - android:layout_marginStart="?android:attr/listPreferredItemPaddingStart" - android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingBottom="16dp"> + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:paddingBottom="@dimen/settingslib_expressive_space_small1"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" diff --git a/packages/SettingsLib/res/drawable/ic_tv_box_internal_speaker.xml b/packages/SettingsLib/res/drawable/ic_tv_box_internal_speaker.xml new file mode 100644 index 000000000000..2a90e051b83b --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_tv_box_internal_speaker.xml @@ -0,0 +1,25 @@ +<!-- + ~ 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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?android:attr/textColorPrimary" + android:autoMirrored="true"> + <path android:fillColor="#FFFFFFFF" + android:pathData="M14,20.725V18.675Q16.25,18.025 17.625,16.175Q19,14.325 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,15.15 19.05,17.587Q17.1,20.025 14,20.725ZM3,15V9H7L12,4V20L7,15ZM14,16V7.95Q15.175,8.5 15.838,9.6Q16.5,10.7 16.5,12Q16.5,13.275 15.838,14.362Q15.175,15.45 14,16ZM10,8.85 L7.85,11H5V13H7.85L10,15.15ZM7.5,12Z"/> +</vector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index f03014ca95e2..eaf155df4785 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1419,7 +1419,7 @@ <!-- Name of the internal speaker and mic. [CHAR LIMIT=30] --> <string name="media_transfer_this_device_name_desktop">This computer (internal)</string> <!-- Name of the default media output of the TV. [CHAR LIMIT=30] --> - <string name="media_transfer_this_device_name_tv">@string/tv_media_transfer_default</string> + <string name="media_transfer_this_device_name_tv">This TV</string> <!-- Name of the dock device. [CHAR LIMIT=30] --> <string name="media_transfer_dock_speaker_device_name">Dock speaker</string> <!-- Default name of the external device. [CHAR LIMIT=30] --> @@ -1462,12 +1462,11 @@ <!-- Media output switcher. Subtitle for devices connected through HDMI EARC if a device name is available. [CHAR LIMIT=NONE] --> <string name="tv_media_transfer_earc_subtitle">Connected via eARC</string> - <!-- TV media output switcher. Title for the default audio output of the device [CHAR LIMIT=NONE] --> - <string name="tv_media_transfer_default">TV default</string> - <!-- TV media output switcher. Subtitle for default audio output which is HDMI, e.g. TV dongle [CHAR LIMIT=NONE] --> - <string name="tv_media_transfer_hdmi">HDMI output</string> - <!-- TV media output switcher. Subtitle for default audio output which is internal speaker, i.e. panel VTs [CHAR LIMIT=NONE] --> - <string name="tv_media_transfer_internal_speakers">Internal speakers</string> + <!-- TV media output switcher. Subtitle for default audio output which is internal speaker [CHAR LIMIT=NONE] --> + <string name="tv_media_transfer_internal_speakers">Built-in speaker</string> + + <!-- TV media output switcher. Title for default audio output which is HDMI, e.g. TV dongle [CHAR LIMIT=NONE] --> + <string name="tv_media_transfer_hdmi_title">TV Audio</string> <!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] --> <string name="profile_connect_timeout_subtext">Problem connecting. Turn device off & back on</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java index b3e48b26782e..fa28cf6c8a76 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java @@ -94,6 +94,14 @@ public class HearingAidDeviceManager { boolean setSubDeviceIfNeeded(CachedBluetoothDevice newDevice) { final long hiSyncId = newDevice.getHiSyncId(); if (isValidHiSyncId(hiSyncId)) { + // The remote device supports CSIP, the other ear should be processed as a member + // device. Ignore hiSyncId grouping from ASHA here. + if (newDevice.getProfiles().stream().anyMatch( + profile -> profile instanceof CsipSetCoordinatorProfile)) { + Log.w(TAG, "Skip ASHA grouping since this device supports CSIP"); + return false; + } + final CachedBluetoothDevice hearingAidDevice = getCachedDevice(hiSyncId); // Just add one of the hearing aids from a pair in the list that is shown in the UI. // Once there is another device with the same hiSyncId, to add new device as sub @@ -161,6 +169,7 @@ public class HearingAidDeviceManager { // device. Ignore hiSyncId grouping from ASHA here. if (cachedDevice.getProfiles().stream().anyMatch( profile -> profile instanceof CsipSetCoordinatorProfile)) { + Log.w(TAG, "Skip ASHA grouping since this device supports CSIP"); continue; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt index 07abb6b912b6..888f54f4bb34 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastCallbackExt.kt @@ -69,8 +69,8 @@ val LocalBluetoothLeBroadcast.onBroadcastStartedOrStopped: Flow<Unit> } .buffer(capacity = Channel.CONFLATED) -/** [Flow] for [BluetoothLeBroadcast.Callback] onPlaybackStarted event */ -val LocalBluetoothLeBroadcast.onPlaybackStarted: Flow<Unit> +/** [Flow] for [BluetoothLeBroadcast.Callback] onBroadcastMetadataChanged event */ +val LocalBluetoothLeBroadcast.onBroadcastMetadataChanged: Flow<Unit> get() = callbackFlow { val listener = @@ -87,7 +87,6 @@ val LocalBluetoothLeBroadcast.onPlaybackStarted: Flow<Unit> } override fun onPlaybackStarted(reason: Int, broadcastId: Int) { - launch { trySend(Unit) } } override fun onPlaybackStopped(reason: Int, broadcastId: Int) { @@ -100,7 +99,9 @@ val LocalBluetoothLeBroadcast.onPlaybackStarted: Flow<Unit> override fun onBroadcastMetadataChanged( broadcastId: Int, metadata: BluetoothLeBroadcastMetadata - ) {} + ) { + trySend(Unit) + } } registerServiceCallBack( ConcurrentUtils.DIRECT_EXECUTOR, diff --git a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java index 717a8ee32082..aa2ede311e23 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java @@ -33,6 +33,8 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; +import android.media.AudioDeviceInfo; +import android.media.AudioManager; import android.media.MediaRoute2Info; import android.os.SystemProperties; import android.util.SparseIntArray; @@ -116,14 +118,15 @@ public class DeviceIconUtil { @SuppressLint("SwitchIntDef") @DrawableRes - private static int getIconResourceIdForTv(@MediaRoute2Info.Type int type) { + private int getIconResourceIdForTv(@MediaRoute2Info.Type int type) { return switch (type) { case MediaRoute2Info.TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_HEADSET -> R.drawable.ic_headphone; case MediaRoute2Info.TYPE_USB_ACCESSORY -> R.drawable.ic_usb; case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device; - case MediaRoute2Info.TYPE_HDMI, MediaRoute2Info.TYPE_BUILTIN_SPEAKER -> - R.drawable.ic_tv; + case MediaRoute2Info.TYPE_BUILTIN_SPEAKER -> + isPanelTv() ? R.drawable.ic_tv : R.drawable.ic_tv_box_internal_speaker; + case MediaRoute2Info.TYPE_HDMI -> R.drawable.ic_tv; case MediaRoute2Info.TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_EARC -> R.drawable.ic_hdmi; case MediaRoute2Info.TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADPHONES -> @@ -132,6 +135,23 @@ public class DeviceIconUtil { }; } + private boolean isPanelTv() { + if (mContext == null) { + // This should only happen during testing. + return true; + } + AudioManager audioManager = mContext.getSystemService(AudioManager.class); + AudioDeviceInfo[] devices = audioManager.getDevices( + AudioManager.GET_DEVICES_OUTPUTS); + // If we have an HDMI output (not ARC/eARC) we can assume it's a dongle / set top box. + for (AudioDeviceInfo device : devices) { + if (device.getType() == TYPE_HDMI) { + return false; + } + } + return true; + } + static { AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_DEVICE); AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_HEADSET, MediaRoute2Info.TYPE_USB_HEADSET); diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java index 2321097d42d7..b01b7c9048ba 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java @@ -390,6 +390,16 @@ public abstract class MediaDevice implements Comparable<MediaDevice> { } /** + * Get the {@link MediaRoute2Info.Type} of the device. + */ + public int getRouteType() { + if (mRouteInfo == null) { + return TYPE_UNKNOWN; + } + return mRouteInfo.getType(); + } + + /** * Checks if route's volume is fixed, if true, we should disable volume control for the device. * * @return route for this device is fixed. diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java index 4766a869e406..6ff1a99156ac 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java @@ -29,6 +29,7 @@ import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; import static android.media.MediaRoute2Info.TYPE_LINE_DIGITAL; import static android.media.MediaRoute2Info.TYPE_LINE_ANALOG; import static android.media.MediaRoute2Info.TYPE_AUX_LINE; + import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER; import android.Manifest; @@ -40,6 +41,7 @@ import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiPortInfo; import android.media.MediaRoute2Info; import android.media.RouteListingPreference; +import android.os.Build; import android.os.SystemProperties; import android.util.Log; @@ -72,7 +74,7 @@ public class PhoneMediaDevice extends MediaDevice { /** Returns this device name for media transfer. */ public static @NonNull String getMediaTransferThisDeviceName(@NonNull Context context) { if (isTv(context)) { - return context.getString(R.string.media_transfer_this_device_name_tv); + return Build.MODEL; } else if (isTablet()) { return context.getString(R.string.media_transfer_this_device_name_tablet); } else if (inputRoutingEnabledAndIsDesktop(context)) { @@ -110,7 +112,7 @@ public class PhoneMediaDevice extends MediaDevice { name = getMediaTransferThisDeviceName(context); break; case TYPE_HDMI: - name = context.getString(isTv ? R.string.tv_media_transfer_default : + name = context.getString(isTv ? R.string.tv_media_transfer_hdmi_title : R.string.media_transfer_external_device_name); break; case TYPE_HDMI_ARC: @@ -223,8 +225,6 @@ public class PhoneMediaDevice extends MediaDevice { switch (mRouteInfo.getType()) { case TYPE_BUILTIN_SPEAKER: return mContext.getString(R.string.tv_media_transfer_internal_speakers); - case TYPE_HDMI: - return mContext.getString(R.string.tv_media_transfer_hdmi); case TYPE_HDMI_ARC: if (getHdmiOutDeviceName(mContext) == null) { // Connection type is already part of the title. diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java index 1b0e1f1236c8..013ff923e130 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java @@ -16,11 +16,14 @@ package com.android.settingslib.applications; +import static android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import android.graphics.drawable.Drawable; +import android.util.Log; import org.junit.After; import org.junit.Before; @@ -30,9 +33,12 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; +import java.util.concurrent.atomic.AtomicInteger; + @RunWith(RobolectricTestRunner.class) public class AppIconCacheManagerTest { + private static final String TAG = "AppIconCacheManagerTest"; private static final String APP_PACKAGE_NAME = "com.test.app"; private static final String APP_PACKAGE_NAME1 = "com.test.app1"; private static final String APP_PACKAGE_NAME2 = "com.test.app2"; @@ -176,4 +182,28 @@ public class AppIconCacheManagerTest { assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME2, APP_UID)).isNull(); assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME3, APP_UID)).isNull(); } + + @Test + public void trimMemory_multiThread_shouldNotCrash() { + int numberOfTasks = 10; + AtomicInteger completedTasks = new AtomicInteger(0); + + Runnable task = + () -> { + String threadName = Thread.currentThread().getName(); + Log.i(TAG, "Starting thread: " + threadName); + AppIconCacheManager.getInstance().trimMemory(TRIM_MEMORY_BACKGROUND); + completedTasks.incrementAndGet(); + Log.i(TAG, "Ending thread: " + threadName); + }; + + for (Integer i = 0; i < numberOfTasks; i++) { + Thread thread = new Thread(task); + thread.start(); + } + + while (completedTasks.get() < numberOfTasks) { + // Wait until all threads are finished. + } + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java index 883640db5e27..5ac22a732f3c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java @@ -173,7 +173,7 @@ public class DeviceIconUtilTest { public void getIconResIdFromMediaRouteType_tv_builtinSpeaker_isTv() { assertThat(new DeviceIconUtil(/* isTv */ true) .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)) - .isEqualTo(R.drawable.ic_tv); + .isAnyOf(R.drawable.ic_tv, R.drawable.ic_tv_box_internal_speaker); } @Test @@ -331,7 +331,7 @@ public class DeviceIconUtilTest { public void getIconResIdFromAudioDeviceType_tv_builtinSpeaker_isTv() { assertThat(new DeviceIconUtil(/* isTv */ true) .getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)) - .isEqualTo(R.drawable.ic_tv); + .isAnyOf(R.drawable.ic_tv, R.drawable.ic_tv_box_internal_speaker); } @Test diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 927a1c59cc76..1f291cdefb03 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -107,6 +107,8 @@ public class SecureSettings { Settings.Secure.DISPLAY_WHITE_BALANCE_ENABLED, Settings.Secure.SYNC_PARENT_SOUNDS, Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, + Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED, + Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE, Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, // ACCESSIBILITY_QS_TARGETS needs to be restored after ENABLED_ACCESSIBILITY_SERVICES diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 6d73ee27f076..abd5b9a4a4bb 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -160,6 +160,9 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.DISPLAY_WHITE_BALANCE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SYNC_PARENT_SOUNDS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE, + new InclusiveIntegerRangeValidator(0, 1)); VALIDATORS.put(Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.QS_TILES, TILE_LIST_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 600c36e5dfc9..5ae11bacb445 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -2124,6 +2124,15 @@ class SettingsProtoDumpUtil { SecureSettingsProto.Display.SCREEN_RESOLUTION_MODE); p.end(displayToken); + final long doubleTapPowerButtonToken = p.start(SecureSettingsProto.DOUBLE_TAP_POWER_BUTTON); + dumpSetting(s, p, + Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED, + SecureSettingsProto.DoubleTapPowerButton.GESTURE_ENABLED); + dumpSetting(s, p, + Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE, + SecureSettingsProto.DoubleTapPowerButton.GESTURE); + p.end(doubleTapPowerButtonToken); + final long dozeToken = p.start(SecureSettingsProto.DOZE); dumpSetting(s, p, Settings.Secure.DOZE_ENABLED, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java index d835c5f5c179..5ce97eb22a04 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java @@ -33,6 +33,16 @@ import java.util.Set; final class WritableNamespaces { public static final Set<String> ALLOWLIST = new ArraySet<String>(Arrays.asList( - "exo" + "adservices", + "captive_portal_login", + "connectivity", + "exo", + "nearby", + "netd_native", + "network_security", + "on_device_personalization", + "tethering", + "tethering_u_or_later_native", + "thread_network" )); } diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp index 3350efc33ad8..5f810858b7cd 100644 --- a/packages/Shell/Android.bp +++ b/packages/Shell/Android.bp @@ -27,6 +27,7 @@ android_app { ], flags_packages: [ "android.security.flags-aconfig", + "android.permission.flags-aconfig", ], platform_apis: true, certificate: "platform", @@ -51,5 +52,6 @@ android_library { manifest: "AndroidManifest.xml", flags_packages: [ "android.security.flags-aconfig", + "android.permission.flags-aconfig", ], } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index ac02af81420c..0ec5571a7b8f 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -151,7 +151,8 @@ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <uses-permission android:name="android.permission.LOCATION_BYPASS" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> - <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> + <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" + android:featureFlag="!android.security.protect_device_config_flags"/> <uses-permission android:name="android.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG" /> <uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS" /> @@ -981,6 +982,14 @@ <uses-permission android:name="android.permission.READ_SYSTEM_PREFERENCES" /> <uses-permission android:name="android.permission.WRITE_SYSTEM_PREFERENCES" /> + <!-- Permissions required for CTS test - ActivityManagerForegroundServiceTypeTest --> + <uses-permission android:name="android.permission.health.READ_HEART_RATE" + android:featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"/> + <uses-permission android:name="android.permission.health.READ_OXYGEN_SATURATION" + android:featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"/> + <uses-permission android:name="android.permission.health.READ_SKIN_TEMPERATURE" + android:featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"/> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" diff --git a/packages/StatementService/Android.bp b/packages/StatementService/Android.bp index 90e1808b7d44..39b0302beff8 100644 --- a/packages/StatementService/Android.bp +++ b/packages/StatementService/Android.bp @@ -38,8 +38,10 @@ android_app { "StatementServiceParser", "androidx.appcompat_appcompat", "androidx.collection_collection-ktx", + "androidx.room_room-runtime", "androidx.work_work-runtime", "androidx.work_work-runtime-ktx", "kotlinx-coroutines-android", ], + plugins: ["androidx.room_room-compiler-plugin"], } diff --git a/packages/StatementService/src/com/android/statementservice/database/Converters.kt b/packages/StatementService/src/com/android/statementservice/database/Converters.kt new file mode 100644 index 000000000000..21ecc8b4a651 --- /dev/null +++ b/packages/StatementService/src/com/android/statementservice/database/Converters.kt @@ -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.statementservice.database + +import android.content.UriRelativeFilter +import android.content.UriRelativeFilterGroup +import android.util.JsonReader +import androidx.room.TypeConverter +import org.json.JSONArray +import org.json.JSONObject +import java.io.StringReader +import java.util.ArrayList + +class Converters { + companion object { + private const val ACTION_NAME = "action" + private const val FILTERS_NAME = "filters" + private const val URI_PART_NAME = "uriPart" + private const val PATTERN_TYPE_NAME = "patternType" + private const val FILTER_NAME = "filter" + } + + @TypeConverter + fun groupsToJson(groups: List<UriRelativeFilterGroup>): String { + val json = JSONArray() + for (group in groups) { + json.put(groupToJson(group)) + } + return json.toString() + } + + @TypeConverter + fun stringToGroups(json: String): List<UriRelativeFilterGroup> { + val groups = ArrayList<UriRelativeFilterGroup>() + StringReader(json).use { stringReader -> + JsonReader(stringReader).use { reader -> + reader.beginArray() + while (reader.hasNext()) { + groups.add(parseGroup(reader)) + } + reader.endArray() + } + } + return groups + } + + private fun groupToJson(group: UriRelativeFilterGroup): JSONObject { + val jsonObject = JSONObject() + jsonObject.put(ACTION_NAME, group.action) + val filters = JSONArray() + for (filter in group.uriRelativeFilters) { + filters.put(filterToJson(filter)) + } + jsonObject.put(FILTERS_NAME, filters) + return jsonObject + } + + private fun filterToJson(filter: UriRelativeFilter): JSONObject { + val jsonObject = JSONObject() + jsonObject.put(URI_PART_NAME, filter.uriPart) + jsonObject.put(PATTERN_TYPE_NAME, filter.patternType) + jsonObject.put(FILTER_NAME, filter.filter) + return jsonObject + } + + private fun parseGroup(reader: JsonReader): UriRelativeFilterGroup { + val jsonObject = JSONObject() + reader.beginObject() + while (reader.hasNext()) { + val name = reader.nextName() + when (name) { + ACTION_NAME -> jsonObject.put(ACTION_NAME, reader.nextInt()) + FILTERS_NAME -> jsonObject.put(FILTERS_NAME, parseFilters(reader)) + else -> reader.skipValue() + } + } + reader.endObject() + + val group = UriRelativeFilterGroup(jsonObject.getInt(ACTION_NAME)) + val filters = jsonObject.getJSONArray(FILTERS_NAME) + for (i in 0 until filters.length()) { + val filter = filters.getJSONObject(i) + group.addUriRelativeFilter(UriRelativeFilter( + filter.getInt(URI_PART_NAME), + filter.getInt(PATTERN_TYPE_NAME), + filter.getString(FILTER_NAME) + )) + } + return group + } + + private fun parseFilters(reader: JsonReader): JSONArray { + val filters = JSONArray() + reader.beginArray() + while (reader.hasNext()) { + filters.put(parseFilter(reader)) + } + reader.endArray() + return filters + } + + private fun parseFilter(reader: JsonReader): JSONObject { + reader.beginObject() + val jsonObject = JSONObject() + while (reader.hasNext()) { + val name = reader.nextName() + when (name) { + URI_PART_NAME, PATTERN_TYPE_NAME -> jsonObject.put(name, reader.nextInt()) + FILTER_NAME -> jsonObject.put(name, reader.nextString()) + else -> reader.skipValue() + } + } + reader.endObject() + return jsonObject + } +}
\ No newline at end of file diff --git a/packages/StatementService/src/com/android/statementservice/database/DomainGroups.kt b/packages/StatementService/src/com/android/statementservice/database/DomainGroups.kt new file mode 100644 index 000000000000..c61666910cb4 --- /dev/null +++ b/packages/StatementService/src/com/android/statementservice/database/DomainGroups.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.statementservice.database + +import android.content.UriRelativeFilterGroup +import androidx.room.Entity + +@Entity(primaryKeys = ["packageName", "domain"]) +data class DomainGroups( + val packageName: String, + val domain: String, + val groups: List<UriRelativeFilterGroup> +)
\ No newline at end of file diff --git a/packages/StatementService/src/com/android/statementservice/database/DomainGroupsDao.kt b/packages/StatementService/src/com/android/statementservice/database/DomainGroupsDao.kt new file mode 100644 index 000000000000..3b4dcea48180 --- /dev/null +++ b/packages/StatementService/src/com/android/statementservice/database/DomainGroupsDao.kt @@ -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 com.android.statementservice.database + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query + +@Dao +interface DomainGroupsDao { + @Query("SELECT * FROM DomainGroups WHERE packageName = :packageName") + fun getDomainGroups(packageName: String): List<DomainGroups> + + @Insert + fun insertDomainGroups(vararg domainGroups: DomainGroups) + + @Query("DELETE FROM DomainGroups WHERE packageName = :packageName AND domain = :domain") + fun clear(packageName: String, domain: String) + + @Query("DELETE FROM DomainGroups WHERE packageName = :packageName") + fun clear(packageName: String) +}
\ No newline at end of file diff --git a/packages/StatementService/src/com/android/statementservice/database/DomainGroupsDatabase.kt b/packages/StatementService/src/com/android/statementservice/database/DomainGroupsDatabase.kt new file mode 100644 index 000000000000..39833f6bc80b --- /dev/null +++ b/packages/StatementService/src/com/android/statementservice/database/DomainGroupsDatabase.kt @@ -0,0 +1,41 @@ +/* + * 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.statementservice.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters + +@Database(entities = [DomainGroups::class], version = 1) +@TypeConverters(Converters::class) +abstract class DomainGroupsDatabase : RoomDatabase() { + companion object { + private const val DATABASE_NAME = "domain-groups" + @Volatile + private var instance: DomainGroupsDatabase? = null + + fun getInstance(context: Context) = instance ?: synchronized(this) { + instance ?: Room.databaseBuilder( + context, + DomainGroupsDatabase::class.java, DATABASE_NAME + ).build().also { instance = it } + } + } + abstract fun domainGroupsDao(): DomainGroupsDao +}
\ No newline at end of file diff --git a/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationReceiverV1.kt b/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationReceiverV1.kt index acb54f6093de..0d7a1fdbcfb8 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationReceiverV1.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationReceiverV1.kt @@ -22,6 +22,7 @@ import android.content.pm.PackageManager import androidx.work.ExistingWorkPolicy import androidx.work.WorkManager import com.android.statementservice.domain.worker.CollectV1Worker +import com.android.statementservice.domain.worker.GroupUpdateV1Worker import com.android.statementservice.domain.worker.SingleV1RequestWorker /** @@ -67,7 +68,7 @@ class DomainVerificationReceiverV1 : BaseDomainVerificationReceiver() { } } - //clear sp before enqueue unique work since policy is REPLACE + // clear sp before enqueue unique work since policy is REPLACE val deContext = context.createDeviceProtectedStorageContext() val editor = deContext?.getSharedPreferences(packageName, Context.MODE_PRIVATE)?.edit() editor?.clear()?.apply() @@ -78,6 +79,7 @@ class DomainVerificationReceiverV1 : BaseDomainVerificationReceiver() { workRequests ) .then(CollectV1Worker.buildRequest(verificationId, packageName)) + .then(GroupUpdateV1Worker.buildRequest(packageName)) .enqueue() } } diff --git a/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt b/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt index 29f844fb1a5d..6914347544de 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt @@ -24,6 +24,7 @@ import androidx.collection.LruCache import com.android.statementservice.network.retriever.StatementRetriever import com.android.statementservice.retriever.AbstractAsset import com.android.statementservice.retriever.AbstractAssetMatcher +import com.android.statementservice.retriever.Statement import com.android.statementservice.utils.Result import com.android.statementservice.utils.StatementUtils import com.android.statementservice.utils.component1 @@ -87,10 +88,10 @@ class DomainVerifier private constructor( host: String, packageName: String, network: Network? = null - ): Pair<WorkResult, VerifyStatus> { + ): Triple<WorkResult, VerifyStatus, Statement?> { val assetMatcher = synchronized(targetAssetCache) { targetAssetCache[packageName] } .takeIf { it!!.isPresent } - ?: return WorkResult.failure() to VerifyStatus.FAILURE_PACKAGE_MANAGER + ?: return Triple(WorkResult.failure(), VerifyStatus.FAILURE_PACKAGE_MANAGER, null) return verifyHost(host, assetMatcher.get(), network) } @@ -98,34 +99,34 @@ class DomainVerifier private constructor( host: String, assetMatcher: AbstractAssetMatcher, network: Network? = null - ): Pair<WorkResult, VerifyStatus> { + ): Triple<WorkResult, VerifyStatus, Statement?> { var exception: Exception? = null val resultAndStatus = try { val sourceAsset = StatementUtils.createWebAssetString(host) .let(AbstractAsset::create) val result = retriever.retrieve(sourceAsset, network) - ?: return WorkResult.success() to VerifyStatus.FAILURE_UNKNOWN + ?: return Triple(WorkResult.success(), VerifyStatus.FAILURE_UNKNOWN, null) when (result.responseCode) { HttpURLConnection.HTTP_MOVED_PERM, HttpURLConnection.HTTP_MOVED_TEMP -> { - WorkResult.failure() to VerifyStatus.FAILURE_REDIRECT + Triple(WorkResult.failure(), VerifyStatus.FAILURE_REDIRECT, null) } else -> { - val isVerified = result.statements.any { statement -> + val statement = result.statements.firstOrNull { statement -> (StatementUtils.RELATION.matches(statement.relation) && assetMatcher.matches(statement.target)) } - if (isVerified) { - WorkResult.success() to VerifyStatus.SUCCESS + if (statement != null) { + Triple(WorkResult.success(), VerifyStatus.SUCCESS, statement) } else { - WorkResult.failure() to VerifyStatus.FAILURE_REJECTED_BY_SERVER + Triple(WorkResult.failure(), VerifyStatus.FAILURE_REJECTED_BY_SERVER, statement) } } } } catch (e: Exception) { exception = e - WorkResult.retry() to VerifyStatus.FAILURE_UNKNOWN + Triple(WorkResult.retry(), VerifyStatus.FAILURE_UNKNOWN, null) } if (DEBUG) { diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/BaseRequestWorker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/BaseRequestWorker.kt index a17f9c9186ff..64d2d98b931b 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/worker/BaseRequestWorker.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/worker/BaseRequestWorker.kt @@ -17,9 +17,12 @@ package com.android.statementservice.domain.worker import android.content.Context +import android.content.UriRelativeFilterGroup +import android.content.pm.verify.domain.DomainVerificationInfo import android.content.pm.verify.domain.DomainVerificationManager import androidx.work.CoroutineWorker import androidx.work.WorkerParameters +import com.android.statementservice.database.DomainGroupsDatabase import com.android.statementservice.domain.DomainVerifier abstract class BaseRequestWorker( @@ -27,8 +30,19 @@ abstract class BaseRequestWorker( protected val params: WorkerParameters ) : CoroutineWorker(appContext, params) { + protected val database = DomainGroupsDatabase.getInstance(appContext).domainGroupsDao() + protected val verificationManager = appContext.getSystemService(DomainVerificationManager::class.java)!! protected val verifier = DomainVerifier.getInstance(appContext) + + protected fun updateUriRelativeFilterGroups(packageName: String, domainGroupUpdates: Map<String, List<UriRelativeFilterGroup>>) { + val verifiedDomains = verificationManager.getDomainVerificationInfo(packageName)?.hostToStateMap?.filterValues { + it == DomainVerificationInfo.STATE_SUCCESS || it == DomainVerificationInfo.STATE_MODIFIABLE_VERIFIED + }?.keys?.toList() ?: emptyList() + val domainGroups = verificationManager.getUriRelativeFilterGroups(packageName, verifiedDomains) + domainGroupUpdates.forEach { (domain, groups) -> domainGroups[domain] = groups } + verificationManager.setUriRelativeFilterGroups(packageName, domainGroups) + } } diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/GroupUpdateV1Worker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/GroupUpdateV1Worker.kt new file mode 100644 index 000000000000..f53dfc47acaa --- /dev/null +++ b/packages/StatementService/src/com/android/statementservice/domain/worker/GroupUpdateV1Worker.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.statementservice.domain.worker + +import android.content.Context +import androidx.work.Data +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkerParameters +import kotlinx.coroutines.coroutineScope + +class GroupUpdateV1Worker(appContext: Context, params: WorkerParameters) : + BaseRequestWorker(appContext, params) { + + companion object { + + private const val PACKAGE_NAME_KEY = "packageName" + + fun buildRequest(packageName: String) = OneTimeWorkRequestBuilder<GroupUpdateV1Worker>() + .setInputData( + Data.Builder() + .putString(PACKAGE_NAME_KEY, packageName) + .build() + ) + .build() + } + + override suspend fun doWork() = coroutineScope { + val packageName = params.inputData.getString(PACKAGE_NAME_KEY)!! + updateUriRelativeFilterGroups(packageName) + Result.success() + } + + private fun updateUriRelativeFilterGroups(packageName: String) { + val groupUpdates = database.getDomainGroups(packageName) + updateUriRelativeFilterGroups( + packageName, + groupUpdates.associateBy({it.domain}, {it.groups}) + ) + database.clear(packageName) + } +}
\ No newline at end of file diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt index 61ab2c264e6a..f83601a7807b 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt @@ -17,10 +17,13 @@ package com.android.statementservice.domain.worker import android.content.Context +import android.content.UriRelativeFilterGroup +import android.content.pm.verify.domain.DomainVerificationManager import androidx.work.NetworkType import androidx.work.WorkerParameters import com.android.statementservice.domain.VerifyStatus import com.android.statementservice.utils.AndroidUtils +import com.android.statementservice.utils.StatementUtils import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope @@ -36,7 +39,13 @@ class RetryRequestWorker( params: WorkerParameters ) : BaseRequestWorker(appContext, params) { - data class VerifyResult(val domainSetId: UUID, val host: String, val status: VerifyStatus) + data class VerifyResult( + val domainSetId: UUID, + val host: String, + val status: VerifyStatus, + val packageName: String, + val groups: List<UriRelativeFilterGroup> + ) override suspend fun doWork() = coroutineScope { if (!AndroidUtils.isReceiverV2Enabled(appContext)) { @@ -49,8 +58,11 @@ class RetryRequestWorker( .map { (domainSetId, packageName, host) -> async { if (isActive && !isStopped) { - val (_, status) = verifier.verifyHost(host, packageName, params.network) - VerifyResult(domainSetId, host, status) + val (_, status, statement) = verifier.verifyHost(host, packageName, params.network) + val groups = statement?.dynamicAppLinkComponents.orEmpty().map { + StatementUtils.createUriRelativeFilterGroup(it) + } + VerifyResult(domainSetId, host, status, packageName, groups) } else { // If the job gets cancelled, stop the remaining hosts, but continue the // job to commit the results for hosts that were already requested. @@ -60,17 +72,25 @@ class RetryRequestWorker( } .awaitAll() .filterNotNull() // TODO(b/159952358): Fast fail packages which can't be retrieved. - .groupBy { it.domainSetId } - .forEach { (domainSetId, resultsById) -> - resultsById.groupBy { it.status } - .mapValues { it.value.map(VerifyResult::host).toSet() } - .forEach { (status, hosts) -> - verificationManager.setDomainVerificationStatus( - domainSetId, - hosts, - status.value - ) + .groupBy { it.packageName } + .forEach { (packageName, resultsByName) -> + val groupUpdates = mutableMapOf<String, List<UriRelativeFilterGroup>>() + resultsByName.groupBy { it.domainSetId } + .forEach { (domainSetId, resultsById) -> + resultsById.groupBy { it.status } + .forEach { (status, verifyResults) -> + val error = verificationManager.setDomainVerificationStatus( + domainSetId, + verifyResults.map(VerifyResult::host).toSet(), + status.value + ) + if (error == DomainVerificationManager.STATUS_OK + && status == VerifyStatus.SUCCESS) { + verifyResults.forEach { groupUpdates[it.host] = it.groups } + } + } } + updateUriRelativeFilterGroups(packageName, groupUpdates) } // Succeed regardless of results since this retry is best effort and not required diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV1RequestWorker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV1RequestWorker.kt index 7a198cb59ca4..253a162a73a2 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV1RequestWorker.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV1RequestWorker.kt @@ -22,7 +22,9 @@ import androidx.work.Data import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters +import com.android.statementservice.database.DomainGroups import com.android.statementservice.utils.AndroidUtils +import com.android.statementservice.utils.StatementUtils import kotlinx.coroutines.coroutineScope class SingleV1RequestWorker(appContext: Context, params: WorkerParameters) : @@ -60,7 +62,9 @@ class SingleV1RequestWorker(appContext: Context, params: WorkerParameters) : val packageName = params.inputData.getString(PACKAGE_NAME_KEY)!! val host = params.inputData.getString(HOST_KEY)!! - val (result, status) = verifier.verifyHost(host, packageName, params.network) + database.clear(packageName, host) + + val (result, status, statement) = verifier.verifyHost(host, packageName, params.network) if (DEBUG) { Log.d( @@ -75,6 +79,10 @@ class SingleV1RequestWorker(appContext: Context, params: WorkerParameters) : val deContext = appContext.createDeviceProtectedStorageContext() val sp = deContext?.getSharedPreferences(packageName, Context.MODE_PRIVATE) sp?.edit()?.putInt("$HOST_SUCCESS_PREFIX$host", status.value)?.apply() + val groups = statement?.dynamicAppLinkComponents.orEmpty().map { + StatementUtils.createUriRelativeFilterGroup(it) + } + database.insertDomainGroups(DomainGroups(packageName, host, groups)) Result.success() } is Result.Failure -> { diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV2RequestWorker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV2RequestWorker.kt index 562b132d36d6..8b1347a69932 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV2RequestWorker.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV2RequestWorker.kt @@ -22,6 +22,7 @@ import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkerParameters import com.android.statementservice.utils.AndroidUtils +import com.android.statementservice.utils.StatementUtils import kotlinx.coroutines.coroutineScope import java.util.UUID @@ -59,9 +60,13 @@ class SingleV2RequestWorker(appContext: Context, params: WorkerParameters) : val packageName = params.inputData.getString(PACKAGE_NAME_KEY)!! val host = params.inputData.getString(HOST_KEY)!! - val (result, status) = verifier.verifyHost(host, packageName, params.network) + val (result, status, statement) = verifier.verifyHost(host, packageName, params.network) verificationManager.setDomainVerificationStatus(domainSetId, setOf(host), status.value) + val groups = statement?.dynamicAppLinkComponents.orEmpty().map { + StatementUtils.createUriRelativeFilterGroup(it) + } + updateUriRelativeFilterGroups(packageName, mapOf(host to groups)) result } diff --git a/packages/StatementService/src/com/android/statementservice/network/retriever/StatementParser.kt b/packages/StatementService/src/com/android/statementservice/network/retriever/StatementParser.kt index ad137400fa86..d10cb0f91c11 100644 --- a/packages/StatementService/src/com/android/statementservice/network/retriever/StatementParser.kt +++ b/packages/StatementService/src/com/android/statementservice/network/retriever/StatementParser.kt @@ -39,6 +39,11 @@ object StatementParser { private const val FIELD_NOT_STRING_FORMAT_STRING = "Expected %s to be string." private const val FIELD_NOT_ARRAY_FORMAT_STRING = "Expected %s to be array." + private const val COMMENTS_NAME = "comments" + private const val EXCLUDE_NAME = "exclude" + private const val FRAGMENT_NAME = "#" + private const val QUERY_NAME = "?" + private const val PATH_NAME = "/" /** * Parses a JSON array of statements. @@ -99,9 +104,7 @@ object StatementParser { FIELD_NOT_ARRAY_FORMAT_STRING.format(StatementUtils.ASSET_DESCRIPTOR_FIELD_RELATION) ) val target = AssetFactory.create(targetObject) - val dynamicAppLinkComponents = parseDynamicAppLinkComponents( - statement.optJSONObject(StatementUtils.ASSET_DESCRIPTOR_FIELD_RELATION_EXTENSIONS) - ) + val dynamicAppLinkComponents = parseDynamicAppLinkComponents(statement) val statements = (0 until relations.length()) .map { relations.getString(it) } @@ -129,13 +132,13 @@ object StatementParser { } private fun parseComponent(component: JSONObject): DynamicAppLinkComponent { - val query = component.optJSONObject("?") + val query = component.optJSONObject(QUERY_NAME) return DynamicAppLinkComponent.create( - component.optBoolean("exclude", false), - component.optString("#"), - component.optString("/"), + component.optBoolean(EXCLUDE_NAME, false), + if (component.has(FRAGMENT_NAME)) component.getString(FRAGMENT_NAME) else null, + if (component.has(PATH_NAME)) component.getString(PATH_NAME) else null, query?.keys()?.asSequence()?.associateWith { query.getString(it) }, - component.optString("comments") + component.optString(COMMENTS_NAME) ) } diff --git a/packages/StatementService/src/com/android/statementservice/retriever/DynamicAppLinkComponent.java b/packages/StatementService/src/com/android/statementservice/retriever/DynamicAppLinkComponent.java index dc27e125e204..c32f1949fed4 100644 --- a/packages/StatementService/src/com/android/statementservice/retriever/DynamicAppLinkComponent.java +++ b/packages/StatementService/src/com/android/statementservice/retriever/DynamicAppLinkComponent.java @@ -130,7 +130,7 @@ public final class DynamicAppLinkComponent { @Override public String toString() { StringBuilder statement = new StringBuilder(); - statement.append("HandleAllUriRule: "); + statement.append("DynamicAppLinkComponent: "); statement.append(mExclude); statement.append(", "); statement.append(mFragment); diff --git a/packages/StatementService/src/com/android/statementservice/retriever/JsonParser.java b/packages/StatementService/src/com/android/statementservice/retriever/JsonParser.java index 7635e8234dc0..ab1853c1d3ae 100644 --- a/packages/StatementService/src/com/android/statementservice/retriever/JsonParser.java +++ b/packages/StatementService/src/com/android/statementservice/retriever/JsonParser.java @@ -24,8 +24,6 @@ import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; /** * A helper class that creates a {@link JSONObject} from a {@link JsonReader}. @@ -48,7 +46,7 @@ public final class JsonParser { JsonToken token = reader.peek(); if (token.equals(JsonToken.BEGIN_ARRAY)) { - output.put(fieldName, new JSONArray(parseArray(reader))); + output.put(fieldName, parseArray(reader)); } else if (token.equals(JsonToken.STRING)) { output.put(fieldName, reader.nextString()); } else if (token.equals(JsonToken.BEGIN_OBJECT)) { @@ -57,9 +55,11 @@ public final class JsonParser { } catch (JSONException e) { errorMsg = e.getMessage(); } + } else if (token.equals(JsonToken.BOOLEAN)) { + output.put(fieldName, reader.nextBoolean()); } else { reader.skipValue(); - errorMsg = "Unsupported value type."; + errorMsg = "Unsupported value type: " + token; } } reader.endObject(); @@ -72,17 +72,36 @@ public final class JsonParser { } /** - * Parses one string array from the {@link JsonReader}. + * Parses one JSON array from the {@link JsonReader}. */ - public static List<String> parseArray(JsonReader reader) throws IOException { - ArrayList<String> output = new ArrayList<>(); + public static JSONArray parseArray(JsonReader reader) throws IOException, JSONException { + JSONArray output = new JSONArray(); + String errorMsg = null; reader.beginArray(); while (reader.hasNext()) { - output.add(reader.nextString()); + JsonToken token = reader.peek(); + if (token.equals(JsonToken.BEGIN_ARRAY)) { + output.put(parseArray(reader)); + } else if (token.equals(JsonToken.STRING)) { + output.put(reader.nextString()); + } else if (token.equals(JsonToken.BEGIN_OBJECT)) { + try { + output.put(parse(reader)); + } catch (JSONException e) { + errorMsg = e.getMessage(); + } + } else { + reader.skipValue(); + errorMsg = "Unsupported value type: " + token; + } } reader.endArray(); + if (errorMsg != null) { + throw new JSONException(errorMsg); + } + return output; } } diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java index c71ef8360008..129dd9b3c14d 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java @@ -36,7 +36,6 @@ import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceManager; -import com.android.systemui.accessibility.accessibilitymenu.Flags; import com.android.systemui.accessibility.accessibilitymenu.R; /** @@ -62,10 +61,8 @@ public class A11yMenuSettingsActivity extends FragmentActivity { ((TextView) findViewById(R.id.action_bar_title)).setText( getResources().getString(R.string.accessibility_menu_settings_name) ); - if (Flags.actionBarWrapContent()) { - setHeightWrapContent(findViewById(com.android.internal.R.id.action_bar)); - setHeightWrapContent(findViewById(com.android.internal.R.id.action_bar_container)); - } + setHeightWrapContent(findViewById(com.android.internal.R.id.action_bar)); + setHeightWrapContent(findViewById(com.android.internal.R.id.action_bar_container)); } private void setHeightWrapContent(View view) { diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java index 3db61a58c7a3..6bc0f42f39aa 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java @@ -220,9 +220,6 @@ public class A11yMenuOverlayLayout { @SuppressLint("MissingPermission") private boolean isShortcutRestricted(int shortcutId) { - if (!Flags.hideRestrictedActions()) { - return false; - } final UserManager userManager = mService.getSystemService(UserManager.class); if (userManager == null) { return false; @@ -366,12 +363,11 @@ public class A11yMenuOverlayLayout { if (mLayout.getVisibility() == View.VISIBLE) { mLayout.setVisibility(View.GONE); } else { - if (Flags.hideRestrictedActions()) { - // Reconfigure the shortcut list in case the set of restricted actions has changed. - mA11yMenuViewPager.configureViewPagerAndFooter( - mLayout, createShortcutList(), getPageIndex()); - updateViewLayout(); - } + // Reconfigure the shortcut list in case the set of restricted actions has changed. + mA11yMenuViewPager.configureViewPagerAndFooter( + mLayout, createShortcutList(), getPageIndex()); + updateViewLayout(); + mLayout.setVisibility(View.VISIBLE); } } diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java index 4ab771be55f4..71726199aeb6 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java @@ -46,9 +46,6 @@ import android.hardware.display.DisplayManager; import android.media.AudioManager; import android.os.PowerManager; import android.os.UserManager; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.uiautomator_helpers.WaitUtils; import android.provider.Settings; import android.util.Log; @@ -63,7 +60,6 @@ import androidx.test.uiautomator.Configurator; import androidx.test.uiautomator.UiDevice; import com.android.compatibility.common.util.TestUtils; -import com.android.systemui.accessibility.accessibilitymenu.Flags; import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut.ShortcutId; import org.junit.After; @@ -71,7 +67,6 @@ import org.junit.AfterClass; import org.junit.Assume; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -82,9 +77,6 @@ import java.util.concurrent.atomic.AtomicInteger; @RunWith(AndroidJUnit4.class) public class AccessibilityMenuServiceTest { - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - private static final String TAG = "A11yMenuServiceTest"; private static final int CLICK_ID = AccessibilityNodeInfo.ACTION_CLICK; @@ -499,7 +491,6 @@ public class AccessibilityMenuServiceTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_HIDE_RESTRICTED_ACTIONS) public void testRestrictedActions_BrightnessNotAvailable() throws Throwable { try { setUserRestriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS, true); @@ -519,7 +510,6 @@ public class AccessibilityMenuServiceTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_HIDE_RESTRICTED_ACTIONS) public void testRestrictedActions_VolumeNotAvailable() throws Throwable { try { setUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME, true); diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 3541742e2bb7..20b83e10e821 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1215,6 +1215,13 @@ flag { } flag { + name: "glanceable_hub_v2" + namespace: "systemui" + description: "Gates the refreshed glanceable hub experience that also brings the glanceable hub to mobile phones" + bug: "375689917" +} + +flag { name: "dream_overlay_updated_font" namespace: "systemui" description: "Flag to enable updated font settings for dream overlay" @@ -1502,6 +1509,16 @@ flag { } flag { + name: "sim_pin_use_slot_id" + namespace: "systemui" + description: "Reorient SIM data processing around slotId instead of subId" + bug: "376173142" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "use_transitions_for_keyguard_occluded" namespace: "systemui" description: "Use Keyguard Transitions to set Notification Shade occlusion state" diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt index eee0cafd34fe..f1cbba7272b0 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt @@ -17,6 +17,7 @@ package com.android.systemui.animation import android.app.ActivityManager +import android.app.ActivityOptions import android.app.ActivityTaskManager import android.app.PendingIntent import android.app.TaskInfo @@ -434,8 +435,7 @@ constructor( private fun cleanUp() { cleanUpRunnable?.run() } - }, - initializeLazily = longLivedReturnAnimationsEnabled(), + } ) // mTypeSet and mModes match back signals only, and not home. This is on purpose, because @@ -478,8 +478,8 @@ constructor( /** Create a new animation [Runner] controlled by [controller]. */ @VisibleForTesting @JvmOverloads - fun createRunner(controller: Controller, initializeLazily: Boolean = false): Runner { - if (initializeLazily) assertLongLivedReturnAnimations() + fun createRunner(controller: Controller, longLived: Boolean = false): Runner { + if (longLived) assertLongLivedReturnAnimations() // Make sure we use the modified timings when animating a dialog into an app. val transitionAnimator = @@ -489,13 +489,7 @@ constructor( transitionAnimator } - return Runner( - controller, - callback!!, - transitionAnimator, - lifecycleListener, - initializeLazily, - ) + return Runner(controller, callback!!, transitionAnimator, lifecycleListener, longLived) } interface PendingIntentStarter { @@ -699,7 +693,7 @@ constructor( } val launchRemoteTransition = RemoteTransition( - OriginTransition(createRunner(controller, initializeLazily = true)), + OriginTransition(createRunner(controller, longLived = true)), "${cookie}_launchTransition", ) transitionRegister.register(launchFilter, launchRemoteTransition, includeTakeover = true) @@ -721,7 +715,7 @@ constructor( } val returnRemoteTransition = RemoteTransition( - OriginTransition(createRunner(returnController, initializeLazily = true)), + OriginTransition(createRunner(returnController, longLived = true)), "${cookie}_returnTransition", ) transitionRegister.register(returnFilter, returnRemoteTransition, includeTakeover = true) @@ -910,14 +904,22 @@ constructor( @VisibleForTesting inner class Runner( - private val controller: Controller, + /** + * This can hold a reference to a view, so it needs to be cleaned up and can't be held on to + * forever when ![longLived]. + */ + private var controller: Controller?, private val callback: Callback, /** The animator to use to animate the window transition. */ private val transitionAnimator: TransitionAnimator, /** Listener for animation lifecycle events. */ private val listener: Listener? = null, - /** Whether the internal [delegate] should be initialized lazily. */ - private val initializeLazily: Boolean = false, + /** + * Whether the internal should be kept around after execution for later usage. IMPORTANT: + * should always be false if this [Runner] is to be used directly with [ActivityOptions] + * (i.e. for ephemeral launches), or the controller will leak its view. + */ + private val longLived: Boolean = false, ) : IRemoteAnimationRunner.Stub() { // This is being passed across IPC boundaries and cycles (through PendingIntentRecords, // etc.) are possible. So we need to make sure we drop any references that might @@ -926,7 +928,7 @@ constructor( init { delegate = null - if (!initializeLazily) { + if (!longLived) { // Ephemeral launches bundle the runner with the launch request (instead of being // registered ahead of time for later use). This means that there could be a timeout // between creation and invocation, so the delegate needs to exist from the @@ -1004,16 +1006,17 @@ constructor( @AnyThread private fun maybeSetUp() { - if (!initializeLazily || delegate != null) return + if (!longLived || delegate != null) return createDelegate() } @AnyThread private fun createDelegate() { + if (controller == null) return delegate = AnimationDelegate( mainExecutor, - controller, + controller!!, callback, DelegatingAnimationCompletionListener(listener, this::dispose), transitionAnimator, @@ -1025,7 +1028,12 @@ constructor( fun dispose() { // Drop references to animation controller once we're done with the animation // to avoid leaking. - mainExecutor.execute { delegate = null } + mainExecutor.execute { + delegate = null + // When long lived, the same Runner can be used more than once. In this case we need + // to keep the controller around so we can rebuild the delegate on demand. + if (!longLived) controller = null + } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index 87e9c427d695..4705d8dd86c4 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -44,6 +44,7 @@ import com.android.compose.animation.scene.SceneTransitionLayout import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.observableTransitionState import com.android.compose.animation.scene.transitions +import com.android.systemui.Flags.communalHubOnMobile import com.android.systemui.communal.shared.model.CommunalBackgroundType import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalTransitionKeys @@ -186,15 +187,19 @@ fun CommunalContainer( ) { scene( CommunalScenes.Blank, - userActions = mapOf(Swipe.Start(fromSource = Edge.End) to CommunalScenes.Communal), + userActions = + if (communalHubOnMobile()) emptyMap() + else mapOf(Swipe.Start(fromSource = Edge.End) to CommunalScenes.Communal), ) { // This scene shows nothing only allowing for transitions to the communal scene. Box(modifier = Modifier.fillMaxSize()) } - val userActions = mapOf(Swipe.End to CommunalScenes.Blank) - - scene(CommunalScenes.Communal, userActions = userActions) { + scene( + CommunalScenes.Communal, + userActions = + if (communalHubOnMobile()) emptyMap() else mapOf(Swipe.End to CommunalScenes.Blank), + ) { CommunalScene( backgroundType = backgroundType, colors = colors, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index 5b1203fa17c9..787edfb9168c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -141,6 +141,7 @@ import androidx.compose.ui.semantics.CustomAccessibilityAction import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.customActions +import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.onClick import androidx.compose.ui.semantics.paneTitle import androidx.compose.ui.semantics.semantics @@ -902,11 +903,17 @@ private fun EmptyStateCta(contentPadding: PaddingValues, viewModel: BaseCommunal Arrangement.spacedBy(Dimensions.Spacing, Alignment.CenterVertically), horizontalAlignment = Alignment.CenterHorizontally, ) { + val titleForEmptyStateCTA = stringResource(R.string.title_for_empty_state_cta) Text( - text = stringResource(R.string.title_for_empty_state_cta), + text = titleForEmptyStateCTA, style = MaterialTheme.typography.displaySmall, textAlign = TextAlign.Center, color = colors.secondary, + modifier = + Modifier.focusable().semantics(mergeDescendants = true) { + contentDescription = titleForEmptyStateCTA + heading() + }, ) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { Button( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt index ef5e90bd7aad..7a500805809d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt @@ -94,7 +94,7 @@ internal constructor( private val scope: CoroutineScope, private val updateDragPositionForRemove: (draggingBoundingBox: IntRect) -> Boolean, ) { - var draggingItemKey by mutableStateOf<Any?>(null) + var draggingItemKey by mutableStateOf<String?>(null) private set var isDraggingToRemove by mutableStateOf(false) @@ -138,7 +138,7 @@ internal constructor( // before content padding from the initial pointer position .firstItemAtOffset(normalizedOffset - contentOffset) ?.apply { - draggingItemKey = key + draggingItemKey = key as String draggingItemInitialOffset = this.offset.toOffset() return true } @@ -284,7 +284,9 @@ fun Modifier.dragContainer( contentOffset, ) ) { - viewModel.onReorderWidgetStart() + // draggingItemKey is guaranteed to be non-null here because it is set in + // onDragStart() + viewModel.onReorderWidgetStart(dragDropState.draggingItemKey!!) } }, onDragEnd = { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt index 2a2c2fc4934e..105e8dadfafb 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt @@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -70,10 +69,7 @@ constructor( override val id: String = "default" @Composable - override fun SceneScope.Content( - viewModel: LockscreenContentViewModel, - modifier: Modifier, - ) { + override fun SceneScope.Content(viewModel: LockscreenContentViewModel, modifier: Modifier) { val isUdfpsVisible = viewModel.isUdfpsVisible val isShadeLayoutWide by viewModel.isShadeLayoutWide.collectAsStateWithLifecycle() val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle() @@ -85,22 +81,18 @@ constructor( with(notificationSection) { HeadsUpNotifications() } } - LockscreenLongPress( - viewModel = viewModel.touchHandling, - modifier = modifier, - ) { onSettingsMenuPlaced -> + LockscreenLongPress(viewModel = viewModel.touchHandling, modifier = modifier) { + onSettingsMenuPlaced -> Layout( content = { // Constrained to above the lock icon. - Column( - modifier = Modifier.fillMaxSize(), - ) { + Column(modifier = Modifier.fillMaxSize()) { with(statusBarSection) { StatusBar( modifier = Modifier.fillMaxWidth() .padding( - horizontal = { unfoldTranslations.start.roundToInt() }, + horizontal = { unfoldTranslations.start.roundToInt() } ) ) } @@ -109,13 +101,14 @@ constructor( with(topAreaSection) { DefaultClockLayout( smartSpacePaddingTop = viewModel::getSmartSpacePaddingTop, + isShadeLayoutWide = isShadeLayoutWide, modifier = Modifier.thenIf(isShadeLayoutWide) { Modifier.fillMaxWidth(0.5f) } .graphicsLayer { translationX = unfoldTranslations.start - } + }, ) } if (isShadeLayoutWide && !isBypassEnabled) { @@ -127,7 +120,7 @@ constructor( modifier = Modifier.fillMaxWidth(0.5f) .fillMaxHeight() - .align(alignment = Alignment.TopEnd) + .align(alignment = Alignment.TopEnd), ) } } @@ -142,7 +135,7 @@ constructor( AodNotificationIcons( modifier = Modifier.align(alignment = Alignment.TopStart) - .padding(start = aodIconPadding), + .padding(start = aodIconPadding) ) Notifications( areNotificationsVisible = areNotificationsVisible, @@ -152,7 +145,7 @@ constructor( } } else { AodNotificationIcons( - modifier = Modifier.padding(start = aodIconPadding), + modifier = Modifier.padding(start = aodIconPadding) ) } } @@ -205,11 +198,7 @@ constructor( val endShortcutMeasurable = measurables[4] val settingsMenuMeasurable = measurables[5] - val noMinConstraints = - constraints.copy( - minWidth = 0, - minHeight = 0, - ) + val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0) val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints) val lockIconBounds = IntRect( @@ -235,14 +224,8 @@ constructor( val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints) layout(constraints.maxWidth, constraints.maxHeight) { - aboveLockIconPlaceable.place( - x = 0, - y = 0, - ) - lockIconPlaceable.place( - x = lockIconBounds.left, - y = lockIconBounds.top, - ) + aboveLockIconPlaceable.place(x = 0, y = 0) + lockIconPlaceable.place(x = lockIconBounds.left, y = lockIconBounds.top) belowLockIconPlaceable.place( x = 0, y = constraints.maxHeight - belowLockIconPlaceable.height, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt index 3ca2b9c1d86c..4a9f44b74099 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt @@ -17,9 +17,11 @@ package com.android.systemui.keyguard.ui.composable.section import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.dimensionResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.SceneScope import com.android.systemui.keyguard.ui.viewmodel.KeyguardMediaViewModel @@ -27,6 +29,7 @@ import com.android.systemui.media.controls.ui.composable.MediaCarousel import com.android.systemui.media.controls.ui.controller.MediaCarouselController import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule +import com.android.systemui.res.R import javax.inject.Inject import javax.inject.Named @@ -39,13 +42,22 @@ constructor( ) { @Composable - fun SceneScope.KeyguardMediaCarousel() { + fun SceneScope.KeyguardMediaCarousel( + isShadeLayoutWide: Boolean, + modifier: Modifier = Modifier, + ) { val isMediaVisible by keyguardMediaViewModel.isMediaVisible.collectAsStateWithLifecycle() - + val horizontalPadding = + if (isShadeLayoutWide) { + dimensionResource(id = R.dimen.notification_side_paddings) + } else { + dimensionResource(id = R.dimen.notification_side_paddings) + + dimensionResource(id = R.dimen.notification_panel_margin_horizontal) + } MediaCarousel( isVisible = isMediaVisible, mediaHost = mediaHost, - modifier = Modifier.fillMaxWidth(), + modifier = modifier.fillMaxWidth().padding(horizontal = horizontalPadding), carouselController = mediaCarouselController, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt index afa92f2533ce..db33e7c628d7 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt @@ -63,6 +63,7 @@ constructor( @Composable fun SceneScope.DefaultClockLayout( smartSpacePaddingTop: (Resources) -> Int, + isShadeLayoutWide: Boolean, modifier: Modifier = Modifier, ) { val currentClockLayout by clockViewModel.currentClockLayout.collectAsStateWithLifecycle() @@ -128,7 +129,7 @@ constructor( ) } } - with(mediaCarouselSection) { KeyguardMediaCarousel() } + with(mediaCarouselSection) { KeyguardMediaCarousel(isShadeLayoutWide) } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt index e725ce589f3e..58336c2e9d41 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt @@ -79,8 +79,8 @@ object QuickSettings { val MediaLandscapeTopOffset = ValueKey("MediaLandscapeTopOffset") object MediaOffset { - // Brightness + padding - val InQS = 92.dp + // Brightness + val InQS = 60.dp val Default = 0.dp @Composable diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt index 0e7165c7f3ec..52adaf2a5b5d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt @@ -74,6 +74,7 @@ import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.animateSceneDpAsState import com.android.compose.animation.scene.animateSceneFloatAsState import com.android.compose.animation.scene.content.state.TransitionState +import com.android.compose.modifiers.padding import com.android.compose.modifiers.thenIf import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.systemui.battery.BatteryMeterViewController @@ -379,7 +380,11 @@ private fun SceneScope.QuickSettingsScene( mediaHost = mediaHost, modifier = Modifier.fillMaxWidth() - .layoutId(QSMediaMeasurePolicy.LayoutId.Media), + .layoutId(QSMediaMeasurePolicy.LayoutId.Media) + .padding( + horizontal = + dimensionResource(id = R.dimen.qs_horizontal_margin) + ), carouselController = mediaCarouselController, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt index 26c827a5417c..3fce89054b20 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt @@ -44,8 +44,11 @@ import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace +import com.android.systemui.plugins.qs.TileDetailsViewModel import com.android.systemui.qs.composefragment.ui.GridAnchor +import com.android.systemui.qs.flags.QsDetailedView import com.android.systemui.qs.panels.ui.compose.EditMode +import com.android.systemui.qs.panels.ui.compose.TileDetails import com.android.systemui.qs.panels.ui.compose.TileGrid import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayActionsViewModel @@ -116,24 +119,50 @@ constructor( } } +// A sealed interface to represent the possible states of the `ShadeBody` +sealed interface ShadeBodyState { + data object Editing : ShadeBodyState + data object TileDetails : ShadeBodyState + data object Default : ShadeBodyState +} + +// Function to map the current state of the `ShadeBody` +fun checkQsState(isEditing: Boolean, tileDetails: TileDetailsViewModel?): ShadeBodyState { + if (isEditing) { + return ShadeBodyState.Editing + } else if (tileDetails != null && QsDetailedView.isEnabled) { + return ShadeBodyState.TileDetails + } + return ShadeBodyState.Default +} + @Composable fun SceneScope.ShadeBody(viewModel: QuickSettingsContainerViewModel) { val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle() + val tileDetails = viewModel.detailsViewModel.activeTileDetails AnimatedContent( - targetState = isEditing, + targetState = checkQsState(isEditing, tileDetails), transitionSpec = { fadeIn(tween(500)) togetherWith fadeOut(tween(500)) }, - ) { editing -> - if (editing) { - EditMode( - viewModel = viewModel.editModeViewModel, - modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding), - ) - } else { - QuickSettingsLayout( - viewModel = viewModel, - modifier = Modifier.sysuiResTag("quick_settings_panel"), - ) + ) { state -> + when (state) { + ShadeBodyState.Editing -> { + EditMode( + viewModel = viewModel.editModeViewModel, + modifier = Modifier + .fillMaxWidth() + .padding(QuickSettingsShade.Dimensions.Padding), + ) + } + ShadeBodyState.TileDetails -> { + TileDetails(viewModel.detailsViewModel) + } + else -> { + QuickSettingsLayout( + viewModel = viewModel, + modifier = Modifier.sysuiResTag("quick_settings_panel"), + ) + } } } } @@ -168,7 +197,6 @@ fun SceneScope.QuickSettingsLayout( modifier = Modifier.fillMaxWidth() .heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight), - viewModel.editModeViewModel::startEditing, ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt index ae5dd8abb82e..5fb9416cf35b 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt @@ -19,12 +19,17 @@ package com.android.systemui.scene.ui.composable import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.animateContentDpAsState import com.android.compose.animation.scene.animateContentFloatAsState +import com.android.compose.animation.scene.content.state.TransitionState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.rememberViewModel @@ -63,9 +68,25 @@ constructor( } @Composable - override fun SceneScope.Content( - modifier: Modifier, - ) { + override fun SceneScope.Content(modifier: Modifier) { + + val isIdle by remember { + derivedStateOf { layoutState.transitionState is TransitionState.Idle } + } + + LaunchedEffect(isIdle) { + // Wait for being Idle on this Scene, otherwise LaunchedEffect would fire too soon, + // and another transition could override the NSSL stack bounds. + if (isIdle) { + // Reset the stack bounds to avoid caching these values from the previous Scenes, + // and not to confuse the StackScrollAlgorithm when it displays a HUN over GONE. + notificationStackScrolLView.get().apply { + setStackTop(0f) + setStackCutoff(0f) + } + } + } + animateContentFloatAsState( value = QuickSettings.SharedValues.SquishinessValues.GoneSceneStarting, key = QuickSettings.SharedValues.TilesSquishiness, @@ -75,9 +96,7 @@ constructor( SnoozeableHeadsUpNotificationSpace( stackScrollView = notificationStackScrolLView.get(), viewModel = - rememberViewModel("GoneScene") { - notificationsPlaceholderViewModelFactory.create() - }, + rememberViewModel("GoneScene") { notificationsPlaceholderViewModelFactory.create() }, ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt index 2d58c8cad2b1..a266e7eb44a1 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt @@ -49,7 +49,6 @@ import com.android.systemui.scene.shared.model.SceneDataSourceDelegator import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import javax.inject.Provider -import kotlinx.coroutines.flow.collectLatest /** * Renders a container of a collection of "scenes" that the user can switch between using certain @@ -117,7 +116,7 @@ fun SceneContainer( ) { "invalid ContentKey: $actionableContentKey" } - actionableContent.userActions.collectLatest { userActions -> + viewModel.filteredUserActions(actionableContent.userActions).collect { userActions -> userActionsByContentKey[actionableContentKey] = viewModel.resolveSceneFamilies(userActions) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt index 05a0119d68e4..bfcde7dab6d2 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt @@ -130,10 +130,6 @@ fun SceneScope.CollapsedShadeHeader( modifier: Modifier = Modifier, ) { val viewModel = rememberViewModel("CollapsedShadeHeader") { viewModelFactory.create() } - val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle() - if (isDisabled) { - return - } val cutoutWidth = LocalDisplayCutout.current.width() val cutoutHeight = LocalDisplayCutout.current.height() @@ -196,7 +192,7 @@ fun SceneScope.CollapsedShadeHeader( horizontalArrangement = Arrangement.End, modifier = Modifier.element(ShadeHeader.Elements.CollapsedContentEnd) - .padding(horizontal = horizontalPadding) + .padding(horizontal = horizontalPadding), ) { if (isLargeScreenLayout) { ShadeCarrierGroup( @@ -207,7 +203,7 @@ fun SceneScope.CollapsedShadeHeader( SystemIconContainer( viewModel = viewModel, isClickable = isLargeScreenLayout, - modifier = Modifier.align(Alignment.CenterVertically) + modifier = Modifier.align(Alignment.CenterVertically), ) { StatusIcons( viewModel = viewModel, @@ -217,7 +213,7 @@ fun SceneScope.CollapsedShadeHeader( modifier = Modifier.align(Alignment.CenterVertically) .padding(end = 6.dp) - .weight(1f, fill = false) + .weight(1f, fill = false), ) BatteryIcon( createBatteryMeterViewController = @@ -252,27 +248,15 @@ fun SceneScope.CollapsedShadeHeader( CutoutLocation.NONE, CutoutLocation.RIGHT -> { startPlaceable.placeRelative(x = 0, y = 0) - endPlaceable.placeRelative( - x = startPlaceable.width, - y = 0, - ) + endPlaceable.placeRelative(x = startPlaceable.width, y = 0) } CutoutLocation.CENTER -> { startPlaceable.placeRelative(x = 0, y = 0) - endPlaceable.placeRelative( - x = startPlaceable.width + cutoutWidthPx, - y = 0, - ) + endPlaceable.placeRelative(x = startPlaceable.width + cutoutWidthPx, y = 0) } CutoutLocation.LEFT -> { - startPlaceable.placeRelative( - x = cutoutWidthPx, - y = 0, - ) - endPlaceable.placeRelative( - x = startPlaceable.width + cutoutWidthPx, - y = 0, - ) + startPlaceable.placeRelative(x = cutoutWidthPx, y = 0) + endPlaceable.placeRelative(x = startPlaceable.width + cutoutWidthPx, y = 0) } } } @@ -288,10 +272,6 @@ fun SceneScope.ExpandedShadeHeader( modifier: Modifier = Modifier, ) { val viewModel = rememberViewModel("ExpandedShadeHeader") { viewModelFactory.create() } - val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle() - if (isDisabled) { - return - } val useExpandedFormat by remember { derivedStateOf { shouldUseExpandedFormat(layoutState.transitionState) } @@ -302,17 +282,14 @@ fun SceneScope.ExpandedShadeHeader( Box(modifier = modifier.sysuiResTag(ShadeHeader.TestTags.Root)) { if (isPrivacyChipVisible) { Box(modifier = Modifier.height(CollapsedHeight).fillMaxWidth()) { - PrivacyChip( - viewModel = viewModel, - modifier = Modifier.align(Alignment.CenterEnd), - ) + PrivacyChip(viewModel = viewModel, modifier = Modifier.align(Alignment.CenterEnd)) } } Column( verticalArrangement = Arrangement.Bottom, modifier = Modifier.fillMaxWidth() - .defaultMinSize(minHeight = ShadeHeader.Dimensions.ExpandedHeight) + .defaultMinSize(minHeight = ShadeHeader.Dimensions.ExpandedHeight), ) { Box(modifier = Modifier.fillMaxWidth()) { Box { @@ -362,11 +339,7 @@ fun SceneScope.ExpandedShadeHeader( } @Composable -private fun SceneScope.Clock( - scale: Float, - viewModel: ShadeHeaderViewModel, - modifier: Modifier, -) { +private fun SceneScope.Clock(scale: Float, viewModel: ShadeHeaderViewModel, modifier: Modifier) { val layoutDirection = LocalLayoutDirection.current Element(key = ShadeHeader.Elements.Clock, modifier = modifier) { @@ -391,10 +364,10 @@ private fun SceneScope.Clock( LayoutDirection.Ltr -> 0f LayoutDirection.Rtl -> 1f }, - 0.5f + 0.5f, ) } - .clickable { viewModel.onClockClicked() } + .clickable { viewModel.onClockClicked() }, ) } } @@ -447,10 +420,7 @@ private fun BatteryIcon( } @Composable -private fun ShadeCarrierGroup( - viewModel: ShadeHeaderViewModel, - modifier: Modifier = Modifier, -) { +private fun ShadeCarrierGroup(viewModel: ShadeHeaderViewModel, modifier: Modifier = Modifier) { Row(modifier = modifier) { val subIds by viewModel.mobileSubIds.collectAsStateWithLifecycle() @@ -465,11 +435,11 @@ private fun ShadeCarrierGroup( viewModel = (viewModel.mobileIconsViewModel.viewModelForSub( subId, - StatusBarLocation.SHADE_CARRIER_GROUP + StatusBarLocation.SHADE_CARRIER_GROUP, ) as ShadeCarrierGroupMobileIconViewModel), ) .also { it.setOnClickListener { viewModel.onShadeCarrierGroupClicked() } } - }, + } ) } } @@ -506,7 +476,7 @@ private fun SceneScope.StatusIcons( Utils.getColorAttrDefaultColor(themedContext, android.R.attr.textColorPrimary), Utils.getColorAttrDefaultColor( themedContext, - android.R.attr.textColorPrimaryInverse + android.R.attr.textColorPrimaryInverse, ), ) statusBarIconController.addIconGroup(iconManager) @@ -551,7 +521,7 @@ private fun SystemIconContainer( viewModel: ShadeHeaderViewModel, isClickable: Boolean, modifier: Modifier = Modifier, - content: @Composable RowScope.() -> Unit + content: @Composable RowScope.() -> Unit, ) { val interactionSource = remember { MutableInteractionSource() } val isHovered by interactionSource.collectIsHoveredAsState() @@ -578,10 +548,7 @@ private fun SystemIconContainer( } @Composable -private fun SceneScope.PrivacyChip( - viewModel: ShadeHeaderViewModel, - modifier: Modifier = Modifier, -) { +private fun SceneScope.PrivacyChip(viewModel: ShadeHeaderViewModel, modifier: Modifier = Modifier) { val privacyList by viewModel.privacyItems.collectAsStateWithLifecycle() AndroidView( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index bba3d69ea77f..22b6dbcf41ec 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -270,6 +270,7 @@ private fun SceneScope.SingleShade( ) val isEmptySpaceClickable by viewModel.isEmptySpaceClickable.collectAsStateWithLifecycle() val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle() + val isQsEnabled by viewModel.isQsEnabled.collectAsStateWithLifecycle() val shouldPunchHoleBehindScrim = layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade) || @@ -353,14 +354,26 @@ private fun SceneScope.SingleShade( ) } + val qqsLayoutPaddingBottom = + dimensionResource(id = R.dimen.qqs_layout_padding_bottom) ShadeMediaCarousel( isVisible = isMediaVisible, isInRow = mediaInRow, mediaHost = mediaHost, mediaOffsetProvider = mediaOffsetProvider, carouselController = mediaCarouselController, - modifier = Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Media), + modifier = + Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Media) + .padding( + horizontal = + shadeHorizontalPadding + + dimensionResource(id = R.dimen.qs_horizontal_margin) + ) + .thenIf(!mediaInRow) { + Modifier.padding(bottom = qqsLayoutPaddingBottom) + }, usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia, + isQsEnabled = isQsEnabled, isInSplitShade = false, ) @@ -416,6 +429,7 @@ private fun SceneScope.SplitShade( val screenCornerRadius = LocalScreenCornerRadius.current val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle() + val isQsEnabled by viewModel.isQsEnabled.collectAsStateWithLifecycle() val isCustomizerShowing by viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle() val customizingAnimationDuration by @@ -562,11 +576,16 @@ private fun SceneScope.SplitShade( mediaOffsetProvider = mediaOffsetProvider, modifier = Modifier.thenIf( - MediaContentPicker.shouldElevateMedia(layoutState) - ) { - Modifier.zIndex(1f) - }, + MediaContentPicker.shouldElevateMedia(layoutState) + ) { + Modifier.zIndex(1f) + } + .padding( + horizontal = + dimensionResource(id = R.dimen.qs_horizontal_margin) + ), carouselController = mediaCarouselController, + isQsEnabled = isQsEnabled, isInSplitShade = true, ) } @@ -620,10 +639,14 @@ private fun SceneScope.ShadeMediaCarousel( mediaHost: MediaHost, carouselController: MediaCarouselController, mediaOffsetProvider: ShadeMediaOffsetProvider, + isInSplitShade: Boolean, + isQsEnabled: Boolean, modifier: Modifier = Modifier, usingCollapsedLandscapeMedia: Boolean = false, - isInSplitShade: Boolean, ) { + if (!isQsEnabled) { + return + } MediaCarousel( modifier = modifier.fillMaxWidth(), isVisible = isVisible, diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml index 21b4c7165226..2bb5541f4b0a 100644 --- a/packages/SystemUI/customization/res/values/dimens.xml +++ b/packages/SystemUI/customization/res/values/dimens.xml @@ -33,6 +33,7 @@ <dimen name="small_clock_height">114dp</dimen> <dimen name="small_clock_padding_top">28dp</dimen> <dimen name="clock_padding_start">28dp</dimen> + <dimen name="weather_date_icon_padding">28dp</dimen> <!-- When large clock is showing, offset the smartspace by this amount --> <dimen name="keyguard_smartspace_top_offset">12dp</dimen> diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index f41d5c8eeb23..8552e48a2024 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -63,8 +63,6 @@ import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; import android.os.SystemClock; -import android.platform.test.annotations.DisableFlags; -import android.platform.test.annotations.EnableFlags; import android.provider.Settings; import android.testing.TestableLooper; import android.testing.TestableResources; @@ -90,7 +88,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; -import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.AnimatorTestRule; import com.android.systemui.kosmos.KosmosJavaAdapter; @@ -612,46 +609,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { .isEqualTo(expectedRatio); } - @DisableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) - @Test - public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierWindow() { - int newSmallestScreenWidthDp = - mContext.getResources().getConfiguration().smallestScreenWidthDp * 2; - int windowFrameSize = mResources.getDimensionPixelSize( - com.android.internal.R.dimen.accessibility_window_magnifier_min_size); - Size preferredWindowSize = new Size(windowFrameSize, windowFrameSize); - mSharedPreferences - .edit() - .putString(String.valueOf(newSmallestScreenWidthDp), - preferredWindowSize.toString()) - .commit(); - mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN, - Float.NaN); - }); - - // Screen density and size change - mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp; - final Rect testWindowBounds = new Rect( - mWindowManager.getCurrentWindowMetrics().getBounds()); - testWindowBounds.set(testWindowBounds.left, testWindowBounds.top, - testWindowBounds.right + 100, testWindowBounds.bottom + 100); - mWindowManager.setWindowBounds(testWindowBounds); - mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); - }); - - // wait for rect update - waitForIdleSync(); - ViewGroup.LayoutParams params = mSurfaceControlViewHost.getView().getLayoutParams(); - final int mirrorSurfaceMargin = mResources.getDimensionPixelSize( - R.dimen.magnification_mirror_surface_margin); - // The width and height of the view include the magnification frame and the margins. - assertThat(params.width).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin); - assertThat(params.height).isEqualTo(windowFrameSize + 2 * mirrorSurfaceMargin); - } - - @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) @Test public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierIndexAndWindow() { int newSmallestScreenWidthDp = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java index 944066fa4954..d47ec8cfe4e5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java @@ -25,15 +25,12 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import android.platform.test.annotations.DisableFlags; -import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper; import android.util.Size; 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.util.FakeSharedPreferences; @@ -59,18 +56,6 @@ public class WindowMagnificationFrameSizePrefsTest extends SysuiTestCase { mWindowMagnificationFrameSizePrefs = new WindowMagnificationFrameSizePrefs(mContext); } - @DisableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) - @Test - public void saveSizeForCurrentDensity_getExpectedSize() { - Size testSize = new Size(500, 500); - mWindowMagnificationFrameSizePrefs - .saveIndexAndSizeForCurrentDensity(MagnificationSize.CUSTOM, testSize); - - assertThat(mWindowMagnificationFrameSizePrefs.getSizeForCurrentDensity()) - .isEqualTo(testSize); - } - - @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) @Test public void saveSizeForCurrentDensity_validPreference_getExpectedSize() { int testIndex = MagnificationSize.MEDIUM; @@ -81,7 +66,6 @@ public class WindowMagnificationFrameSizePrefsTest extends SysuiTestCase { .isEqualTo(testSize); } - @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) @Test public void saveSizeForCurrentDensity_validPreference_getExpectedIndex() { int testIndex = MagnificationSize.MEDIUM; @@ -92,7 +76,6 @@ public class WindowMagnificationFrameSizePrefsTest extends SysuiTestCase { .isEqualTo(testIndex); } - @EnableFlags(Flags.FLAG_SAVE_AND_RESTORE_MAGNIFICATION_SETTINGS_BUTTONS) @Test public void saveSizeForCurrentDensity_invalidPreference_getDefaultIndex() { mSharedPreferences diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt index cdda9ccc9b9e..41cc6ee182cf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt @@ -48,8 +48,8 @@ import com.android.systemui.shade.domain.interactor.ShadeBackActionInteractor import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt index 25f956574274..a11dace0505c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.bluetooth.qsdialog import android.bluetooth.BluetoothLeBroadcast +import android.bluetooth.BluetoothLeBroadcastMetadata import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -50,6 +51,7 @@ class AudioSharingInteractorTest : SysuiTestCase() { @get:Rule val mockito: MockitoRule = MockitoJUnit.rule() private val kosmos = testKosmos() @Mock private lateinit var localBluetoothLeBroadcast: LocalBluetoothLeBroadcast + @Mock private lateinit var bluetoothLeBroadcastMetadata: BluetoothLeBroadcastMetadata @Captor private lateinit var callbackCaptor: ArgumentCaptor<BluetoothLeBroadcast.Callback> private lateinit var underTest: AudioSharingInteractor @@ -202,7 +204,7 @@ class AudioSharingInteractorTest : SysuiTestCase() { verify(localBluetoothLeBroadcast) .registerServiceCallBack(any(), callbackCaptor.capture()) runCurrent() - callbackCaptor.value.onPlaybackStarted(0, 0) + callbackCaptor.value.onBroadcastMetadataChanged(0, bluetoothLeBroadcastMetadata) runCurrent() assertThat(bluetoothTileDialogAudioSharingRepository.sourceAdded).isTrue() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt index 0f148f89e3c5..2d093bf1630b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt @@ -25,6 +25,7 @@ import com.android.systemui.brightness.domain.interactor.brightnessPolicyEnforce import com.android.systemui.brightness.domain.interactor.screenBrightnessInteractor import com.android.systemui.brightness.shared.model.GammaBrightness import com.android.systemui.brightness.shared.model.LinearBrightness +import com.android.systemui.classifier.domain.interactor.falsingInteractor import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text @@ -61,6 +62,7 @@ class BrightnessSliderViewModelTest : SysuiTestCase() { brightnessPolicyEnforcementInteractor, sliderHapticsViewModelFactory, brightnessMirrorShowingInteractor, + falsingInteractor, supportsMirroring = true, brightnessWarningToast, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt index 5bbd3ffc625a..18cc8bf5f0d3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt @@ -172,16 +172,16 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { } @Test - fun selectedKey_onReorderWidgets_isCleared() = + fun selectedKey_onReorderWidgets_isSet() = testScope.runTest { val selectedKey by collectLastValue(underTest.selectedKey) + underTest.setSelectedKey(null) + assertThat(selectedKey).isNull() + val key = CommunalContentModel.KEY.widget(123) - underTest.setSelectedKey(key) + underTest.onReorderWidgetStart(key) assertThat(selectedKey).isEqualTo(key) - - underTest.onReorderWidgetStart() - assertThat(selectedKey).isNull() } @Test @@ -234,7 +234,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { @Test fun reorderWidget_uiEventLogging_start() { - underTest.onReorderWidgetStart() + underTest.onReorderWidgetStart(CommunalContentModel.KEY.widget(123)) verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt index d90d58b8d25c..1bb5c9afdc33 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt @@ -43,7 +43,6 @@ import com.android.systemui.keyguard.data.repository.fakeTrustRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope -import com.android.systemui.scene.domain.interactor.sceneBackInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.domain.startable.sceneContainerStartable import com.android.systemui.scene.shared.model.Scenes @@ -72,7 +71,6 @@ class DeviceEntryInteractorTest : SysuiTestCase() { private val trustRepository by lazy { kosmos.fakeTrustRepository } private val sceneInteractor by lazy { kosmos.sceneInteractor } private val authenticationInteractor by lazy { kosmos.authenticationInteractor } - private val sceneBackInteractor by lazy { kosmos.sceneBackInteractor } private val sceneContainerStartable by lazy { kosmos.sceneContainerStartable } private val sysuiStatusBarStateController by lazy { kosmos.sysuiStatusBarStateController } private lateinit var underTest: DeviceEntryInteractor @@ -437,7 +435,9 @@ class DeviceEntryInteractorTest : SysuiTestCase() { fun isDeviceEntered_unlockedWhileOnShade_emitsTrue() = testScope.runTest { val isDeviceEntered by collectLastValue(underTest.isDeviceEntered) + val isDeviceEnteredDirectly by collectLastValue(underTest.isDeviceEnteredDirectly) assertThat(isDeviceEntered).isFalse() + assertThat(isDeviceEnteredDirectly).isFalse() val currentScene by collectLastValue(sceneInteractor.currentScene) assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -445,19 +445,20 @@ class DeviceEntryInteractorTest : SysuiTestCase() { switchToScene(Scenes.Shade) assertThat(currentScene).isEqualTo(Scenes.Shade) // Simulating a "leave it open when the keyguard is hidden" which means the bouncer will - // be - // shown and successful authentication should take the user back to where they are, the - // shade scene. + // be shown and successful authentication should take the user back to where they are, + // the shade scene. sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true) switchToScene(Scenes.Bouncer) assertThat(currentScene).isEqualTo(Scenes.Bouncer) assertThat(isDeviceEntered).isFalse() + assertThat(isDeviceEnteredDirectly).isFalse() // Authenticate with PIN to unlock and dismiss the lockscreen: authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) runCurrent() assertThat(isDeviceEntered).isTrue() + assertThat(isDeviceEnteredDirectly).isFalse() } private fun TestScope.switchToScene(sceneKey: SceneKey) { 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 bbfc9607fed9..a8048793be06 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt @@ -36,7 +36,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.test.filters.SmallTest -import com.android.app.viewcapture.ViewCapture import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.app.viewcapture.ViewCaptureFactory import com.android.compose.animation.scene.ObservableTransitionState @@ -44,15 +43,15 @@ import com.android.internal.logging.UiEventLogger import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.Flags.FLAG_COMMUNAL_HUB +import com.android.systemui.Flags.FLAG_COMMUNAL_HUB_ON_MOBILE import com.android.systemui.Flags.FLAG_SCENE_CONTAINER import com.android.systemui.SysuiTestCase +import com.android.systemui.ambient.touch.TouchHandler import com.android.systemui.ambient.touch.TouchMonitor import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent import com.android.systemui.ambient.touch.scrim.ScrimController import com.android.systemui.ambient.touch.scrim.ScrimManager -import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository -import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.communalInteractor @@ -64,6 +63,7 @@ import com.android.systemui.complication.ComplicationLayoutEngine import com.android.systemui.complication.dagger.ComplicationComponent import com.android.systemui.dreams.complication.HideComplicationTouchHandler import com.android.systemui.dreams.dagger.DreamOverlayComponent +import com.android.systemui.dreams.touch.CommunalTouchHandler import com.android.systemui.flags.andSceneContainer import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.gesture.domain.gestureInteractor @@ -87,21 +87,17 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor -import org.mockito.Captor -import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.isNull import org.mockito.Mockito.never import org.mockito.Mockito.verify -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.verifyNoMoreInteractions import org.mockito.kotlin.whenever import platform.test.runner.parameterized.ParameterizedAndroidJunit4 @@ -117,68 +113,50 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope - @Mock lateinit var mLifecycleOwner: DreamOverlayLifecycleOwner - - private lateinit var lifecycleRegistry: FakeLifecycleRegistry - - lateinit var mCommunalInteractor: CommunalInteractor - - private lateinit var mWindowParams: WindowManager.LayoutParams - - @Mock lateinit var mDreamOverlayCallback: IDreamOverlayCallback - - @Mock lateinit var mWindowManager: WindowManagerImpl - - @Mock lateinit var mComplicationComponentFactory: ComplicationComponent.Factory - - @Mock lateinit var mComplicationHostViewController: ComplicationHostViewController - - @Mock lateinit var mComplicationVisibilityController: ComplicationLayoutEngine - - @Mock - lateinit var mDreamComplicationComponentFactory: - com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory - - @Mock lateinit var mHideComplicationTouchHandler: HideComplicationTouchHandler - - @Mock lateinit var mDreamOverlayComponentFactory: DreamOverlayComponent.Factory - - @Mock lateinit var mAmbientTouchComponentFactory: AmbientTouchComponent.Factory - - @Mock lateinit var mDreamOverlayContainerView: DreamOverlayContainerView - - @Mock lateinit var mDreamOverlayContainerViewController: DreamOverlayContainerViewController - - @Mock lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor - - @Mock lateinit var mTouchMonitor: TouchMonitor - - @Mock lateinit var mStateController: DreamOverlayStateController - - @Mock lateinit var mDreamOverlayContainerViewParent: ViewGroup - - @Mock lateinit var mTouchInsetManager: TouchInsetManager - - @Mock lateinit var mUiEventLogger: UiEventLogger - - @Mock lateinit var mScrimManager: ScrimManager - - @Mock lateinit var mScrimController: ScrimController - - @Mock lateinit var mSystemDialogsCloser: SystemDialogsCloser - - @Mock lateinit var mDreamOverlayCallbackController: DreamOverlayCallbackController - - @Mock lateinit var mLazyViewCapture: Lazy<ViewCapture> + private val mLifecycleOwner = mock<DreamOverlayLifecycleOwner>() + private val mDreamOverlayCallback = mock<IDreamOverlayCallback>() + private val mWindowManager = mock<WindowManagerImpl>() + private val mComplicationComponentFactory = mock<ComplicationComponent.Factory>() + private val mComplicationHostViewController = mock<ComplicationHostViewController>() + private val mComplicationVisibilityController = mock<ComplicationLayoutEngine>() + private val mDreamComplicationComponentFactory = + mock<com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory>() + private val mHideComplicationTouchHandler = mock<HideComplicationTouchHandler>() + private val mDreamOverlayComponentFactory = mock<DreamOverlayComponent.Factory>() + private val mCommunalTouchHandler = mock<CommunalTouchHandler>() + private val mAmbientTouchComponentFactory = mock<AmbientTouchComponent.Factory>() + private val mDreamOverlayContainerView = mock<DreamOverlayContainerView>() + private val mDreamOverlayContainerViewController = + mock<DreamOverlayContainerViewController> { + on { containerView }.thenReturn(mDreamOverlayContainerView) + } + private val mKeyguardUpdateMonitor = mock<KeyguardUpdateMonitor>() + private val mTouchMonitor = mock<TouchMonitor>() + private val mStateController = mock<DreamOverlayStateController>() + private val mDreamOverlayContainerViewParent = mock<ViewGroup>() + private val mTouchInsetManager = mock<TouchInsetManager>() + private val mUiEventLogger = mock<UiEventLogger>() + private val mScrimController = mock<ScrimController>() + private val mScrimManager = + mock<ScrimManager> { on { currentController }.thenReturn(mScrimController) } + private val mSystemDialogsCloser = mock<SystemDialogsCloser>() + private val mDreamOverlayCallbackController = mock<DreamOverlayCallbackController>() + private val mLazyViewCapture = lazy { viewCaptureSpy } + + private val mViewCaptor = argumentCaptor<View>() + private val mTouchHandlersCaptor = argumentCaptor<Set<TouchHandler>>() + + private val mWindowParams = WindowManager.LayoutParams() + private val lifecycleRegistry = FakeLifecycleRegistry(mLifecycleOwner) + private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository + private val communalRepository = kosmos.fakeCommunalSceneRepository + private var viewCaptureSpy = spy(ViewCaptureFactory.getInstance(context)) + private val gestureInteractor = spy(kosmos.gestureInteractor) + private lateinit var mCommunalInteractor: CommunalInteractor private lateinit var mViewCaptureAwareWindowManager: ViewCaptureAwareWindowManager - private lateinit var bouncerRepository: FakeKeyguardBouncerRepository - private lateinit var communalRepository: FakeCommunalSceneRepository - private var viewCaptureSpy = spy(ViewCaptureFactory.getInstance(context)) - private lateinit var gestureInteractor: GestureInteractor private lateinit var environmentComponents: EnvironmentComponents - @Captor var mViewCaptor: ArgumentCaptor<View>? = null private lateinit var mService: DreamOverlayService private class EnvironmentComponents( @@ -234,6 +212,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { mock<com.android.systemui.dreams.complication.dagger.ComplicationComponent>() whenever(dreamComplicationComponent.getHideComplicationTouchHandler()) .thenReturn(mHideComplicationTouchHandler) + whenever(dreamOverlayComponent.communalTouchHandler).thenReturn(mCommunalTouchHandler) whenever(dreamComplicationComponentFactory.create(any(), any())) .thenReturn(dreamComplicationComponent) @@ -259,13 +238,6 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { @Before fun setup() { - MockitoAnnotations.initMocks(this) - - lifecycleRegistry = FakeLifecycleRegistry(mLifecycleOwner) - bouncerRepository = kosmos.fakeKeyguardBouncerRepository - communalRepository = kosmos.fakeCommunalSceneRepository - gestureInteractor = spy(kosmos.gestureInteractor) - environmentComponents = setupComponentFactories( mDreamComplicationComponentFactory, @@ -273,12 +245,6 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { mComplicationComponentFactory, mAmbientTouchComponentFactory, ) - - whenever(mDreamOverlayContainerViewController.containerView) - .thenReturn(mDreamOverlayContainerView) - whenever(mScrimManager.getCurrentController()).thenReturn(mScrimController) - whenever(mLazyViewCapture.value).thenReturn(viewCaptureSpy) - mWindowParams = WindowManager.LayoutParams() mViewCaptureAwareWindowManager = ViewCaptureAwareWindowManager( mWindowManager, @@ -381,10 +347,10 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { verify(mStateController).setOverlayActive(false) verify(mStateController).setLowLightActive(false) verify(mStateController).setEntryAnimationsFinished(false) - verify(mStateController, Mockito.never()).setOverlayActive(true) - verify(mUiEventLogger, Mockito.never()) + verify(mStateController, never()).setOverlayActive(true) + verify(mUiEventLogger, never()) .log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START) - verify(mDreamOverlayCallbackController, Mockito.never()).onStartDream() + verify(mDreamOverlayCallbackController, never()).onStartDream() } @Test @@ -528,14 +494,14 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { mMainExecutor.runAllReady() // Verify view added. - verify(mWindowManager).addView(mViewCaptor!!.capture(), any()) + verify(mWindowManager).addView(mViewCaptor.capture(), any()) // Service destroyed. mService.onEndDream() mMainExecutor.runAllReady() // Verify view removed. - verify(mWindowManager).removeView(mViewCaptor!!.value) + verify(mWindowManager).removeView(mViewCaptor.firstValue) // Verify state correctly set. verify(mStateController).setOverlayActive(false) @@ -567,8 +533,8 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { // The overlay starts then finishes. val inOrder = Mockito.inOrder(mWindowManager) - inOrder.verify(mWindowManager).addView(mViewCaptor!!.capture(), any()) - inOrder.verify(mWindowManager).removeView(mViewCaptor!!.value) + inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any()) + inOrder.verify(mWindowManager).removeView(mViewCaptor.firstValue) } @Test @@ -596,8 +562,8 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { // The overlay starts then finishes. val inOrder = Mockito.inOrder(mWindowManager) - inOrder.verify(mWindowManager).addView(mViewCaptor!!.capture(), any()) - inOrder.verify(mWindowManager).removeView(mViewCaptor!!.value) + inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any()) + inOrder.verify(mWindowManager).removeView(mViewCaptor.firstValue) } @Test @@ -615,14 +581,14 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { mMainExecutor.runAllReady() // Verify view added. - verify(mWindowManager).addView(mViewCaptor!!.capture(), any()) + verify(mWindowManager).addView(mViewCaptor.capture(), any()) // Service destroyed. mService.onDestroy() mMainExecutor.runAllReady() // Verify view removed. - verify(mWindowManager).removeView(mViewCaptor!!.value) + verify(mWindowManager).removeView(mViewCaptor.firstValue) // Verify state correctly set. verify(mKeyguardUpdateMonitor).removeCallback(any()) @@ -639,7 +605,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { mMainExecutor.runAllReady() // Verify no view is removed. - verify(mWindowManager, Mockito.never()).removeView(any()) + verify(mWindowManager, never()).removeView(any()) // Verify state still correctly set. verify(mKeyguardUpdateMonitor).removeCallback(any()) @@ -665,7 +631,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { false, /*shouldShowComplication*/ ) mMainExecutor.runAllReady() - verify(mWindowManager, Mockito.never()).addView(any(), any()) + verify(mWindowManager, never()).addView(any(), any()) } @Test @@ -673,7 +639,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { // Service destroyed before dream started. mService.onDestroy() mMainExecutor.runAllReady() - verify(mWindowManager, Mockito.never()).removeView(any()) + verify(mWindowManager, never()).removeView(any()) } @Test @@ -691,8 +657,8 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { mMainExecutor.runAllReady() // Verify that a new window is added. - verify(mWindowManager).addView(mViewCaptor!!.capture(), any()) - val windowDecorView = mViewCaptor!!.value + verify(mWindowManager).addView(mViewCaptor.capture(), any()) + val windowDecorView = mViewCaptor.firstValue // Assert that the overlay is not showing complications. assertThat(mService.shouldShowComplications()).isFalse() @@ -751,7 +717,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { @Test fun testWakeUpBeforeStartDoesNothing() { mService.onWakeUp() - verify(mDreamOverlayContainerViewController, Mockito.never()).onWakeUp() + verify(mDreamOverlayContainerViewController, never()).onWakeUp() } @Test @@ -879,8 +845,8 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { ) mMainExecutor.runAllReady() - whenever(mDreamOverlayContainerViewController.isBouncerShowing()).thenReturn(true) - mService!!.onComeToFront() + whenever(mDreamOverlayContainerViewController.isBouncerShowing).thenReturn(true) + mService.onComeToFront() verify(mScrimController).expand(any()) } @@ -900,7 +866,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { ) mMainExecutor.runAllReady() - mService!!.onComeToFront() + mService.onComeToFront() assertThat(communalRepository.currentScene.value).isEqualTo(CommunalScenes.Blank) } @@ -920,7 +886,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { ) mMainExecutor.runAllReady() - mService!!.onComeToFront() + mService.onComeToFront() verify(mSystemDialogsCloser).closeSystemDialogs() } @@ -1320,6 +1286,45 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { environmentComponents.verifyNoMoreInteractions() } + @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE) + @Test + fun testAmbientTouchHandlersRegistration_registerHideComplicationAndCommunal() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*isPreview*/, + false, /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + + verify(mAmbientTouchComponentFactory).create(any(), mTouchHandlersCaptor.capture(), any()) + assertThat(mTouchHandlersCaptor.firstValue) + .containsExactly(mHideComplicationTouchHandler, mCommunalTouchHandler) + } + + @EnableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE) + @Test + fun testAmbientTouchHandlersRegistration_v2_registerOnlyHideComplication() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*isPreview*/, + false, /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + + verify(mAmbientTouchComponentFactory).create(any(), mTouchHandlersCaptor.capture(), any()) + assertThat(mTouchHandlersCaptor.firstValue).containsExactly(mHideComplicationTouchHandler) + } + internal class FakeLifecycleRegistry(provider: LifecycleOwner) : LifecycleRegistry(provider) { val mLifecycles: MutableList<State> = ArrayList() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt index 82bcecef1f70..55b87db232e8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelTest.kt @@ -27,6 +27,7 @@ import com.android.compose.animation.scene.UserActionResult import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.EnableSceneContainer @@ -72,8 +73,10 @@ class DreamUserActionsViewModelTest : SysuiTestCase() { @Test @DisableFlags(DualShade.FLAG_NAME) - fun actions_singleShade() = + fun actions_communalNotAvailable_singleShade() = testScope.runTest { + kosmos.setCommunalAvailable(false) + val actions by collectLastValue(underTest.actions) setUpState( @@ -85,6 +88,8 @@ class DreamUserActionsViewModelTest : SysuiTestCase() { assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) assertThat(actions?.get(Swipe.Down)) .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true)) + assertThat(actions?.get(Swipe.Start)).isNull() + assertThat(actions?.get(Swipe.End)).isNull() setUpState( isShadeTouchable = false, @@ -102,12 +107,16 @@ class DreamUserActionsViewModelTest : SysuiTestCase() { assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) assertThat(actions?.get(Swipe.Down)) .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true)) + assertThat(actions?.get(Swipe.Start)).isNull() + assertThat(actions?.get(Swipe.End)).isNull() } @Test @DisableFlags(DualShade.FLAG_NAME) - fun actions_splitShade() = + fun actions_communalNotAvailable_splitShade() = testScope.runTest { + kosmos.setCommunalAvailable(false) + val actions by collectLastValue(underTest.actions) setUpState( @@ -119,6 +128,8 @@ class DreamUserActionsViewModelTest : SysuiTestCase() { assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) assertThat(actions?.get(Swipe.Down)) .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true)) + assertThat(actions?.get(Swipe.Start)).isNull() + assertThat(actions?.get(Swipe.End)).isNull() setUpState( isShadeTouchable = false, @@ -136,12 +147,136 @@ class DreamUserActionsViewModelTest : SysuiTestCase() { assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) assertThat(actions?.get(Swipe.Down)) .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true)) + assertThat(actions?.get(Swipe.Start)).isNull() + assertThat(actions?.get(Swipe.End)).isNull() } @Test @EnableFlags(DualShade.FLAG_NAME) - fun actions_dualShade() = + fun actions_communalNotAvailable_dualShade() = testScope.runTest { + kosmos.setCommunalAvailable(false) + + val actions by collectLastValue(underTest.actions) + + setUpState( + isShadeTouchable = true, + isDeviceUnlocked = false, + shadeMode = ShadeMode.Dual, + ) + assertThat(actions).isNotEmpty() + assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) + assertThat(actions?.get(Swipe.Down)) + .isEqualTo( + UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true) + ) + assertThat(actions?.get(Swipe.Start)).isNull() + assertThat(actions?.get(Swipe.End)).isNull() + + setUpState( + isShadeTouchable = false, + isDeviceUnlocked = false, + shadeMode = ShadeMode.Dual, + ) + assertThat(actions).isEmpty() + + setUpState(isShadeTouchable = true, isDeviceUnlocked = true, shadeMode = ShadeMode.Dual) + assertThat(actions).isNotEmpty() + assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) + assertThat(actions?.get(Swipe.Down)) + .isEqualTo( + UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true) + ) + assertThat(actions?.get(Swipe.Start)).isNull() + assertThat(actions?.get(Swipe.End)).isNull() + } + + @Test + @DisableFlags(DualShade.FLAG_NAME) + fun actions_communalAvailable_singleShade() = + testScope.runTest { + kosmos.setCommunalAvailable(true) + + val actions by collectLastValue(underTest.actions) + + setUpState( + isShadeTouchable = true, + isDeviceUnlocked = false, + shadeMode = ShadeMode.Single, + ) + assertThat(actions).isNotEmpty() + assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) + assertThat(actions?.get(Swipe.Down)) + .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true)) + assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) + assertThat(actions?.get(Swipe.End)).isNull() + + setUpState( + isShadeTouchable = false, + isDeviceUnlocked = false, + shadeMode = ShadeMode.Single, + ) + assertThat(actions).isEmpty() + + setUpState( + isShadeTouchable = true, + isDeviceUnlocked = true, + shadeMode = ShadeMode.Single, + ) + assertThat(actions).isNotEmpty() + assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) + assertThat(actions?.get(Swipe.Down)) + .isEqualTo(UserActionResult(Scenes.Shade, isIrreversible = true)) + assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) + assertThat(actions?.get(Swipe.End)).isNull() + } + + @Test + @DisableFlags(DualShade.FLAG_NAME) + fun actions_communalAvailable_splitShade() = + testScope.runTest { + kosmos.setCommunalAvailable(true) + + val actions by collectLastValue(underTest.actions) + + setUpState( + isShadeTouchable = true, + isDeviceUnlocked = false, + shadeMode = ShadeMode.Split, + ) + assertThat(actions).isNotEmpty() + assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Bouncer)) + assertThat(actions?.get(Swipe.Down)) + .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true)) + assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) + assertThat(actions?.get(Swipe.End)).isNull() + + setUpState( + isShadeTouchable = false, + isDeviceUnlocked = false, + shadeMode = ShadeMode.Split, + ) + assertThat(actions).isEmpty() + + setUpState( + isShadeTouchable = true, + isDeviceUnlocked = true, + shadeMode = ShadeMode.Split, + ) + assertThat(actions).isNotEmpty() + assertThat(actions?.get(Swipe.Up)).isEqualTo(UserActionResult(Scenes.Gone)) + assertThat(actions?.get(Swipe.Down)) + .isEqualTo(UserActionResult(Scenes.Shade, ToSplitShade, isIrreversible = true)) + assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) + assertThat(actions?.get(Swipe.End)).isNull() + } + + @Test + @EnableFlags(DualShade.FLAG_NAME) + fun actions_communalAvailable_dualShade() = + testScope.runTest { + kosmos.setCommunalAvailable(true) + val actions by collectLastValue(underTest.actions) setUpState( @@ -155,6 +290,8 @@ class DreamUserActionsViewModelTest : SysuiTestCase() { .isEqualTo( UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true) ) + assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) + assertThat(actions?.get(Swipe.End)).isNull() setUpState( isShadeTouchable = false, @@ -170,6 +307,8 @@ class DreamUserActionsViewModelTest : SysuiTestCase() { .isEqualTo( UserActionResult.ShowOverlay(Overlays.NotificationsShade, isIrreversible = true) ) + assertThat(actions?.get(Swipe.Start)).isEqualTo(UserActionResult(Scenes.Communal)) + assertThat(actions?.get(Swipe.End)).isNull() } private fun TestScope.setUpState( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt index dd8370231ef0..e288522ec212 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt @@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.contextualeducation.GestureType import com.android.systemui.contextualeducation.GestureType.BACK +import com.android.systemui.contextualeducation.GestureType.HOME import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor import com.android.systemui.education.domain.interactor.contextualEducationInteractor @@ -52,6 +53,7 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.junit.MockitoJUnit import org.mockito.kotlin.any +import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -66,6 +68,7 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { private val minDurationForNextEdu = KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds private lateinit var underTest: ContextualEduUiCoordinator + private lateinit var previousDialog: Dialog @Mock private lateinit var dialog: Dialog @Mock private lateinit var notificationManager: NotificationManager @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper @@ -95,9 +98,11 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { kosmos.applicationCoroutineScope, viewModel, kosmos.applicationContext, - notificationManager + notificationManager, ) { model -> toastContent = model.message + previousDialog = dialog + dialog = mock<Dialog>() dialog } underTest.start() @@ -129,6 +134,14 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { } @Test + fun dismissPreviousDialogOnNewDialog() = + testScope.runTest { + triggerEducation(BACK) + triggerEducation(HOME) + verify(previousDialog).dismiss() + } + + @Test fun verifyBackEduToastContent() = testScope.runTest { triggerEducation(BACK) @@ -149,14 +162,14 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { verifyNotificationContent( R.string.back_edu_notification_title, R.string.back_edu_notification_content, - notificationCaptor.value + notificationCaptor.value, ) } private fun verifyNotificationContent( titleResId: Int, contentResId: Int, - notification: Notification + notification: Notification, ) { val expectedContent = context.getString(contentResId) val expectedTitle = context.getString(titleResId) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt index 639737b37efd..76434ee54627 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt @@ -27,9 +27,11 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.domain.interactor.KeyboardTouchpadConnectionInteractor -import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEY -import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEYBOARD -import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_TOUCHPAD +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_KEY +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_KEYBOARD +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_TOUCHPAD +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_TOUCHPAD_BACK +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_TOUCHPAD_HOME import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.ACTION_KEY import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTURE import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE @@ -66,8 +68,8 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { private val sysUiState = kosmos.sysUiState private val touchpadRepo = PrettyFakeTouchpadRepository() private val keyboardRepo = kosmos.keyboardRepository - private var startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD - private val viewModel by lazy { createViewModel(startingPeripheral) } + private var tutorialScope = INTENT_TUTORIAL_SCOPE_TOUCHPAD + private val viewModel by lazy { createViewModel(tutorialScope) } // createUnsafe so its methods don't have to be called on Main thread private val lifecycle = LifecycleRegistry.createUnsafe(mock(LifecycleOwner::class.java)) @@ -75,7 +77,7 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { @get:Rule val mainDispatcherRule = MainDispatcherRule(kosmos.testDispatcher) private fun createViewModel( - startingPeripheral: String = INTENT_TUTORIAL_TYPE_TOUCHPAD, + scope: String = INTENT_TUTORIAL_SCOPE_TOUCHPAD, hasTouchpadTutorialScreens: Boolean = true, ): KeyboardTouchpadTutorialViewModel { val viewModel = @@ -84,7 +86,7 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { KeyboardTouchpadConnectionInteractor(keyboardRepo, touchpadRepo), hasTouchpadTutorialScreens, mock<InputDeviceTutorialLogger>(), - SavedStateHandle(mapOf(INTENT_TUTORIAL_TYPE_KEY to startingPeripheral)) + SavedStateHandle(mapOf(INTENT_TUTORIAL_SCOPE_KEY to scope)), ) lifecycle.addObserver(viewModel) return viewModel @@ -169,7 +171,7 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { @Test fun screensOrder_whenGoingBackAndOnlyKeyboardConnected() = testScope.runTest { - startingPeripheral = INTENT_TUTORIAL_TYPE_KEYBOARD + tutorialScope = INTENT_TUTORIAL_SCOPE_KEYBOARD val screens by collectValues(viewModel.screen) val closeActivity by collectLastValue(viewModel.closeActivity) peripheralsState(keyboardConnected = true, touchpadConnected = false) @@ -185,7 +187,7 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { @Test fun screensOrder_whenTouchpadConnected() = testScope.runTest { - startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD + tutorialScope = INTENT_TUTORIAL_SCOPE_TOUCHPAD val screens by collectValues(viewModel.screen) val closeActivity by collectLastValue(viewModel.closeActivity) @@ -193,22 +195,47 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { goToNextScreen() goToNextScreen() - goToNextScreen() assertThat(screens).containsExactly(BACK_GESTURE, HOME_GESTURE).inOrder() assertThat(closeActivity).isTrue() } @Test - fun screensOrder_whenKeyboardConnected() = + fun screensOrder_withBackGestureScope() = testScope.runTest { - startingPeripheral = INTENT_TUTORIAL_TYPE_KEYBOARD + tutorialScope = INTENT_TUTORIAL_SCOPE_TOUCHPAD_BACK val screens by collectValues(viewModel.screen) val closeActivity by collectLastValue(viewModel.closeActivity) + peripheralsState(touchpadConnected = true) - peripheralsState(keyboardConnected = true) + goToNextScreen() + + assertThat(screens).containsExactly(BACK_GESTURE).inOrder() + assertThat(closeActivity).isTrue() + } + + @Test + fun screensOrder_withHomeGestureScope() = + testScope.runTest { + tutorialScope = INTENT_TUTORIAL_SCOPE_TOUCHPAD_HOME + val screens by collectValues(viewModel.screen) + val closeActivity by collectLastValue(viewModel.closeActivity) + peripheralsState(touchpadConnected = true) goToNextScreen() + + assertThat(screens).containsExactly(HOME_GESTURE).inOrder() + assertThat(closeActivity).isTrue() + } + + @Test + fun screensOrder_withKeyboardScope() = + testScope.runTest { + tutorialScope = INTENT_TUTORIAL_SCOPE_KEYBOARD + val screens by collectValues(viewModel.screen) + val closeActivity by collectLastValue(viewModel.closeActivity) + peripheralsState(keyboardConnected = true) + goToNextScreen() assertThat(screens).containsExactly(ACTION_KEY).inOrder() @@ -218,7 +245,7 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { @Test fun touchpadGesturesDisabled_onlyDuringTouchpadTutorial() = testScope.runTest { - startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD + tutorialScope = INTENT_TUTORIAL_SCOPE_TOUCHPAD collectValues(viewModel.screen) // just to initialize viewModel peripheralsState(keyboardConnected = true, touchpadConnected = true) @@ -234,8 +261,8 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { testScope.runTest { val viewModel = createViewModel( - startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD, - hasTouchpadTutorialScreens = false + scope = INTENT_TUTORIAL_SCOPE_TOUCHPAD, + hasTouchpadTutorialScreens = false, ) val screens by collectValues(viewModel.screen) val closeActivity by collectLastValue(viewModel.closeActivity) @@ -248,7 +275,7 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { @Test fun touchpadGesturesDisabled_whenTutorialGoesToForeground() = testScope.runTest { - startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD + tutorialScope = INTENT_TUTORIAL_SCOPE_TOUCHPAD collectValues(viewModel.screen) // just to initialize viewModel peripheralsState(touchpadConnected = true) @@ -260,7 +287,7 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { @Test fun touchpadGesturesNotDisabled_whenTutorialGoesToBackground() = testScope.runTest { - startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD + tutorialScope = INTENT_TUTORIAL_SCOPE_TOUCHPAD collectValues(viewModel.screen) peripheralsState(touchpadConnected = true) @@ -288,7 +315,7 @@ class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() { private fun TestScope.peripheralsState( keyboardConnected: Boolean = false, - touchpadConnected: Boolean = false + touchpadConnected: Boolean = false, ) { keyboardRepo.setIsAnyKeyboardConnected(keyboardConnected) touchpadRepo.setIsAnyTouchpadConnected(touchpadConnected) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt index f04540426fc1..0d32b7fb1b3e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt @@ -18,36 +18,31 @@ package com.android.systemui.keyboard.shortcut.data.repository import android.content.Context import android.content.Context.INPUT_SERVICE -import android.hardware.input.InputGestureData -import android.hardware.input.InputGestureData.createKeyTrigger -import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS +import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS import android.hardware.input.fakeInputManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags -import android.view.KeyEvent.KEYCODE_A import android.view.KeyEvent.KEYCODE_SLASH -import android.view.KeyEvent.META_ALT_ON import android.view.KeyEvent.META_CAPS_LOCK_ON -import android.view.KeyEvent.META_CTRL_ON -import android.view.KeyEvent.META_FUNCTION_ON -import android.view.KeyEvent.META_META_LEFT_ON import android.view.KeyEvent.META_META_ON -import android.view.KeyEvent.META_SHIFT_ON -import android.view.KeyEvent.META_SHIFT_RIGHT_ON -import android.view.KeyEvent.META_SYM_ON import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.hardware.input.Flags.FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult import com.android.systemui.keyboard.shortcut.customShortcutCategoriesRepository +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.ALL_SUPPORTED_MODIFIERS +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allCustomizableInputGesturesWithSimpleShortcutCombinations import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.customizableInputGestureWithUnknownKeyGestureType import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedShortcutCategoriesWithSimpleShortcutCombination +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardAddCustomShortcutRequestInfo +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardDeleteCustomShortcutRequestInfo +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardKeyCombination import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper import com.android.systemui.kosmos.testScope @@ -74,24 +69,21 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext }) } - private val fakeInputManager = kosmos.fakeInputManager + private val inputManager = kosmos.fakeInputManager.inputManager private val testScope = kosmos.testScope private val helper = kosmos.shortcutHelperTestHelper private val repo = kosmos.customShortcutCategoriesRepository @Before fun setup() { - whenever(mockUserContext.getSystemService(INPUT_SERVICE)) - .thenReturn(fakeInputManager.inputManager) + whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager) } @Test @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER) fun categories_emitsCorrectlyConvertedShortcutCategories() { testScope.runTest { - whenever( - fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()) - ) + whenever(inputManager.getCustomInputGestures(/* filter= */ anyOrNull())) .thenReturn(allCustomizableInputGesturesWithSimpleShortcutCombinations) helper.toggle(deviceId = 123) @@ -106,9 +98,7 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { @DisableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER) fun categories_emitsEmptyListWhenFlagIsDisabled() { testScope.runTest { - whenever( - fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()) - ) + whenever(inputManager.getCustomInputGestures(/* filter= */ anyOrNull())) .thenReturn(allCustomizableInputGesturesWithSimpleShortcutCombinations) helper.toggle(deviceId = 123) @@ -122,9 +112,7 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER) fun categories_ignoresUnknownKeyGestureTypes() { testScope.runTest { - whenever( - fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()) - ) + whenever(inputManager.getCustomInputGestures(/* filter= */ anyOrNull())) .thenReturn(customizableInputGestureWithUnknownKeyGestureType) helper.toggle(deviceId = 123) @@ -151,7 +139,7 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { helper.toggle(deviceId = 123) val pressedKeys by collectLastValue(repo.pressedKeys) repo.updateUserKeyCombination( - KeyCombination(modifiers = allSupportedModifiers, keyCode = null) + KeyCombination(modifiers = ALL_SUPPORTED_MODIFIERS, keyCode = null) ) assertThat(pressedKeys) @@ -199,11 +187,11 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { @Test fun shortcutBeingCustomized_updatedOnCustomizationRequested() { testScope.runTest { - repo.onCustomizationRequested(standardCustomizationRequestInfo) + repo.onCustomizationRequested(standardAddCustomShortcutRequestInfo) val shortcutBeingCustomized = repo.getShortcutBeingCustomized() - assertThat(shortcutBeingCustomized).isEqualTo(standardCustomizationRequestInfo) + assertThat(shortcutBeingCustomized).isEqualTo(standardAddCustomShortcutRequestInfo) } } @@ -223,7 +211,7 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { fun buildInputGestureDataForShortcutBeingCustomized_noKeyCombinationSelected_returnsNull() { testScope.runTest { helper.toggle(deviceId = 123) - repo.onCustomizationRequested(standardCustomizationRequestInfo) + repo.onCustomizationRequested(standardAddCustomShortcutRequestInfo) val inputGestureData = repo.buildInputGestureDataForShortcutBeingCustomized() @@ -235,46 +223,32 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { fun buildInputGestureDataForShortcutBeingCustomized_successfullyBuildInputGestureData() { testScope.runTest { helper.toggle(deviceId = 123) - repo.onCustomizationRequested(standardCustomizationRequestInfo) + repo.onCustomizationRequested(standardAddCustomShortcutRequestInfo) repo.updateUserKeyCombination(standardKeyCombination) val inputGestureData = repo.buildInputGestureDataForShortcutBeingCustomized() // using toString as we're testing for only structural equality not referential. // inputGestureData is a java class and isEqual Tests for referential equality // as well which would cause this assert to fail - assertThat(inputGestureData.toString()).isEqualTo(standardInputGestureData.toString()) + assertThat(inputGestureData.toString()).isEqualTo(allAppsInputGestureData.toString()) } } - private val standardCustomizationRequestInfo = - ShortcutCustomizationRequestInfo.Add( - label = "Open apps list", - categoryType = ShortcutCategoryType.System, - subCategoryLabel = "System controls", - ) - - private val standardKeyCombination = - KeyCombination( - modifiers = META_META_ON or META_SHIFT_ON or META_META_LEFT_ON or META_SHIFT_RIGHT_ON, - keyCode = KEYCODE_A, - ) - - private val allSupportedModifiers = - META_META_ON or - META_CTRL_ON or - META_FUNCTION_ON or - META_SHIFT_ON or - META_ALT_ON or - META_SYM_ON - - private val standardInputGestureData = - InputGestureData.Builder() - .setKeyGestureType(KEY_GESTURE_TYPE_ALL_APPS) - .setTrigger( - createKeyTrigger( - /* keycode = */ standardKeyCombination.keyCode!!, - /* modifierState = */ standardKeyCombination.modifiers and allSupportedModifiers, - ) - ) - .build() + @Test + @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER) + fun deleteShortcut_successfullyRetrievesGestureDataAndDeletesShortcut() { + testScope.runTest { + whenever(inputManager.getCustomInputGestures(anyOrNull())) + .thenReturn(listOf(allAppsInputGestureData, goHomeInputGestureData)) + whenever(inputManager.removeCustomInputGesture(allAppsInputGestureData)) + .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_SUCCESS) + + helper.toggle(deviceId = 123) + repo.onCustomizationRequested(standardDeleteCustomShortcutRequestInfo) + + val result = repo.deleteShortcutCurrentlyBeingCustomized() + + assertThat(result).isEqualTo(ShortcutCustomizationRequestResult.SUCCESS) + } + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt index a1e7ef4ac5a3..8466eab2aca6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt @@ -19,9 +19,23 @@ package com.android.systemui.keyboard.shortcut.data.source import android.hardware.input.InputGestureData import android.hardware.input.InputGestureData.createKeyTrigger import android.hardware.input.KeyGestureEvent +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_HOME +import android.os.SystemClock import android.view.KeyEvent +import android.view.KeyEvent.ACTION_DOWN +import android.view.KeyEvent.KEYCODE_A +import android.view.KeyEvent.META_ALT_ON +import android.view.KeyEvent.META_CTRL_ON +import android.view.KeyEvent.META_FUNCTION_ON +import android.view.KeyEvent.META_META_LEFT_ON +import android.view.KeyEvent.META_META_ON +import android.view.KeyEvent.META_SHIFT_ON +import android.view.KeyEvent.META_SHIFT_RIGHT_ON +import android.view.KeyEvent.META_SYM_ON import android.view.KeyboardShortcutGroup import android.view.KeyboardShortcutInfo +import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination import com.android.systemui.keyboard.shortcut.shared.model.Shortcut import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType @@ -29,9 +43,11 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType. import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory import com.android.systemui.keyboard.shortcut.shared.model.shortcut +import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState import com.android.systemui.res.R object TestShortcuts { @@ -525,4 +541,110 @@ object TestShortcuts { keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER ), ) + + val standardAddCustomShortcutRequestInfo = + ShortcutCustomizationRequestInfo.Add( + label = "Open apps list", + categoryType = System, + subCategoryLabel = "System controls", + ) + + val standardDeleteCustomShortcutRequestInfo = + ShortcutCustomizationRequestInfo.Delete( + label = "Open apps list", + categoryType = System, + subCategoryLabel = "System controls", + ) + + val standardKeyCombination = + KeyCombination( + modifiers = META_META_ON or META_SHIFT_ON or META_META_LEFT_ON or META_SHIFT_RIGHT_ON, + keyCode = KEYCODE_A, + ) + + const val ALL_SUPPORTED_MODIFIERS = + META_META_ON or + META_CTRL_ON or + META_FUNCTION_ON or + META_SHIFT_ON or + META_ALT_ON or + META_SYM_ON + + val allAppsInputGestureData: InputGestureData = + InputGestureData.Builder() + .setKeyGestureType(KEY_GESTURE_TYPE_ALL_APPS) + .setTrigger( + createKeyTrigger( + /* keycode = */ standardKeyCombination.keyCode!!, + /* modifierState = */ standardKeyCombination.modifiers and + ALL_SUPPORTED_MODIFIERS, + ) + ) + .build() + + val goHomeInputGestureData: InputGestureData = + InputGestureData.Builder() + .setKeyGestureType(KEY_GESTURE_TYPE_HOME) + .setTrigger( + createKeyTrigger( + /* keycode = */ standardKeyCombination.keyCode!!, + /* modifierState = */ standardKeyCombination.modifiers and + ALL_SUPPORTED_MODIFIERS, + ) + ) + .build() + + val expectedStandardDeleteShortcutUiState = + ShortcutCustomizationUiState.DeleteShortcutDialog(isDialogShowing = false) + + val keyDownEventWithoutActionKeyPressed = + androidx.compose.ui.input.key.KeyEvent( + android.view.KeyEvent( + /* downTime = */ SystemClock.uptimeMillis(), + /* eventTime = */ SystemClock.uptimeMillis(), + /* action = */ ACTION_DOWN, + /* code = */ KEYCODE_A, + /* repeat = */ 0, + /* metaState = */ META_CTRL_ON, + ) + ) + + val keyDownEventWithActionKeyPressed = + androidx.compose.ui.input.key.KeyEvent( + android.view.KeyEvent( + /* downTime = */ SystemClock.uptimeMillis(), + /* eventTime = */ SystemClock.uptimeMillis(), + /* action = */ ACTION_DOWN, + /* code = */ KEYCODE_A, + /* repeat = */ 0, + /* metaState = */ META_CTRL_ON or META_META_ON, + ) + ) + + val keyUpEventWithActionKeyPressed = + androidx.compose.ui.input.key.KeyEvent( + android.view.KeyEvent( + /* downTime = */ SystemClock.uptimeMillis(), + /* eventTime = */ SystemClock.uptimeMillis(), + /* action = */ ACTION_DOWN, + /* code = */ KEYCODE_A, + /* repeat = */ 0, + /* metaState = */ 0, + ) + ) + + val standardAddShortcutRequest = + ShortcutCustomizationRequestInfo.Add( + label = "Standard shortcut", + categoryType = ShortcutCategoryType.System, + subCategoryLabel = "Standard subcategory", + ) + + val expectedStandardAddShortcutUiState = + ShortcutCustomizationUiState.AddShortcutDialog( + shortcutLabel = "Standard shortcut", + defaultCustomShortcutModifierKey = + ShortcutKey.Icon.ResIdIcon(R.drawable.ic_ksh_key_meta), + isDialogShowing = false, + ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt index 032979447861..0a4198a99bba 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt @@ -32,7 +32,8 @@ import java.util.Optional import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -125,14 +126,11 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes ) underTest = - HomeControlsKeyguardQuickAffordanceConfig( - context = context, - component = component, - ) + HomeControlsKeyguardQuickAffordanceConfig(context = context, component = component) } @Test - fun state() = runBlockingTest { + fun state() = runTest(UnconfinedTestDispatcher()) { whenever(component.isEnabled()).thenReturn(isFeatureEnabled) whenever(controlsController.getFavorites()) .thenReturn( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt index 7d68cc0a3560..0003d07d1e2f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt @@ -19,19 +19,20 @@ package com.android.systemui.keyguard.data.quickaffordance import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.Expandable import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.dagger.ControlsComponent import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult +import com.android.systemui.res.R import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -54,14 +55,11 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() { whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true)) underTest = - HomeControlsKeyguardQuickAffordanceConfig( - context = context, - component = component, - ) + HomeControlsKeyguardQuickAffordanceConfig(context = context, component = component) } @Test - fun state_whenCannotShowWhileLocked_returnsHidden() = runBlockingTest { + fun state_whenCannotShowWhileLocked_returnsHidden() = runTest(UnconfinedTestDispatcher()) { whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false)) whenever(component.isEnabled()).thenReturn(true) whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon) @@ -81,7 +79,7 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun state_whenListingControllerIsMissing_returnsHidden() = runBlockingTest { + fun state_whenListingControllerIsMissing_returnsHidden() = runTest(UnconfinedTestDispatcher()) { whenever(component.isEnabled()).thenReturn(true) whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon) whenever(component.getTileTitleId()).thenReturn(R.string.quick_controls_title) @@ -100,23 +98,26 @@ class HomeControlsKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsTrue() = runBlockingTest { - whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true)) + fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsTrue() = + runTest(UnconfinedTestDispatcher()) { + whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true)) - val onClickedResult = underTest.onTriggered(expandable) + val onClickedResult = underTest.onTriggered(expandable) - assertThat(onClickedResult).isInstanceOf(OnTriggeredResult.StartActivity::class.java) - assertThat((onClickedResult as OnTriggeredResult.StartActivity).canShowWhileLocked).isTrue() - } + assertThat(onClickedResult).isInstanceOf(OnTriggeredResult.StartActivity::class.java) + assertThat((onClickedResult as OnTriggeredResult.StartActivity).canShowWhileLocked) + .isTrue() + } @Test - fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsFalse() = runBlockingTest { - whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false)) + fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsFalse() = + runTest(UnconfinedTestDispatcher()) { + whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false)) - val onClickedResult = underTest.onTriggered(expandable) + val onClickedResult = underTest.onTriggered(expandable) - assertThat(onClickedResult).isInstanceOf(OnTriggeredResult.StartActivity::class.java) - assertThat((onClickedResult as OnTriggeredResult.StartActivity).canShowWhileLocked) - .isFalse() - } + assertThat(onClickedResult).isInstanceOf(OnTriggeredResult.StartActivity::class.java) + assertThat((onClickedResult as OnTriggeredResult.StartActivity).canShowWhileLocked) + .isFalse() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt index ca64cec98b2e..05a74c038cc1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt @@ -29,7 +29,7 @@ import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -56,60 +56,63 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun affordance_setsUpRegistrationAndDeliversInitialModel() = runBlockingTest { - whenever(controller.isEnabledForLockScreenButton).thenReturn(true) - var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null - - val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) + fun affordance_setsUpRegistrationAndDeliversInitialModel() = + runTest(UnconfinedTestDispatcher()) { + whenever(controller.isEnabledForLockScreenButton).thenReturn(true) + var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null + + val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) + + val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>() + verify(controller).addCallback(callbackCaptor.capture()) + verify(controller) + .registerQRCodeScannerChangeObservers( + QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE, + QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE, + ) + assertVisibleState(latest) - val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>() - verify(controller).addCallback(callbackCaptor.capture()) - verify(controller) - .registerQRCodeScannerChangeObservers( - QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE, - QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE - ) - assertVisibleState(latest) - - job.cancel() - verify(controller).removeCallback(callbackCaptor.value) - } + job.cancel() + verify(controller).removeCallback(callbackCaptor.value) + } @Test - fun affordance_scannerActivityChanged_deliversModelWithUpdatedIntent() = runBlockingTest { - whenever(controller.isEnabledForLockScreenButton).thenReturn(true) - var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null - val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) - val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>() - verify(controller).addCallback(callbackCaptor.capture()) + fun affordance_scannerActivityChanged_deliversModelWithUpdatedIntent() = + runTest(UnconfinedTestDispatcher()) { + whenever(controller.isEnabledForLockScreenButton).thenReturn(true) + var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null + val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) + val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>() + verify(controller).addCallback(callbackCaptor.capture()) - whenever(controller.intent).thenReturn(INTENT_2) - callbackCaptor.value.onQRCodeScannerActivityChanged() + whenever(controller.intent).thenReturn(INTENT_2) + callbackCaptor.value.onQRCodeScannerActivityChanged() - assertVisibleState(latest) + assertVisibleState(latest) - job.cancel() - verify(controller).removeCallback(callbackCaptor.value) - } + job.cancel() + verify(controller).removeCallback(callbackCaptor.value) + } @Test - fun affordance_scannerPreferenceChanged_deliversVisibleModel() = runBlockingTest { - var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null - val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) - val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>() - verify(controller).addCallback(callbackCaptor.capture()) + fun affordance_scannerPreferenceChanged_deliversVisibleModel() = + runTest(UnconfinedTestDispatcher()) { + var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null + val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) + val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>() + verify(controller).addCallback(callbackCaptor.capture()) - whenever(controller.isEnabledForLockScreenButton).thenReturn(true) - callbackCaptor.value.onQRCodeScannerPreferenceChanged() + whenever(controller.isEnabledForLockScreenButton).thenReturn(true) + callbackCaptor.value.onQRCodeScannerPreferenceChanged() - assertVisibleState(latest) + assertVisibleState(latest) - job.cancel() - verify(controller).removeCallback(callbackCaptor.value) - } + job.cancel() + verify(controller).removeCallback(callbackCaptor.value) + } @Test - fun affordance_scannerPreferenceChanged_deliversNone() = runBlockingTest { + fun affordance_scannerPreferenceChanged_deliversNone() = runTest(UnconfinedTestDispatcher()) { var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>() @@ -128,30 +131,29 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { fun onQuickAffordanceTriggered() { assertThat(underTest.onTriggered(mock())) .isEqualTo( - OnTriggeredResult.StartActivity( - intent = INTENT_1, - canShowWhileLocked = true, - ) + OnTriggeredResult.StartActivity(intent = INTENT_1, canShowWhileLocked = true) ) } @Test - fun getPickerScreenState_enabledIfConfiguredOnDevice_isEnabledForPickerState() = runTest { - whenever(controller.isAllowedOnLockScreen).thenReturn(true) - whenever(controller.isAbleToLaunchScannerActivity).thenReturn(true) + fun getPickerScreenState_enabledIfConfiguredOnDevice_isEnabledForPickerState() = + runTest(UnconfinedTestDispatcher()) { + whenever(controller.isAllowedOnLockScreen).thenReturn(true) + whenever(controller.isAbleToLaunchScannerActivity).thenReturn(true) - assertThat(underTest.getPickerScreenState()) - .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default()) - } + assertThat(underTest.getPickerScreenState()) + .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default()) + } @Test - fun getPickerScreenState_disabledIfConfiguredOnDevice_isDisabledForPickerState() = runTest { - whenever(controller.isAllowedOnLockScreen).thenReturn(true) - whenever(controller.isAbleToLaunchScannerActivity).thenReturn(false) - - assertThat(underTest.getPickerScreenState()) - .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice) - } + fun getPickerScreenState_disabledIfConfiguredOnDevice_isDisabledForPickerState() = + runTest(UnconfinedTestDispatcher()) { + whenever(controller.isAllowedOnLockScreen).thenReturn(true) + whenever(controller.isAbleToLaunchScannerActivity).thenReturn(false) + + assertThat(underTest.getPickerScreenState()) + .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice) + } private fun assertVisibleState(latest: KeyguardQuickAffordanceConfig.LockScreenState?) { assertThat(latest) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt index bd26e4205242..bef995fa5225 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.internal.widget.lockPatternUtils import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.repository.biometricSettingsRepository @@ -33,6 +34,8 @@ import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) @@ -52,14 +55,21 @@ class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() { testScope.runTest { val values by collectValues(underTest.lockWhileAwakeEvents) - underTest.onKeyguardServiceDoKeyguardTimeout(options = null) + kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true) + runCurrent() + + kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout( + options = null + ) runCurrent() assertThat(values) .containsExactly(LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON) advanceTimeBy(1000) - underTest.onKeyguardServiceDoKeyguardTimeout(options = null) + kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout( + options = null + ) runCurrent() assertThat(values) @@ -69,8 +79,15 @@ class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() { ) } + /** + * We re-show keyguard when it's re-enabled, but only if it was originally showing when we + * disabled it. + * + * If it wasn't showing when originally disabled it, re-enabling it should do nothing (the + * keyguard will re-show next time we're locked). + */ @Test - fun emitsWhenKeyguardEnabled_onlyIfShowingWhenDisabled() = + fun emitsWhenKeyguardReenabled_onlyIfShowingWhenDisabled() = testScope.runTest { val values by collectValues(underTest.lockWhileAwakeEvents) @@ -98,4 +115,49 @@ class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() { assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED) } + + /** + * Un-suppressing keyguard should never cause us to re-show. We'll re-show when we're next + * locked, even if we were showing when originally suppressed. + */ + @Test + fun doesNotEmit_keyguardNoLongerSuppressed() = + testScope.runTest { + val values by collectValues(underTest.lockWhileAwakeEvents) + + // Enable keyguard and then suppress it. + kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true) + whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true) + runCurrent() + + assertEquals(0, values.size) + + // Un-suppress keyguard. + whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false) + runCurrent() + + assertEquals(0, values.size) + } + + /** + * Lockdown and lockNow() should not cause us to lock while awake if we are suppressed via adb. + */ + @Test + fun doesNotEmit_fromLockdown_orFromLockNow_ifEnabledButSuppressed() = + testScope.runTest { + val values by collectValues(underTest.lockWhileAwakeEvents) + + // Set keyguard enabled, but then disable lockscreen (suppress it). + kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true) + whenever(kosmos.lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true) + runCurrent() + + kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null) + runCurrent() + + kosmos.biometricSettingsRepository.setIsUserInLockdown(true) + runCurrent() + + assertEquals(0, values.size) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt index 7e249e8c179d..ead151ee6df2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt @@ -87,9 +87,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) repository.setKeyguardEnabled(false) @@ -100,33 +100,26 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { false, // Default to false. true, // True now that keyguard service is disabled ), - canWake + canWake, ) repository.setKeyguardEnabled(true) runCurrent() - assertEquals( - listOf( - false, - true, - false, - ), - canWake - ) + assertEquals(listOf(false, true, false), canWake) } @Test @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) - fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled() = + fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled_onlyAfterWakefulnessChange() = testScope.runTest { val canWake by collectValues(underTest.canWakeDirectlyToGone) assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true) @@ -136,9 +129,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { listOf( // Still false - isLockScreenDisabled only causes canWakeDirectlyToGone to // update on the next wake/sleep event. - false, + false ), - canWake + canWake, ) kosmos.powerInteractor.setAsleepForTest() @@ -150,7 +143,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { // True since we slept after setting isLockScreenDisabled=true true, ), - canWake + canWake, ) kosmos.powerInteractor.setAwakeForTest() @@ -159,25 +152,75 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { kosmos.powerInteractor.setAsleepForTest() runCurrent() + assertEquals(listOf(false, true), canWake) + + whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false) + kosmos.powerInteractor.setAwakeForTest() + runCurrent() + + assertEquals(listOf(false, true, false), canWake) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled_lockNowEvent() = + testScope.runTest { + val canWake by collectValues(underTest.canWakeDirectlyToGone) + + assertEquals( + listOf( + false // Defaults to false. + ), + canWake, + ) + + whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true) + runCurrent() + + assertEquals( + listOf( + // Still false - isLockScreenDisabled only causes canWakeDirectlyToGone to + // update on the next wakefulness or lockNow event. + false + ), + canWake, + ) + + kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null) + runCurrent() + assertEquals( listOf( false, + // True when lockNow() called after setting isLockScreenDisabled=true true, ), - canWake + canWake, ) whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false) - kosmos.powerInteractor.setAwakeForTest() + runCurrent() + + assertEquals( + listOf( + false, + // Still true since no lockNow() calls made. + true, + ), + canWake, + ) + + kosmos.keyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(null) runCurrent() assertEquals( listOf( false, true, + // False again after the lockNow() call. false, ), - canWake + canWake, ) } @@ -189,9 +232,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) repository.setBiometricUnlockState(BiometricUnlockMode.WAKE_AND_UNLOCK) @@ -213,9 +256,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) repository.setCanIgnoreAuthAndReturnToGone(true) @@ -237,9 +280,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) whenever(kosmos.devicePolicyManager.getMaximumTimeToLock(eq(null), anyInt())) @@ -257,13 +300,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { ) runCurrent() - assertEquals( - listOf( - false, - true, - ), - canWake - ) + assertEquals(listOf(false, true), canWake) verify(kosmos.alarmManager) .setExactAndAllowWhileIdle( @@ -281,9 +318,9 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { assertEquals( listOf( - false, // Defaults to false. + false // Defaults to false. ), - canWake + canWake, ) whenever(kosmos.devicePolicyManager.getMaximumTimeToLock(eq(null), anyInt())) @@ -312,7 +349,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { // Timed out, so we can ignore auth/return to GONE. true, ), - canWake + canWake, ) verify(kosmos.alarmManager) @@ -338,7 +375,7 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { // alarm in flight that should be canceled. false, ), - canWake + canWake, ) kosmos.powerInteractor.setAsleepForTest( @@ -354,25 +391,17 @@ class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() { // Back to sleep. true, ), - canWake + canWake, ) // Simulate the first sleep's alarm coming in. lastRegisteredBroadcastReceiver?.onReceive( kosmos.mockedContext, - Intent("com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD") + Intent("com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD"), ) runCurrent() // It should not have any effect. - assertEquals( - listOf( - false, - true, - false, - true, - ), - canWake - ) + assertEquals(listOf(false, true, false, true), canWake) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt index 2c12f8782ddc..21019875f51e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt @@ -24,6 +24,7 @@ import com.android.systemui.authentication.data.repository.FakeAuthenticationRep import com.android.systemui.authentication.domain.interactor.authenticationInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.EnableSceneContainer @@ -34,10 +35,12 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati 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.scene.data.model.asIterable +import com.android.systemui.scene.data.model.sceneStackOf import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.Transition -import com.android.systemui.scene.data.repository.sceneContainerRepository import com.android.systemui.scene.data.repository.setSceneTransition +import com.android.systemui.scene.domain.interactor.sceneBackInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos @@ -52,6 +55,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith @@ -85,7 +89,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { fun setUp() { // lazy value needs to be called here otherwise flow collection misbehaves underTest.value - kosmos.sceneContainerRepository.setTransitionState(sceneTransitions) + kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen)) } @Test @@ -883,6 +887,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer + @Ignore("b/378766637") fun lockscreenVisibilityWithScenes() = testScope.runTest { val isDeviceUnlocked by @@ -967,15 +972,56 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer + fun lockscreenVisibilityWithScenes_staysTrue_despiteEnteringIndirectly() = + testScope.runTest { + val isDeviceUnlocked by + collectLastValue( + kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked } + ) + assertThat(isDeviceUnlocked).isFalse() + + val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + + val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility) + assertThat(lockscreenVisibility).isTrue() + + kosmos.setSceneTransition(Idle(Scenes.Shade)) + kosmos.sceneInteractor.changeScene(Scenes.Shade, "") + kosmos.sceneBackInteractor.onSceneChange(from = Scenes.Lockscreen, to = Scenes.Shade) + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(lockscreenVisibility).isTrue() + val sceneBackStack by collectLastValue(kosmos.sceneBackInteractor.backStack) + assertThat(sceneBackStack?.asIterable()?.toList()).isEqualTo(listOf(Scenes.Lockscreen)) + + val isDeviceEntered by collectLastValue(kosmos.deviceEntryInteractor.isDeviceEntered) + val isDeviceEnteredDirectly by + collectLastValue(kosmos.deviceEntryInteractor.isDeviceEnteredDirectly) + runCurrent() + assertThat(isDeviceEntered).isFalse() + assertThat(isDeviceEnteredDirectly).isFalse() + + kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) + kosmos.sceneBackInteractor.updateBackStack { sceneStackOf(Scenes.Gone) } + assertThat(sceneBackStack?.asIterable()?.toList()).isEqualTo(listOf(Scenes.Gone)) + + assertThat(isDeviceEntered).isTrue() + assertThat(isDeviceEnteredDirectly).isFalse() + assertThat(isDeviceUnlocked).isTrue() + assertThat(lockscreenVisibility).isTrue() + } + + @Test + @EnableSceneContainer fun sceneContainer_usingGoingAwayAnimation_duringTransitionToGone() = testScope.runTest { val usingKeyguardGoingAwayAnimation by collectLastValue(underTest.value.usingKeyguardGoingAwayAnimation) - sceneTransitions.value = lsToGone + kosmos.setSceneTransition(lsToGone) assertThat(usingKeyguardGoingAwayAnimation).isTrue() - sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone) + kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone)) assertThat(usingKeyguardGoingAwayAnimation).isFalse() } @@ -986,14 +1032,14 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { val usingKeyguardGoingAwayAnimation by collectLastValue(underTest.value.usingKeyguardGoingAwayAnimation) - sceneTransitions.value = lsToGone + kosmos.setSceneTransition(lsToGone) surfaceBehindIsAnimatingFlow.emit(true) assertThat(usingKeyguardGoingAwayAnimation).isTrue() - sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone) + kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone)) assertThat(usingKeyguardGoingAwayAnimation).isTrue() - sceneTransitions.value = goneToLs + kosmos.setSceneTransition(goneToLs) assertThat(usingKeyguardGoingAwayAnimation).isTrue() surfaceBehindIsAnimatingFlow.emit(false) @@ -1003,11 +1049,6 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { companion object { private val progress = MutableStateFlow(0f) - private val sceneTransitions = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(Scenes.Lockscreen) - ) - private val lsToGone = ObservableTransitionState.Transition( Scenes.Lockscreen, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt index 5c4b7432e18d..62cc76345c87 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt @@ -32,12 +32,11 @@ import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository -import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.flags.EnableSceneContainer +import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.power.data.repository.fakePowerRepository @@ -48,7 +47,6 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge import com.android.systemui.shade.data.repository.shadeRepository -import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.math.pow @@ -69,12 +67,13 @@ import platform.test.runner.parameterized.Parameters class LockscreenUserActionsViewModelTest : SysuiTestCase() { companion object { - private const val parameterCount = 6 + private const val parameterCount = 7 @Parameters( name = "canSwipeToEnter={0}, downWithTwoPointers={1}, downFromEdge={2}," + - " isSingleShade={3}, isCommunalAvailable={4}, isShadeTouchable={5}" + " isSingleShade={3}, isCommunalAvailable={4}, isShadeTouchable={5}," + + " isOccluded={6}" ) @JvmStatic fun combinations() = buildList { @@ -87,6 +86,7 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { /* isSingleShade= */ combination and 8 != 0, /* isCommunalAvailable= */ combination and 16 != 0, /* isShadeTouchable= */ combination and 32 != 0, + /* isOccluded= */ combination and 64 != 0, ) .also { check(it.size == parameterCount) } ) @@ -116,10 +116,12 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { downFromEdge: Boolean, isNarrowScreen: Boolean, isShadeTouchable: Boolean, + isOccluded: Boolean, ): SceneKey? { return when { !isShadeTouchable -> null - downFromEdge && isNarrowScreen -> Scenes.QuickSettings + downFromEdge && isNarrowScreen && !isOccluded -> Scenes.QuickSettings + downFromEdge && isNarrowScreen && isOccluded -> null else -> Scenes.Shade } } @@ -168,8 +170,9 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { @JvmField @Parameter(3) var isNarrowScreen: Boolean = true @JvmField @Parameter(4) var isCommunalAvailable: Boolean = false @JvmField @Parameter(5) var isShadeTouchable: Boolean = false + @JvmField @Parameter(6) var isOccluded: Boolean = false - private val underTest by lazy { createLockscreenSceneViewModel() } + private val underTest by lazy { kosmos.lockscreenUserActionsViewModel } @Test @EnableFlags(Flags.FLAG_COMMUNAL_HUB) @@ -196,6 +199,7 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { WakefulnessState.ASLEEP } ) + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = isOccluded) val userActions by collectLastValue(underTest.actions) val downDestination = @@ -217,6 +221,7 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { downFromEdge = downFromEdge, isNarrowScreen = isNarrowScreen, isShadeTouchable = isShadeTouchable, + isOccluded = isOccluded, ) ) @@ -285,6 +290,7 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { WakefulnessState.ASLEEP } ) + kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = isOccluded) val userActions by collectLastValue(underTest.actions) @@ -354,12 +360,4 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { ) ) } - - private fun createLockscreenSceneViewModel(): LockscreenUserActionsViewModel { - return LockscreenUserActionsViewModel( - deviceEntryInteractor = kosmos.deviceEntryInteractor, - communalInteractor = kosmos.communalInteractor, - shadeInteractor = kosmos.shadeInteractor, - ) - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt index 02825a55923f..ff00bfb540c6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.mediaprojection.data.repository import android.hardware.display.displayManager import android.media.projection.MediaProjectionInfo +import android.media.projection.StopReason import android.os.Binder import android.os.Handler import android.os.UserHandle @@ -339,8 +340,9 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { @Test fun stopProjecting_invokesManager() = testScope.runTest { - repo.stopProjecting() + repo.stopProjecting(StopReason.STOP_QS_TILE) - verify(fakeMediaProjectionManager.mediaProjectionManager).stopActiveProjection() + verify(fakeMediaProjectionManager.mediaProjectionManager) + .stopActiveProjection(StopReason.STOP_QS_TILE) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java index 646722bee35f..2905a7329d21 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java @@ -116,7 +116,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.data.repository.LightBarControllerStore; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LightBarController; @@ -209,7 +208,7 @@ public class NavigationBarTest extends SysuiTestCase { @Mock private LightBarController mLightBarController; @Mock - private LightBarControllerStore mLightBarControllerStore; + private LightBarController.Factory mLightBarcontrollerFactory; @Mock private AutoHideController mAutoHideController; @Mock @@ -258,7 +257,7 @@ public class NavigationBarTest extends SysuiTestCase { public void setup() throws Exception { MockitoAnnotations.initMocks(this); - when(mLightBarControllerStore.forDisplay(anyInt())).thenReturn(mLightBarController); + when(mLightBarcontrollerFactory.create(any(Context.class))).thenReturn(mLightBarController); when(mAutoHideControllerFactory.create(any(Context.class))).thenReturn(mAutoHideController); when(mNavigationBarView.getHomeButton()).thenReturn(mHomeButton); when(mNavigationBarView.getRecentsButton()).thenReturn(mRecentsButton); @@ -650,7 +649,8 @@ public class NavigationBarTest extends SysuiTestCase { mFakeExecutor, mUiEventLogger, mNavBarHelper, - mLightBarControllerStore, + mLightBarController, + mLightBarcontrollerFactory, mAutoHideController, mAutoHideControllerFactory, Optional.of(mTelecomManager), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt index 4acf3ee7878b..645efae16b8b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt @@ -27,7 +27,7 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class MutableSelectionStateTest : SysuiTestCase() { - private val underTest = MutableSelectionState({}, {}) + private val underTest = MutableSelectionState() @Test fun selectTile_isCorrectlySelected() { @@ -48,120 +48,6 @@ class MutableSelectionStateTest : SysuiTestCase() { assertThat(underTest.selection?.manual).isFalse() } - @Test - fun startResize_createsResizingState() { - assertThat(underTest.resizingState).isNull() - - // Resizing starts but no tile is selected - underTest.onResizingDragStart(TileWidths(0, 0, 1)) - assertThat(underTest.resizingState).isNull() - - // Resizing starts with a selected tile - underTest.select(TEST_SPEC, manual = true) - underTest.onResizingDragStart(TileWidths(0, 0, 1)) - - assertThat(underTest.resizingState).isNotNull() - } - - @Test - fun endResize_clearsResizingState() { - val spec = TileSpec.create("testSpec") - - // Resizing starts with a selected tile - underTest.select(spec, manual = true) - underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10)) - assertThat(underTest.resizingState).isNotNull() - - underTest.onResizingDragEnd() - assertThat(underTest.resizingState).isNull() - } - - @Test - fun unselect_clearsResizingState() { - // Resizing starts with a selected tile - underTest.select(TEST_SPEC, manual = true) - underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10)) - assertThat(underTest.resizingState).isNotNull() - - underTest.unSelect() - assertThat(underTest.resizingState).isNull() - } - - @Test - fun onResizingDrag_updatesResizingState() { - // Resizing starts with a selected tile - underTest.select(TEST_SPEC, manual = true) - underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10)) - assertThat(underTest.resizingState).isNotNull() - - underTest.onResizingDrag(5f) - assertThat(underTest.resizingState?.width).isEqualTo(5) - - underTest.onResizingDrag(2f) - assertThat(underTest.resizingState?.width).isEqualTo(7) - - underTest.onResizingDrag(-6f) - assertThat(underTest.resizingState?.width).isEqualTo(1) - } - - @Test - fun onResizingDrag_receivesResizeCallback() { - var resized = false - val onResize: (TileSpec) -> Unit = { - assertThat(it).isEqualTo(TEST_SPEC) - resized = !resized - } - val underTest = MutableSelectionState(onResize = onResize, {}) - - // Resizing starts with a selected tile - underTest.select(TEST_SPEC, true) - underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10)) - assertThat(underTest.resizingState).isNotNull() - - // Drag under the threshold - underTest.onResizingDrag(1f) - assertThat(resized).isFalse() - - // Drag over the threshold - underTest.onResizingDrag(5f) - assertThat(resized).isTrue() - - // Drag back under the threshold - underTest.onResizingDrag(-5f) - assertThat(resized).isFalse() - } - - @Test - fun onResizingEnded_receivesResizeEndCallback() { - var resizeEnded = false - val onResizeEnd: (TileSpec) -> Unit = { resizeEnded = true } - val underTest = MutableSelectionState({}, onResizeEnd = onResizeEnd) - - // Resizing starts with a selected tile - underTest.select(TEST_SPEC, true) - underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10)) - - underTest.onResizingDragEnd() - assertThat(resizeEnded).isTrue() - } - - @Test - fun onResizingEnded_setsSelectionAutomatically() { - val underTest = MutableSelectionState({}, {}) - - // Resizing starts with a selected tile - underTest.select(TEST_SPEC, manual = true) - underTest.onResizingDragStart(TileWidths(base = 0, min = 0, max = 10)) - - // Assert the selection was manual - assertThat(underTest.selection?.manual).isTrue() - - underTest.onResizingDragEnd() - - // Assert the selection is no longer manual due to the resizing - assertThat(underTest.selection?.manual).isFalse() - } - companion object { private val TEST_SPEC = TileSpec.create("testSpec") } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingStateTest.kt index 6e66783da44e..2206f4dcf5a8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingStateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingStateTest.kt @@ -19,7 +19,9 @@ package com.android.systemui.qs.panels.ui.compose.selection import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.qs.pipeline.shared.TileSpec import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -27,36 +29,32 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ResizingStateTest : SysuiTestCase() { - @Test - fun drag_updatesStateCorrectly() { - var resized = false - val underTest = - ResizingState(TileWidths(base = 0, min = 0, max = 10)) { resized = !resized } - - assertThat(underTest.width).isEqualTo(0) + private val underTest = + ResizingState(TileSpec.create("a"), startsAsIcon = true).apply { updateAnchors(10f, 20f) } - underTest.onDrag(2f) - assertThat(underTest.width).isEqualTo(2) - - underTest.onDrag(1f) - assertThat(underTest.width).isEqualTo(3) - assertThat(resized).isTrue() + @Test + fun newResizingState_setInitialValueCorrectly() { + assertThat(underTest.anchoredDraggableState.currentValue).isEqualTo(QSDragAnchor.Icon) + } - underTest.onDrag(-1f) - assertThat(underTest.width).isEqualTo(2) - assertThat(resized).isFalse() + @Test + fun updateAnchors_setBoundsCorrectly() { + assertThat(underTest.bounds).isEqualTo(10f to 20f) } @Test - fun dragOutOfBounds_isClampedCorrectly() { - val underTest = ResizingState(TileWidths(base = 0, min = 0, max = 10)) {} + fun dragOverThreshold_resizesToLarge() = runTest { + underTest.anchoredDraggableState.anchoredDrag { dragTo(16f) } - assertThat(underTest.width).isEqualTo(0) + assertThat(underTest.temporaryResizeOperation.spec).isEqualTo(TileSpec.create("a")) + assertThat(underTest.temporaryResizeOperation.toIcon).isFalse() + } - underTest.onDrag(100f) - assertThat(underTest.width).isEqualTo(10) + @Test + fun dragUnderThreshold_staysIcon() = runTest { + underTest.anchoredDraggableState.anchoredDrag { dragTo(12f) } - underTest.onDrag(-200f) - assertThat(underTest.width).isEqualTo(0) + assertThat(underTest.temporaryResizeOperation.spec).isEqualTo(TileSpec.create("a")) + assertThat(underTest.temporaryResizeOperation.toIcon).isTrue() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt new file mode 100644 index 000000000000..98770c724126 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.ui.viewmodel + + +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.kosmos.testScope +import com.android.systemui.qs.FakeQSTile +import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository +import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class DetailsViewModelTest : SysuiTestCase() { + private val kosmos = testKosmos() + private lateinit var underTest: DetailsViewModel + private val spec = TileSpec.create("internet") + private val specNoDetails = TileSpec.create("NoDetailsTile") + + @Before + fun setUp() { + underTest = kosmos.detailsViewModel + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun changeTileDetailsViewModel() = with(kosmos) { + testScope.runTest { + val specs = listOf( + spec, + specNoDetails, + ) + tileSpecRepository.setTiles(0, specs) + runCurrent() + + val tiles = currentTilesInteractor.currentTiles.value + + assertThat(currentTilesInteractor.currentTilesSpecs.size).isEqualTo(2) + assertThat(tiles!![1].spec).isEqualTo(specNoDetails) + (tiles!![1].tile as FakeQSTile).hasDetailsViewModel = false + + assertThat(underTest.activeTileDetails).isNull() + + // Click on the tile who has the `spec`. + assertThat(underTest.onTileClicked(spec)).isTrue() + assertThat(underTest.activeTileDetails).isNotNull() + assertThat(underTest.activeTileDetails?.getTitle()).isEqualTo("internet") + + // Click on a tile who dose not have a valid spec. + assertThat(underTest.onTileClicked(null)).isFalse() + assertThat(underTest.activeTileDetails).isNull() + + // Click again on the tile who has the `spec`. + assertThat(underTest.onTileClicked(spec)).isTrue() + assertThat(underTest.activeTileDetails).isNotNull() + assertThat(underTest.activeTileDetails?.getTitle()).isEqualTo("internet") + + // Click on a tile who dose not have a detailed view. + assertThat(underTest.onTileClicked(specNoDetails)).isFalse() + assertThat(underTest.activeTileDetails).isNull() + + underTest.closeDetailedView() + assertThat(underTest.activeTileDetails).isNull() + + assertThat(underTest.onTileClicked(null)).isFalse() + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt new file mode 100644 index 000000000000..f2bfd729f74a --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt @@ -0,0 +1,63 @@ +/* + * 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.qs.panels.ui.viewmodel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.fakeFalsingManager +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runCurrent +import com.android.systemui.kosmos.runTest +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class EditModeButtonViewModelTest : SysuiTestCase() { + val kosmos = testKosmos() + + val underTest = kosmos.editModeButtonViewModelFactory.create() + + @Test + fun falsingFalseTap_editModeDoesntStart() = + kosmos.runTest { + val isEditing by collectLastValue(editModeViewModel.isEditing) + + fakeFalsingManager.setFalseTap(true) + + underTest.onButtonClick() + runCurrent() + + assertThat(isEditing).isFalse() + } + + @Test + fun falsingNotFalseTap_editModeStarted() = + kosmos.runTest { + val isEditing by collectLastValue(editModeViewModel.isEditing) + + fakeFalsingManager.setFalseTap(false) + + underTest.onButtonClick() + runCurrent() + + assertThat(isEditing).isTrue() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt index 8995f46e7a72..165ff7bf08b1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt @@ -691,6 +691,32 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { verify(logger, never()).logTileUserChanged(TileSpec.create("a"), 0) } + + @Test + fun getTileDetails() = + testScope.runTest(USER_INFO_0) { + val tiles by collectLastValue(underTest.currentTiles) + val tileA = TileSpec.create("a") + val tileB = TileSpec.create("b") + val tileNoDetails = TileSpec.create("NoDetails") + + val specs = listOf(tileA, tileB, tileNoDetails) + + assertThat(tiles!!.isEmpty()).isTrue() + + tileSpecRepository.setTiles(USER_INFO_0.id, specs) + assertThat(tiles!!.size).isEqualTo(3) + + // The third tile doesn't have a details view. + assertThat(tiles!![2].spec).isEqualTo(tileNoDetails) + (tiles!![2].tile as FakeQSTile).hasDetailsViewModel = false + + assertThat(tiles!![0].tile.detailsViewModel.getTitle()).isEqualTo("a") + assertThat(tiles!![1].tile.detailsViewModel.getTitle()).isEqualTo("b") + assertThat(tiles!![2].tile.detailsViewModel).isNull() + } + + private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) { this.state = state this.label = label @@ -770,7 +796,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { private val USER_INFO_0 = UserInfo().apply { id = 0 } private val USER_INFO_1 = UserInfo().apply { id = 1 } - private val VALID_TILES = setOf("a", "b", "c", "d", "e") + private val VALID_TILES = setOf("a", "b", "c", "d", "e", "NoDetails") private val TEST_COMPONENT = ComponentName("pkg", "cls") private val CUSTOM_TILE_SPEC = TileSpec.Companion.create(TEST_COMPONENT) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/DisabledContentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/DisabledContentInteractorTest.kt new file mode 100644 index 000000000000..08225a7770d2 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/DisabledContentInteractorTest.kt @@ -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.systemui.scene.domain.interactor + +import android.app.StatusBarManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.Swipe +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest +import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.scene.shared.model.Overlays +import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository +import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DisabledContentInteractorTest : SysuiTestCase() { + + private val kosmos = testKosmos().useUnconfinedTestDispatcher() + + private val underTest = kosmos.disabledContentInteractor + + @Test + fun isDisabled_notificationsShade() = + kosmos.runTest { + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_NONE) + assertThat(underTest.isDisabled(Overlays.NotificationsShade)).isFalse() + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_NOTIFICATION_SHADE) + assertThat(underTest.isDisabled(Overlays.NotificationsShade)).isTrue() + } + + @Test + fun isDisabled_qsShade() = + kosmos.runTest { + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_NONE) + assertThat(underTest.isDisabled(Overlays.QuickSettingsShade)).isFalse() + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_QUICK_SETTINGS) + assertThat(underTest.isDisabled(Overlays.QuickSettingsShade)).isTrue() + } + + @Test + fun repeatWhenDisabled() = + kosmos.runTest { + var notificationDisabledCount = 0 + applicationCoroutineScope.launch { + underTest.repeatWhenDisabled(Overlays.NotificationsShade) { + notificationDisabledCount++ + } + } + var qsDisabledCount = 0 + applicationCoroutineScope.launch { + underTest.repeatWhenDisabled(Overlays.QuickSettingsShade) { qsDisabledCount++ } + } + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_QUICK_SETTINGS) + assertThat(notificationDisabledCount).isEqualTo(0) + assertThat(qsDisabledCount).isEqualTo(1) + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = + StatusBarManager.DISABLE2_NOTIFICATION_SHADE or + StatusBarManager.DISABLE2_QUICK_SETTINGS + ) + assertThat(notificationDisabledCount).isEqualTo(1) + assertThat(qsDisabledCount).isEqualTo(1) + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_NOTIFICATION_SHADE) + assertThat(notificationDisabledCount).isEqualTo(1) + assertThat(qsDisabledCount).isEqualTo(1) + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_QUICK_SETTINGS) + assertThat(notificationDisabledCount).isEqualTo(1) + assertThat(qsDisabledCount).isEqualTo(2) + } + + @Test + fun filteredUserActions() = + kosmos.runTest { + val map = + mapOf<UserAction, UserActionResult>( + Swipe.Up to UserActionResult.ShowOverlay(Overlays.NotificationsShade), + Swipe.Down to UserActionResult.ShowOverlay(Overlays.QuickSettingsShade), + ) + val unfiltered = MutableStateFlow(map) + val filtered by collectLastValue(underTest.filteredUserActions(unfiltered)) + assertThat(filtered).isEqualTo(map) + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_NOTIFICATION_SHADE) + assertThat(filtered) + .isEqualTo( + mapOf(Swipe.Down to UserActionResult.ShowOverlay(Overlays.QuickSettingsShade)) + ) + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_QUICK_SETTINGS) + assertThat(filtered) + .isEqualTo( + mapOf(Swipe.Up to UserActionResult.ShowOverlay(Overlays.NotificationsShade)) + ) + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel( + disable2 = + StatusBarManager.DISABLE2_NOTIFICATION_SHADE or + StatusBarManager.DISABLE2_QUICK_SETTINGS + ) + assertThat(filtered).isEqualTo(emptyMap<UserAction, UserActionResult>()) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index 7fe3d8d08afa..48edded5df18 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene.domain.interactor +import android.app.StatusBarManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState @@ -30,6 +31,8 @@ import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.scene.data.repository.Idle import com.android.systemui.scene.data.repository.Transition @@ -43,6 +46,8 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.SceneFamilies import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource +import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository +import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -523,4 +528,51 @@ class SceneInteractorTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(Scenes.Gone) } + + @Test + fun showOverlay_overlayDisabled_doesNothing() = + kosmos.runTest { + val currentOverlays by collectLastValue(underTest.currentOverlays) + val disabledOverlay = Overlays.QuickSettingsShade + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_QUICK_SETTINGS) + assertThat(disabledContentInteractor.isDisabled(disabledOverlay)).isTrue() + assertThat(currentOverlays).doesNotContain(disabledOverlay) + + underTest.showOverlay(disabledOverlay, "reason") + + assertThat(currentOverlays).doesNotContain(disabledOverlay) + } + + @Test + fun replaceOverlay_withDisabledOverlay_doesNothing() = + kosmos.runTest { + val currentOverlays by collectLastValue(underTest.currentOverlays) + val showingOverlay = Overlays.NotificationsShade + underTest.showOverlay(showingOverlay, "reason") + assertThat(currentOverlays).isEqualTo(setOf(showingOverlay)) + val disabledOverlay = Overlays.QuickSettingsShade + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_QUICK_SETTINGS) + assertThat(disabledContentInteractor.isDisabled(disabledOverlay)).isTrue() + + underTest.replaceOverlay(showingOverlay, disabledOverlay, "reason") + + assertThat(currentOverlays).isEqualTo(setOf(showingOverlay)) + } + + @Test + fun changeScene_toDisabledScene_doesNothing() = + kosmos.runTest { + val currentScene by collectLastValue(underTest.currentScene) + val disabledScene = Scenes.Shade + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_NOTIFICATION_SHADE) + assertThat(disabledContentInteractor.isDisabled(disabledScene)).isTrue() + assertThat(currentScene).isNotEqualTo(disabledScene) + + underTest.changeScene(disabledScene, "reason") + + assertThat(currentScene).isNotEqualTo(disabledScene) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt index db2297c99315..3d014b6822b4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt @@ -35,9 +35,9 @@ import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.init.NotificationsController import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index cca847effe94..79bb0c401e78 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -81,6 +81,9 @@ import com.android.systemui.keyguard.domain.interactor.scenetransition.lockscree import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runCurrent +import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.model.sysUiState import com.android.systemui.power.data.repository.fakePowerRepository @@ -101,6 +104,8 @@ import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.shared.system.QuickStepContract import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository +import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository @@ -2673,6 +2678,25 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(isAlternateBouncerVisible).isFalse() } + @Test + fun handleDisableFlags() = + kosmos.runTest { + underTest.start() + val currentScene by collectLastValue(sceneInteractor.currentScene) + val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) + sceneInteractor.changeScene(Scenes.Shade, "reason") + sceneInteractor.showOverlay(Overlays.NotificationsShade, "reason") + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(currentOverlays).contains(Overlays.NotificationsShade) + + fakeDisableFlagsRepository.disableFlags.value = + DisableFlagsModel(disable2 = StatusBarManager.DISABLE2_NOTIFICATION_SHADE) + runCurrent() + + assertThat(currentScene).isNotEqualTo(Scenes.Shade) + assertThat(currentOverlays).isEmpty() + } + private fun TestScope.emulateSceneTransition( transitionStateFlow: MutableStateFlow<ObservableTransitionState>, toScene: SceneKey, @@ -2837,9 +2861,7 @@ class SceneContainerStartableTest : SysuiTestCase() { ) private fun fakeHeadsUpRowRepository(key: String, isPinned: Boolean) = - FakeHeadsUpRowRepository(key = key, elementKey = Any()).apply { - this.isPinned.value = isPinned - } + FakeHeadsUpRowRepository(key = key, elementKey = Any(), isPinned = isPinned) private fun setFingerprintSensorType(fingerprintSensorType: FingerprintSensorType) { kosmos.fingerprintPropertyRepository.setProperties( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index ed31f366e1d0..d720e1a6595d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -150,7 +150,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.ConversationNotificationManager; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger; import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository; @@ -183,7 +183,7 @@ import com.android.systemui.statusbar.phone.TapAgainViewController; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt index 0f476d0a0455..c6ce58185cf0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt @@ -36,10 +36,10 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.row.NotificationGutsManager import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.statusbar.window.StatusBarWindowControllerStore diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt new file mode 100644 index 000000000000..096675962d80 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt @@ -0,0 +1,89 @@ +/* + * 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.shade.data.repository + +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.kosmos.testScope +import com.android.systemui.shade.display.ShadeDisplayPolicy +import com.android.systemui.shade.display.SpecificDisplayIdPolicy +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ShadeDisplaysRepositoryTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val defaultPolicy = SpecificDisplayIdPolicy(0) + + private val shadeDisplaysRepository = + ShadeDisplaysRepositoryImpl(defaultPolicy, testScope.backgroundScope) + + @Test + fun policy_changing_propagatedFromTheLatestPolicy() = + testScope.runTest { + val displayIds by collectValues(shadeDisplaysRepository.displayId) + val policy1 = MutablePolicy() + val policy2 = MutablePolicy() + + assertThat(displayIds).containsExactly(0) + + shadeDisplaysRepository.policy.value = policy1 + + policy1.sendDisplayId(1) + + assertThat(displayIds).containsExactly(0, 1) + + policy1.sendDisplayId(2) + + assertThat(displayIds).containsExactly(0, 1, 2) + + shadeDisplaysRepository.policy.value = policy2 + + assertThat(displayIds).containsExactly(0, 1, 2, 0) + + policy1.sendDisplayId(4) + + // Changes to the first policy don't affect the output now + assertThat(displayIds).containsExactly(0, 1, 2, 0) + + policy2.sendDisplayId(5) + + assertThat(displayIds).containsExactly(0, 1, 2, 0, 5) + } + + private class MutablePolicy : ShadeDisplayPolicy { + fun sendDisplayId(id: Int) { + _displayId.value = id + } + + private val _displayId = MutableStateFlow(0) + override val name: String + get() = "mutable_policy" + + override val displayId: StateFlow<Int> + get() = _displayId + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt index af01547be7e3..d584dc9ceef2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt @@ -23,12 +23,17 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.kosmos.testScope +import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.shade.ShadePrimaryDisplayCommand +import com.android.systemui.shade.display.ShadeDisplayPolicy import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.testKosmos +import com.google.common.truth.StringSubject import com.google.common.truth.Truth.assertThat import java.io.PrintWriter import java.io.StringWriter +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -37,15 +42,26 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class ShadePrimaryDisplayCommandTest : SysuiTestCase() { - private val kosmos = testKosmos() + private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val testScope = kosmos.testScope private val commandRegistry = kosmos.commandRegistry private val displayRepository = kosmos.displayRepository - private val shadeDisplaysRepository = ShadeDisplaysRepositoryImpl() + private val defaultPolicy = kosmos.defaultShadeDisplayPolicy + private val policy1 = makePolicy("policy_1") + private val shadeDisplaysRepository = kosmos.shadeDisplaysRepository private val pw = PrintWriter(StringWriter()) + private val policies = + setOf(defaultPolicy, policy1, makePolicy("policy_2"), makePolicy("policy_3")) + private val underTest = - ShadePrimaryDisplayCommand(commandRegistry, displayRepository, shadeDisplaysRepository) + ShadePrimaryDisplayCommand( + commandRegistry, + displayRepository, + shadeDisplaysRepository, + policies, + defaultPolicy, + ) @Before fun setUp() { @@ -96,4 +112,41 @@ class ShadePrimaryDisplayCommandTest : SysuiTestCase() { assertThat(displayId).isEqualTo(newDisplayId) } + + @Test + fun policies_listsAllPolicies() = + testScope.runTest { + val stringWriter = StringWriter() + commandRegistry.onShellCommand( + PrintWriter(stringWriter), + arrayOf("shade_display_override", "policies"), + ) + val result = stringWriter.toString() + + assertThat(result).containsAllIn(policies.map { it.name }) + } + + @Test + fun policies_setsSpecificPolicy() = + testScope.runTest { + val policy by collectLastValue(shadeDisplaysRepository.policy) + + commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", policy1.name)) + + assertThat(policy!!.name).isEqualTo(policy1.name) + } + + private fun makePolicy(policyName: String): ShadeDisplayPolicy { + return object : ShadeDisplayPolicy { + override val name: String + get() = policyName + + override val displayId: StateFlow<Int> + get() = MutableStateFlow(0) + } + } +} + +private fun StringSubject.containsAllIn(strings: List<String>) { + strings.forEach { contains(it) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicyTest.kt new file mode 100644 index 000000000000..4d4efd11f3a8 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicyTest.kt @@ -0,0 +1,84 @@ +/* + * 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.shade.display + +import android.view.Display +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.display.data.repository.display +import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.kosmos.testScope +import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.test.runTest +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class AnyExternalShadeDisplayPolicyTest : SysuiTestCase() { + private val kosmos = testKosmos().useUnconfinedTestDispatcher() + private val testScope = kosmos.testScope + private val displayRepository = kosmos.displayRepository + val underTest = AnyExternalShadeDisplayPolicy(displayRepository, testScope.backgroundScope) + + @Test + fun displayId_ignoresUnwantedTypes() = + testScope.runTest { + val displayId by collectLastValue(underTest.displayId) + + displayRepository.addDisplays( + display(id = 0, type = Display.TYPE_INTERNAL), + display(id = 1, type = Display.TYPE_UNKNOWN), + display(id = 2, type = Display.TYPE_VIRTUAL), + display(id = 3, type = Display.TYPE_EXTERNAL), + ) + + assertThat(displayId).isEqualTo(3) + } + + @Test + fun displayId_onceRemoved_goesToNextDisplay() = + testScope.runTest { + val displayId by collectLastValue(underTest.displayId) + + displayRepository.addDisplays( + display(id = 0, type = Display.TYPE_INTERNAL), + display(id = 2, type = Display.TYPE_EXTERNAL), + display(id = 3, type = Display.TYPE_EXTERNAL), + ) + + assertThat(displayId).isEqualTo(2) + + displayRepository.removeDisplay(2) + + assertThat(displayId).isEqualTo(3) + } + + @Test + fun displayId_onlyDefaultDisplay_defaultDisplay() = + testScope.runTest { + val displayId by collectLastValue(underTest.displayId) + + displayRepository.addDisplays(display(id = 0, type = Display.TYPE_INTERNAL)) + + assertThat(displayId).isEqualTo(0) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt index 8ef1e568cd58..982c51b8318c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorTest.kt @@ -29,7 +29,7 @@ import com.android.systemui.display.data.repository.FakeDisplayWindowPropertiesR import com.android.systemui.display.shared.model.DisplayWindowProperties import com.android.systemui.scene.ui.view.WindowRootView import com.android.systemui.shade.data.repository.FakeShadeDisplayRepository -import com.android.systemui.statusbar.phone.ConfigurationForwarder +import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -37,6 +37,7 @@ import kotlinx.coroutines.test.advanceUntilIdle import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.inOrder import org.mockito.Mockito.verify import org.mockito.kotlin.any import org.mockito.kotlin.eq @@ -51,26 +52,22 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { private val shadeRootview = mock<WindowRootView>() private val positionRepository = FakeShadeDisplayRepository() - private val defaultContext = mock<Context>() - private val secondaryContext = mock<Context>() + private val shadeContext = mock<Context>() private val contextStore = FakeDisplayWindowPropertiesRepository() private val testScope = TestScope(UnconfinedTestDispatcher()) - private val configurationForwarder = mock<ConfigurationForwarder>() - private val defaultWm = mock<WindowManager>() - private val secondaryWm = mock<WindowManager>() + private val shadeWm = mock<WindowManager>() private val resources = mock<Resources>() private val configuration = mock<Configuration>() private val display = mock<Display>() private val interactor = ShadeDisplaysInteractor( - shadeRootview, + Optional.of(shadeRootview), positionRepository, - defaultContext, - contextStore, - testScope, - configurationForwarder, - testScope.coroutineContext, + shadeContext, + shadeWm, + testScope.backgroundScope, + testScope.backgroundScope.coroutineContext, ) @Before @@ -79,30 +76,16 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { whenever(display.displayId).thenReturn(0) whenever(resources.configuration).thenReturn(configuration) - whenever(resources.configuration).thenReturn(configuration) - whenever(defaultContext.displayId).thenReturn(0) - whenever(defaultContext.getSystemService(any())).thenReturn(defaultWm) - whenever(defaultContext.resources).thenReturn(resources) + whenever(shadeContext.displayId).thenReturn(0) + whenever(shadeContext.getSystemService(any())).thenReturn(shadeWm) + whenever(shadeContext.resources).thenReturn(resources) contextStore.insert( DisplayWindowProperties( displayId = 0, windowType = TYPE_NOTIFICATION_SHADE, - context = defaultContext, - windowManager = defaultWm, - layoutInflater = mock(), - ) - ) - - whenever(secondaryContext.displayId).thenReturn(1) - whenever(secondaryContext.getSystemService(any())).thenReturn(secondaryWm) - whenever(secondaryContext.resources).thenReturn(resources) - contextStore.insert( - DisplayWindowProperties( - displayId = 1, - windowType = TYPE_NOTIFICATION_SHADE, - context = secondaryContext, - windowManager = secondaryWm, + context = shadeContext, + windowManager = shadeWm, layoutInflater = mock(), ) ) @@ -115,8 +98,7 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { interactor.start() testScope.advanceUntilIdle() - verifyNoMoreInteractions(defaultWm) - verifyNoMoreInteractions(secondaryWm) + verifyNoMoreInteractions(shadeWm) } @Test @@ -124,10 +106,11 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { whenever(display.displayId).thenReturn(0) positionRepository.setDisplayId(1) interactor.start() - testScope.advanceUntilIdle() - verify(defaultWm).removeView(eq(shadeRootview)) - verify(secondaryWm).addView(eq(shadeRootview), any()) + inOrder(shadeWm).apply { + verify(shadeWm).removeView(eq(shadeRootview)) + verify(shadeWm).addView(eq(shadeRootview), any()) + } } @Test @@ -135,25 +118,12 @@ class ShadeDisplaysInteractorTest : SysuiTestCase() { whenever(display.displayId).thenReturn(0) positionRepository.setDisplayId(0) interactor.start() - testScope.advanceUntilIdle() - - positionRepository.setDisplayId(1) - testScope.advanceUntilIdle() - - verify(defaultWm).removeView(eq(shadeRootview)) - verify(secondaryWm).addView(eq(shadeRootview), any()) - } - - @Test - fun start_shadePositionChanges_newConfigPropagated() { - whenever(display.displayId).thenReturn(0) - positionRepository.setDisplayId(0) - interactor.start() - testScope.advanceUntilIdle() positionRepository.setDisplayId(1) - testScope.advanceUntilIdle() - verify(configurationForwarder).onConfigurationChanged(eq(configuration)) + inOrder(shadeWm).apply { + verify(shadeWm).removeView(eq(shadeRootview)) + verify(shadeWm).addView(eq(shadeRootview), any()) + } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt index 558606f00300..a9d5790d1f08 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade.ui.viewmodel +import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS import android.platform.test.annotations.DisableFlags import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -42,6 +43,7 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.shade.shared.model.ShadeMode +import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository import com.android.systemui.testKosmos import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider import com.google.common.truth.Truth.assertThat @@ -49,6 +51,7 @@ import java.util.Locale import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.update import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -158,10 +161,7 @@ class ShadeSceneContentViewModelTest : SysuiTestCase() { underTest.unfoldTranslationX(isOnStartSide = true), underTest.unfoldTranslationX(isOnStartSide = false), ) { start, end -> - Translations( - start = start, - end = end, - ) + Translations(start = start, end = end) } ) @@ -186,6 +186,20 @@ class ShadeSceneContentViewModelTest : SysuiTestCase() { assertThat(translations?.end).isEqualTo(-0f) } + @Test + fun disable2QuickSettings_isQsEnabledIsFalse() = + testScope.runTest { + val isQsEnabled by collectLastValue(underTest.isQsEnabled) + assertThat(isQsEnabled).isTrue() + + kosmos.fakeDisableFlagsRepository.disableFlags.update { + it.copy(disable2 = DISABLE2_QUICK_SETTINGS) + } + runCurrent() + + assertThat(isQsEnabled).isFalse() + } + private fun prepareConfiguration(): Int { val configuration = context.resources.configuration configuration.setLayoutDirection(Locale.US) @@ -193,7 +207,7 @@ class ShadeSceneContentViewModelTest : SysuiTestCase() { val maxTranslation = 10 kosmos.fakeConfigurationRepository.setDimensionPixelSize( R.dimen.notification_side_paddings, - maxTranslation + maxTranslation, ) return maxTranslation } @@ -224,8 +238,5 @@ class ShadeSceneContentViewModelTest : SysuiTestCase() { runCurrent() } - private data class Translations( - val start: Float, - val end: Float, - ) + private data class Translations(val start: Float, val end: Float) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt index 9907740672ed..cd66ef32180a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt @@ -26,10 +26,10 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.mockito.mock import kotlinx.coroutines.flow.MutableStateFlow import org.junit.Before @@ -76,7 +76,7 @@ class PulseExpansionHandlerTest : SysuiTestCase() { falsingManager, shadeInteractor, lockscreenShadeTransitionController, - dumpManager + dumpManager, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt index cb92b7745961..a1772e3f62ed 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt @@ -14,11 +14,11 @@ import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.HeadsUpUtil import com.android.systemui.testKosmos import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue @@ -69,7 +69,7 @@ class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { headsUpManager, notification, kosmos.interactionJankMonitor, - onFinishAnimationCallback + onFinishAnimationCallback, ) } @@ -95,7 +95,7 @@ class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { notificationKey, /* releaseImmediately= */ true, /* animate= */ true, - /* reason= */ "onIntentStarted(willAnimate=false)" + /* reason= */ "onIntentStarted(willAnimate=false)", ) verify(onFinishAnimationCallback).run() } @@ -118,7 +118,7 @@ class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { notificationKey, /* releaseImmediately= */ true, /* animate= */ true, - /* reason= */ "onLaunchAnimationCancelled()" + /* reason= */ "onLaunchAnimationCancelled()", ) verify(onFinishAnimationCallback).run() } @@ -141,7 +141,7 @@ class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { notificationKey, /* releaseImmediately= */ true, /* animate= */ false, - /* reason= */ "onLaunchAnimationEnd()" + /* reason= */ "onLaunchAnimationEnd()", ) verify(onFinishAnimationCallback).run() } @@ -180,14 +180,14 @@ class NotificationTransitionAnimatorControllerTest : SysuiTestCase() { summary.key, /* releaseImmediately= */ true, /* animate= */ false, - /* reason= */ "onLaunchAnimationEnd()" + /* reason= */ "onLaunchAnimationEnd()", ) verify(headsUpManager, never()) .removeNotification( notification.entry.key, /* releaseImmediately= */ true, /* animate= */ false, - /* reason= */ "onLaunchAnimationEnd()" + /* reason= */ "onLaunchAnimationEnd()", ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt index c4b1b841c6a5..0dc01a65df19 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt @@ -39,13 +39,13 @@ import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.ShadeViewController.Companion.WAKEUP_ANIMATION_DELAY_MS import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_WAKEUP import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.testKosmos import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java index 1f2925528077..544d20145db3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; +import static android.app.Notification.FLAG_PROMOTED_ONGOING; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_MIN; @@ -24,12 +25,18 @@ import static android.app.NotificationManager.IMPORTANCE_MIN; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + import android.app.Notification; import android.app.PendingIntent; import android.app.Person; import android.content.Intent; import android.graphics.Color; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -38,11 +45,14 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -136,6 +146,60 @@ public class ColorizedFgsCoordinatorTest extends SysuiTestCase { assertFalse(mFgsSection.isInSection(mEntryBuilder.build())); } + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME) + public void testIncludePromotedOngoingInSection_flagEnabled() { + // GIVEN the notification has FLAG_PROMOTED_ONGOING + mEntryBuilder.setFlag(mContext, FLAG_PROMOTED_ONGOING, true); + + // THEN the entry is in the fgs section + assertTrue(mFgsSection.isInSection(mEntryBuilder.build())); + } + + @Test + @DisableFlags(PromotedNotificationUi.FLAG_NAME) + public void testDiscludePromotedOngoingInSection_flagDisabled() { + // GIVEN the notification has FLAG_PROMOTED_ONGOING + mEntryBuilder.setFlag(mContext, FLAG_PROMOTED_ONGOING, true); + + // THEN the entry is NOT in the fgs section + assertFalse(mFgsSection.isInSection(mEntryBuilder.build())); + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME) + public void promoterSelectsPromotedOngoing_flagEnabled() { + ArgumentCaptor<NotifPromoter> captor = ArgumentCaptor.forClass(NotifPromoter.class); + verify(mNotifPipeline).addPromoter(captor.capture()); + NotifPromoter promoter = captor.getValue(); + + // GIVEN the notification has FLAG_PROMOTED_ONGOING + mEntryBuilder.setFlag(mContext, FLAG_PROMOTED_ONGOING, true); + + // THEN the entry is promoted to top level + assertTrue(promoter.shouldPromoteToTopLevel(mEntryBuilder.build())); + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME) + public void promoterIgnoresNonPromotedOngoing_flagEnabled() { + ArgumentCaptor<NotifPromoter> captor = ArgumentCaptor.forClass(NotifPromoter.class); + verify(mNotifPipeline).addPromoter(captor.capture()); + NotifPromoter promoter = captor.getValue(); + + // GIVEN the notification does not have FLAG_PROMOTED_ONGOING + mEntryBuilder.setFlag(mContext, FLAG_PROMOTED_ONGOING, false); + + // THEN the entry is NOT promoted to top level + assertFalse(promoter.shouldPromoteToTopLevel(mEntryBuilder.build())); + } + + @Test + @DisableFlags(PromotedNotificationUi.FLAG_NAME) + public void noPromoterAdded_flagDisabled() { + verify(mNotifPipeline, never()).addPromoter(any()); + } + private Notification.CallStyle makeCallStyle() { final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("action"), PendingIntent.FLAG_IMMUTABLE); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index 86a51912cc4f..1d7f25784327 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -38,12 +38,13 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.collection.mockNotifCollection -import com.android.systemui.statusbar.notification.collection.notifCollection import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider import com.android.systemui.statusbar.notification.collection.render.NodeController +import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.DecisionImpl @@ -51,8 +52,6 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback import com.android.systemui.statusbar.phone.NotificationGroupTestHelper -import com.android.systemui.statusbar.policy.BaseHeadsUpManager -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -103,7 +102,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { private val notifPipeline: NotifPipeline = mock() private val logger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true) - private val headsUpManager: BaseHeadsUpManager = mock() + private val headsUpManager: HeadsUpManagerImpl = mock() private val headsUpViewBinder: HeadsUpViewBinder = mock() private val visualInterruptionDecisionProvider: VisualInterruptionDecisionProvider = mock() private val remoteInputManager: NotificationRemoteInputManager = mock() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt index d772e3effbeb..14148cdc0f03 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinatorTest.kt @@ -21,7 +21,6 @@ import android.app.Notification import android.app.NotificationManager.IMPORTANCE_DEFAULT import android.app.NotificationManager.IMPORTANCE_LOW 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 @@ -43,6 +42,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.domain.interactor.lockScreenNotificationMinimalismSetting +import com.android.systemui.statusbar.notification.headsup.PinnedStatus import com.android.systemui.statusbar.notification.shared.NotificationMinimalism import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.testKosmos @@ -394,7 +394,7 @@ class LockScreenMinimalismCoordinatorTest : SysuiTestCase() { assertThatTopUnseenKey().isEqualTo(solo1.key) // TEST: even being pinned doesn't take effect immediately - hunRepo1.isPinned.value = true + hunRepo1.pinnedStatus.value = PinnedStatus.PinnedBySystem testScheduler.advanceTimeBy(0.5.seconds) onBeforeTransformGroupsListener.onBeforeTransformGroups(listEntryList) assertThatTopUnseenKey().isEqualTo(solo1.key) @@ -406,8 +406,8 @@ class LockScreenMinimalismCoordinatorTest : SysuiTestCase() { // TEST: repeat; being heads up and pinned for 1 second triggers seen kosmos.headsUpNotificationRepository.orderedHeadsUpRows.value = listOf(hunRepo2) - hunRepo1.isPinned.value = false - hunRepo2.isPinned.value = true + hunRepo1.pinnedStatus.value = PinnedStatus.NotPinned + hunRepo2.pinnedStatus.value = PinnedStatus.PinnedBySystem testScheduler.advanceTimeBy(1.seconds) onBeforeTransformGroupsListener.onBeforeTransformGroups(listEntryList) assertThatTopUnseenKey().isEqualTo(null) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt index 3fd9c2160ce2..d38fb5050c5f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt @@ -27,7 +27,6 @@ import com.android.systemui.flags.andSceneContainer import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.keyguardRepository -import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -48,9 +47,9 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor import com.android.systemui.statusbar.notification.domain.interactor.lockScreenShowOnlyUnseenNotificationsSetting import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener +import com.android.systemui.statusbar.notification.headsup.headsUpManager import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener -import com.android.systemui.statusbar.policy.headsUpManager import com.android.systemui.testKosmos import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.fakeSettings @@ -155,7 +154,7 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu runKeyguardCoordinatorTest { kosmos.setTransition( sceneTransition = Idle(Scenes.Gone), - stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE) + stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE), ) // WHEN: A notification is posted @@ -170,7 +169,7 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu keyguardRepository.setKeyguardShowing(true) kosmos.setTransition( sceneTransition = Idle(Scenes.Lockscreen), - stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD) + stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD), ) // THEN: The notification is recognized as "seen" and is filtered out. @@ -180,7 +179,7 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu keyguardRepository.setKeyguardShowing(false) kosmos.setTransition( sceneTransition = Idle(Scenes.Gone), - stateTransition = TransitionStep(KeyguardState.AOD, KeyguardState.GONE) + stateTransition = TransitionStep(KeyguardState.AOD, KeyguardState.GONE), ) // THEN: The notification is shown regardless @@ -359,14 +358,14 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu keyguardRepository.setKeyguardShowing(false) kosmos.setTransition( sceneTransition = Idle(Scenes.Gone), - stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE) + stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE), ) // WHEN: Keyguard is shown again keyguardRepository.setKeyguardShowing(true) kosmos.setTransition( sceneTransition = Idle(Scenes.Lockscreen), - stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD) + stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.AOD), ) // THEN: The notification is now recognized as "seen" and is filtered out. @@ -412,7 +411,7 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu runKeyguardCoordinatorTest { kosmos.setTransition( sceneTransition = Idle(Scenes.Lockscreen), - stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.LOCKSCREEN) + stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.LOCKSCREEN), ) val firstEntry = NotificationEntryBuilder().setId(1).build() collectionListener.onEntryAdded(firstEntry) @@ -435,14 +434,14 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu keyguardRepository.setKeyguardShowing(false) kosmos.setTransition( sceneTransition = Idle(Scenes.Gone), - stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE) + stateTransition = TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE), ) // WHEN: Keyguard is shown again keyguardRepository.setKeyguardShowing(true) kosmos.setTransition( sceneTransition = Idle(Scenes.Lockscreen), - stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.LOCKSCREEN) + stateTransition = TransitionStep(KeyguardState.GONE, KeyguardState.LOCKSCREEN), ) // THEN: The first notification is considered seen and is filtered out. diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java index 3ad41a54ac7e..ba85e32484df 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java @@ -67,7 +67,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; 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 d717fe4c1e04..dc0231f40609 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 @@ -31,6 +31,7 @@ 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.headsup.PinnedStatus 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 @@ -102,7 +103,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { } @Test - fun hasPinnedRows_rowGetsPinned_true() = + fun hasPinnedRows_rowGetsPinnedNormally_true() = testScope.runTest { val hasPinnedRows by collectLastValue(underTest.hasPinnedRows) // GIVEN no rows are pinned @@ -115,8 +116,30 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { headsUpRepository.setNotifications(rows) runCurrent() - // WHEN a row gets pinned - rows[0].isPinned.value = true + // WHEN a row gets pinned normally + rows[0].pinnedStatus.value = PinnedStatus.PinnedBySystem + runCurrent() + + // THEN hasPinnedRows updates to true + assertThat(hasPinnedRows).isTrue() + } + + @Test + fun hasPinnedRows_rowGetsPinnedByUser_true() = + testScope.runTest { + val hasPinnedRows by collectLastValue(underTest.hasPinnedRows) + // GIVEN no rows are pinned + val rows = + arrayListOf( + fakeHeadsUpRowRepository("key 0"), + fakeHeadsUpRowRepository("key 1"), + fakeHeadsUpRowRepository("key 2"), + ) + headsUpRepository.setNotifications(rows) + runCurrent() + + // WHEN a row gets pinned due to a chip tap + rows[0].pinnedStatus.value = PinnedStatus.PinnedByUser runCurrent() // THEN hasPinnedRows updates to true @@ -138,7 +161,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { runCurrent() // THEN that row gets unpinned - rows[0].isPinned.value = false + rows[0].pinnedStatus.value = PinnedStatus.NotPinned runCurrent() // THEN hasPinnedRows updates to false @@ -246,7 +269,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { runCurrent() // WHEN all rows gets pinned - rows[2].isPinned.value = true + rows[2].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() // THEN no rows are filtered @@ -271,7 +294,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { assertThat(activeHeadsUpRows).containsExactly(rows[0], rows[1], rows[2]) // WHEN all rows gets pinned - rows[2].isPinned.value = true + rows[2].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() // THEN no change @@ -329,7 +352,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { runCurrent() // WHEN a row gets unpinned - rows[0].isPinned.value = false + rows[0].pinnedStatus.value = PinnedStatus.NotPinned runCurrent() // THEN the unpinned row is filtered @@ -351,7 +374,7 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { runCurrent() // WHEN a row gets unpinned - rows[0].isPinned.value = false + rows[0].pinnedStatus.value = PinnedStatus.NotPinned runCurrent() // THEN all rows are still present @@ -372,15 +395,15 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { headsUpRepository.setNotifications(rows) runCurrent() - rows[0].isPinned.value = true + rows[0].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() assertThat(pinnedHeadsUpRows).containsExactly(rows[0]) - rows[0].isPinned.value = false + rows[0].pinnedStatus.value = PinnedStatus.NotPinned runCurrent() assertThat(pinnedHeadsUpRows).isEmpty() - rows[0].isPinned.value = true + rows[0].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() assertThat(pinnedHeadsUpRows).containsExactly(rows[0]) } @@ -485,7 +508,5 @@ class HeadsUpNotificationInteractorTest : SysuiTestCase() { } private fun fakeHeadsUpRowRepository(key: String, isPinned: Boolean = false) = - FakeHeadsUpRowRepository(key = key, elementKey = Any()).apply { - this.isPinned.value = isPinned - } + FakeHeadsUpRowRepository(key = key, isPinned = isPinned) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerTest.kt index c5eed7365d92..22a9c64d2cc9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerTest.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import android.app.Notification import android.os.Handler @@ -31,9 +31,11 @@ import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl +import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerTestUtil.createFullScreenIntentEntry import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun import com.android.systemui.statusbar.phone.keyguardBypassController -import com.android.systemui.statusbar.policy.HeadsUpManagerTestUtil.createFullScreenIntentEntry +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper +import com.android.systemui.statusbar.policy.configurationController import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.kotlin.JavaAdapter @@ -76,7 +78,7 @@ class AvalancheControllerTest : SysuiTestCase() { private val mGlobalSettings = FakeGlobalSettings() private val mSystemClock = FakeSystemClock() private val mExecutor = FakeExecutor(mSystemClock) - private lateinit var testableHeadsUpManager: BaseHeadsUpManager + private lateinit var testableHeadsUpManager: HeadsUpManagerImpl @Before fun setUp() { @@ -114,7 +116,7 @@ class AvalancheControllerTest : SysuiTestCase() { ) } - private fun createHeadsUpEntry(id: Int): BaseHeadsUpManager.HeadsUpEntry { + private fun createHeadsUpEntry(id: Int): HeadsUpManagerImpl.HeadsUpEntry { return testableHeadsUpManager.createHeadsUpEntry( NotificationEntryBuilder() .setSbn(HeadsUpManagerTestUtil.createSbn(id, Notification.Builder(mContext, ""))) @@ -122,7 +124,7 @@ class AvalancheControllerTest : SysuiTestCase() { ) } - private fun createFsiHeadsUpEntry(id: Int): BaseHeadsUpManager.HeadsUpEntry { + private fun createFsiHeadsUpEntry(id: Int): HeadsUpManagerImpl.HeadsUpEntry { return testableHeadsUpManager.createHeadsUpEntry(createFullScreenIntentEntry(id, mContext)) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.java index 0fbee6d29441..01f78cb289fd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED; @@ -54,18 +54,17 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.res.R; import com.android.systemui.shade.domain.interactor.ShadeInteractor; -import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.FakeGlobalSettings; import com.android.systemui.util.time.FakeSystemClock; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -73,6 +72,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import kotlinx.coroutines.flow.StateFlowKt; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; @@ -81,8 +81,8 @@ import java.util.List; @SmallTest @TestableLooper.RunWithLooper @RunWith(ParameterizedAndroidJunit4.class) -// TODO(b/378142453): Merge this with BaseHeadsUpManagerTest. -public class BaseHeadsUpManagerTest extends SysuiTestCase { +// TODO(b/378142453): Merge this with HeadsUpManagerPhoneTest. +public class HeadsUpManagerImplTest extends SysuiTestCase { protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); @Rule @@ -119,7 +119,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME); } - private BaseHeadsUpManager createHeadsUpManager() { + private HeadsUpManagerImpl createHeadsUpManager() { return new TestableHeadsUpManager( mContext, mLogger, @@ -169,7 +169,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { return FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME); } - public BaseHeadsUpManagerTest(FlagsParameterization flags) { + public HeadsUpManagerImplTest(FlagsParameterization flags) { mSetFlagsRule.setFlagsParameterization(flags); } @@ -184,7 +184,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testHasNotifications_headsUpManagerMapNotEmpty_true() { - final BaseHeadsUpManager bhum = createHeadsUpManager(); + final HeadsUpManagerImpl bhum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); bhum.showNotification(entry); @@ -195,10 +195,10 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) public void testHasNotifications_avalancheMapNotEmpty_true() { - final BaseHeadsUpManager bhum = createHeadsUpManager(); + final HeadsUpManagerImpl bhum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); mAvalancheController.addToNext(headsUpEntry, () -> {}); assertThat(mAvalancheController.getWaitingEntryList()).isNotEmpty(); @@ -208,7 +208,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) public void testHasNotifications_false() { - final BaseHeadsUpManager bhum = createHeadsUpManager(); + final HeadsUpManagerImpl bhum = createHeadsUpManager(); assertThat(bhum.mHeadsUpEntryMap).isEmpty(); assertThat(mAvalancheController.getWaitingEntryList()).isEmpty(); assertThat(bhum.hasNotifications()).isFalse(); @@ -217,10 +217,10 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) public void testGetHeadsUpEntryList_includesAvalancheEntryList() { - final BaseHeadsUpManager bhum = createHeadsUpManager(); + final HeadsUpManagerImpl bhum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); mAvalancheController.addToNext(headsUpEntry, () -> {}); assertThat(bhum.getHeadsUpEntryList()).contains(headsUpEntry); @@ -229,10 +229,10 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) public void testGetHeadsUpEntry_returnsAvalancheEntry() { - final BaseHeadsUpManager bhum = createHeadsUpManager(); + final HeadsUpManagerImpl bhum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry); mAvalancheController.addToNext(headsUpEntry, () -> {}); assertThat(bhum.getHeadsUpEntry(notifEntry.getKey())).isEqualTo(headsUpEntry); @@ -240,7 +240,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_addsEntry() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); alm.showNotification(entry); @@ -252,7 +252,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_autoDismisses() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); alm.showNotification(entry); @@ -263,7 +263,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testRemoveNotification_removeDeferred() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); alm.showNotification(entry); @@ -276,7 +276,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testRemoveNotification_forceRemove() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); alm.showNotification(entry); @@ -289,7 +289,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testReleaseAllImmediately() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) { final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(i, mContext); entry.setRow(mRow); @@ -303,7 +303,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testCanRemoveImmediately_notShownLongEnough() { - final BaseHeadsUpManager alm = createHeadsUpManager(); + final HeadsUpManagerImpl alm = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); alm.showNotification(entry); @@ -314,11 +314,13 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testHunRemovedLogging() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = mock( - BaseHeadsUpManager.HeadsUpEntry.class); + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = mock( + HeadsUpManagerImpl.HeadsUpEntry.class); + when(headsUpEntry.getPinnedStatus()) + .thenReturn(StateFlowKt.MutableStateFlow(PinnedStatus.NotPinned)); headsUpEntry.mEntry = notifEntry; hum.onEntryRemoved(headsUpEntry, "test"); @@ -329,7 +331,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); useAccessibilityTimeout(false); @@ -342,7 +344,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_autoDismissesWithDefaultTimeout() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); useAccessibilityTimeout(false); @@ -356,7 +358,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -370,7 +372,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_sticky_neverAutoDismisses() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = createStickyEntry(/* id = */ 0); useAccessibilityTimeout(false); @@ -383,7 +385,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_autoDismissesWithAccessibilityTimeout() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); useAccessibilityTimeout(true); @@ -397,7 +399,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); useAccessibilityTimeout(true); @@ -411,7 +413,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testRemoveNotification_beforeMinimumDisplayTime() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); useAccessibilityTimeout(false); @@ -430,7 +432,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testRemoveNotification_afterMinimumDisplayTime() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); useAccessibilityTimeout(false); @@ -448,7 +450,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testRemoveNotification_releaseImmediately() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); hum.showNotification(entry); @@ -462,7 +464,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testIsSticky_rowPinnedAndExpanded_true() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); when(mRow.isPinned()).thenReturn(true); @@ -470,7 +472,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { hum.showNotification(notifEntry); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( notifEntry.getKey()); headsUpEntry.setExpanded(true); @@ -479,13 +481,13 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testIsSticky_remoteInputActive_true() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); hum.showNotification(notifEntry); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( notifEntry.getKey()); headsUpEntry.mRemoteInputActive = true; @@ -494,7 +496,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testIsSticky_hasFullScreenIntent_true() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext); @@ -506,7 +508,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testIsSticky_stickyForSomeTime_false() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0); hum.showNotification(entry); @@ -517,13 +519,13 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testIsSticky_false() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); hum.showNotification(notifEntry); - final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry( notifEntry.getKey()); headsUpEntry.setExpanded(false); headsUpEntry.mRemoteInputActive = false; @@ -533,7 +535,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testCompareTo_withNullEntries() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build(); hum.showNotification(alertEntry); @@ -545,7 +547,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testCompareTo_withNonAlertEntries() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry nonAlertEntry1 = new NotificationEntryBuilder().setTag( "nae1").build(); @@ -561,9 +563,9 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); - final BaseHeadsUpManager.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry( new NotificationEntryBuilder() .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0, new Notification.Builder(mContext, "") @@ -571,7 +573,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { .setOngoing(true))) .build()); - final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry( HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext)); activeRemoteInput.mRemoteInputActive = true; @@ -581,11 +583,11 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test public void testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final Person person = new Person.Builder().setName("person").build(); final PendingIntent intent = mock(PendingIntent.class); - final BaseHeadsUpManager.HeadsUpEntry incomingCall = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry incomingCall = hum.new HeadsUpEntry( new NotificationEntryBuilder() .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0, new Notification.Builder(mContext, "") @@ -593,7 +595,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { .forIncomingCall(person, intent, intent)))) .build()); - final BaseHeadsUpManager.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry( HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext)); activeRemoteInput.mRemoteInputActive = true; @@ -604,10 +606,10 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) public void testPinEntry_logsPeek_throttleEnabled() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); // Needs full screen intent in order to be pinned - final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry entryToPin = hum.new HeadsUpEntry( HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext)); // Note: the standard way to show a notification would be calling showNotification rather @@ -620,17 +622,17 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { assertEquals(2, mUiEventLoggerFake.numLogs()); assertEquals(AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(), mUiEventLoggerFake.eventId(0)); - assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), + assertEquals(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), mUiEventLoggerFake.eventId(1)); } @Test @DisableFlags(NotificationThrottleHun.FLAG_NAME) public void testPinEntry_logsPeek_throttleDisabled() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); // Needs full screen intent in order to be pinned - final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry( + final HeadsUpManagerImpl.HeadsUpEntry entryToPin = hum.new HeadsUpEntry( HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext)); // Note: the standard way to show a notification would be calling showNotification rather @@ -641,13 +643,13 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { hum.onEntryAdded(entryToPin); assertEquals(1, mUiEventLoggerFake.numLogs()); - assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), + assertEquals(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), mUiEventLoggerFake.eventId(0)); } @Test public void testSetUserActionMayIndirectlyRemove() { - final BaseHeadsUpManager hum = createHeadsUpManager(); + final HeadsUpManagerImpl hum = createHeadsUpManager(); final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerPhoneTest.kt index 6175e05923a7..35d825310fdc 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerPhoneTest.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import android.os.Handler import android.platform.test.annotations.EnableFlags @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun import com.android.systemui.statusbar.phone.ConfigurationControllerImpl import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.mockExecutorHandler import com.android.systemui.util.kotlin.JavaAdapter @@ -58,7 +59,7 @@ import platform.test.runner.parameterized.Parameters @SmallTest @RunWith(ParameterizedAndroidJunit4::class) @RunWithLooper -class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManagerTest(flags) { +class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : HeadsUpManagerImplTest(flags) { private val mHeadsUpManagerLogger = HeadsUpManagerLogger(logcatLogBuffer()) @@ -87,8 +88,8 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager @Mock private lateinit var mBgHandler: Handler - private fun createHeadsUpManagerPhone(): BaseHeadsUpManager { - return BaseHeadsUpManager( + private fun createHeadsUpManagerPhone(): HeadsUpManagerImpl { + return HeadsUpManagerImpl( mContext, mHeadsUpManagerLogger, statusBarStateController, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerTestUtil.java index 306d6efd40b5..684ce59d6129 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerTestUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java index 59987f413ad6..2b077ed7f80d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import static com.android.systemui.util.concurrency.MockExecutorHandlerKt.mockExecutorHandler; @@ -33,12 +33,14 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.time.SystemClock; -class TestableHeadsUpManager extends BaseHeadsUpManager { +class TestableHeadsUpManager extends HeadsUpManagerImpl { private HeadsUpEntry mLastCreatedEntry; @@ -76,10 +78,10 @@ class TestableHeadsUpManager extends BaseHeadsUpManager { shadeInteractor, avalancheController); - mTouchAcceptanceDelay = BaseHeadsUpManagerTest.TEST_TOUCH_ACCEPTANCE_TIME; - mMinimumDisplayTime = BaseHeadsUpManagerTest.TEST_MINIMUM_DISPLAY_TIME; - mAutoDismissTime = BaseHeadsUpManagerTest.TEST_AUTO_DISMISS_TIME; - mStickyForSomeTimeAutoDismissTime = BaseHeadsUpManagerTest.TEST_STICKY_AUTO_DISMISS_TIME; + mTouchAcceptanceDelay = HeadsUpManagerImplTest.TEST_TOUCH_ACCEPTANCE_TIME; + mMinimumDisplayTime = HeadsUpManagerImplTest.TEST_MINIMUM_DISPLAY_TIME; + mAutoDismissTime = HeadsUpManagerImplTest.TEST_AUTO_DISMISS_TIME; + mStickyForSomeTimeAutoDismissTime = HeadsUpManagerImplTest.TEST_STICKY_AUTO_DISMISS_TIME; } @NonNull diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt index 284efc71a96d..d3bde84ad0c3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt @@ -70,13 +70,13 @@ import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN import com.android.systemui.statusbar.policy.FakeDeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.FakeEventLog import com.android.systemui.util.settings.FakeGlobalSettings import com.android.systemui.util.settings.FakeSettings @@ -109,7 +109,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { override fun isTagLoggable(tagName: String, level: LogLevel): Boolean = true }, - systrace = false + systrace = false, ) private val leakCheck = LeakCheckedTest.SysuiLeakCheck() @@ -162,10 +162,10 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { @Before fun setUp() { val userId = ActivityManager.getCurrentUser() - val user = UserInfo(userId, "Current user", /* flags = */ 0) + val user = UserInfo(userId, "Current user", /* flags= */ 0) deviceProvisionedController.currentUser = userId - userTracker.set(listOf(user), /* currentUserIndex = */ 0) + userTracker.set(listOf(user), /* currentUserIndex= */ 0) systemSettings = FakeSettings() whenever(bubbles.canShowBubbleNotification()).thenReturn(true) whenever(settingsInteractor.isCooldownEnabled).thenReturn(MutableStateFlow(true)) @@ -491,7 +491,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { private fun withPeekAndPulseEntry( extendEntry: EntryBuilder.() -> Unit, - block: (NotificationEntry) -> Unit + block: (NotificationEntry) -> Unit, ) { ensurePeekState() block(buildPeekEntry(extendEntry)) @@ -540,10 +540,10 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG) fun testShouldNotHeadsUp_silentNotification() { withPeekAndPulseEntry({ - isGrouped = false - isGroupSummary = false - isSilent = true - }) { + isGrouped = false + isGroupSummary = false + isSilent = true + }) { assertShouldNotHeadsUp(it) assertNoEventsLogged() } @@ -553,10 +553,10 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG) fun testShouldHeadsUp_silentNotificationFalse() { withPeekAndPulseEntry({ - isGrouped = false - isGroupSummary = false - isSilent = false - }) { + isGrouped = false + isGroupSummary = false + isSilent = false + }) { assertShouldHeadsUp(it) assertNoEventsLogged() } @@ -697,7 +697,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { forEachFsiState { assertShouldNotFsi( buildFsiEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_FULL_SCREEN_INTENT }, - expectWouldInterruptWithoutDnd = true + expectWouldInterruptWithoutDnd = true, ) assertNoEventsLogged() } @@ -719,7 +719,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { suppressedVisualEffects = SUPPRESSED_EFFECT_FULL_SCREEN_INTENT importance = IMPORTANCE_DEFAULT }, - expectWouldInterruptWithoutDnd = false + expectWouldInterruptWithoutDnd = false, ) assertNoEventsLogged() } @@ -754,7 +754,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { assertUiEventLogged( FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR, entry.sbn.uid, - entry.sbn.packageName + entry.sbn.packageName, ) assertSystemEventLogged("231322873", entry.sbn.uid, "groupAlertBehavior") } @@ -813,7 +813,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { assertUiEventLogged( FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA, entry.sbn.uid, - entry.sbn.packageName + entry.sbn.packageName, ) assertSystemEventLogged("274759612", entry.sbn.uid, "bubbleMetadata") } @@ -960,7 +960,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { var keyguardIsShowing: Boolean = false, var keyguardIsOccluded: Boolean = false, var deviceProvisioned: Boolean = true, - var currentUserSetup: Boolean = true + var currentUserSetup: Boolean = true, ) protected fun setState(state: State): Unit = @@ -1131,7 +1131,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { protected fun withLegacySuppressor( suppressor: NotificationInterruptSuppressor, - block: () -> Unit + block: () -> Unit, ) { provider.addLegacySuppressor(suppressor) block() @@ -1166,7 +1166,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { protected fun assertShouldNotFsi( entry: NotificationEntry, - expectWouldInterruptWithoutDnd: Boolean? = null + expectWouldInterruptWithoutDnd: Boolean? = null, ) = provider.makeUnloggedFullScreenIntentDecision(entry).let { provider.logFullScreenIntentDecision(it) @@ -1175,7 +1175,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { assertEquals( "unexpected wouldInterruptWithoutDnd for FSI: ${it.logReason}", expectWouldInterruptWithoutDnd, - it.wouldInterruptWithoutDnd + it.wouldInterruptWithoutDnd, ) } } @@ -1227,9 +1227,9 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { context, /* requestCode = */ 0, Intent().setPackage(context.packageName), - FLAG_MUTABLE + FLAG_MUTABLE, ), - Icon.createWithResource(context.resources, R.drawable.android) + Icon.createWithResource(context.resources, R.drawable.android), ) } @@ -1272,7 +1272,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { } if (hasFsi) { - nb.setFullScreenIntent(mock(), /* highPriority = */ true) + nb.setFullScreenIntent(mock(), /* highPriority= */ true) } } .build() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt index 7fd9c9fbdd6b..ff8ef189eb76 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt @@ -27,9 +27,9 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.settings.UserTracker import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor import com.android.systemui.statusbar.notification.NotifPipelineFlags +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.EventLog import com.android.systemui.util.settings.GlobalSettings @@ -63,7 +63,7 @@ object VisualInterruptionDecisionProviderTestUtil { bubbles: Optional<Bubbles>, context: Context, notificationManager: NotificationManager, - settingsInteractor: NotificationSettingsInteractor + settingsInteractor: NotificationSettingsInteractor, ): VisualInterruptionDecisionProvider { return if (VisualInterruptionRefactor.isEnabled) { VisualInterruptionDecisionProviderImpl( @@ -88,7 +88,7 @@ object VisualInterruptionDecisionProviderTestUtil { bubbles, context, notificationManager, - settingsInteractor + settingsInteractor, ) } else { NotificationInterruptStateProviderWrapper( @@ -109,7 +109,7 @@ object VisualInterruptionDecisionProviderTestUtil { systemClock, globalSettings, eventLog, - bubbles + bubbles, ) ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt new file mode 100644 index 000000000000..6736ccf739ce --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt @@ -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 com.android.systemui.statusbar.notification.promoted + +import android.app.Notification +import android.app.Notification.BigPictureStyle +import android.app.Notification.BigTextStyle +import android.app.Notification.CallStyle +import android.app.Notification.MessagingStyle +import android.app.Notification.ProgressStyle +import android.app.Notification.ProgressStyle.Segment +import android.app.PendingIntent +import android.app.Person +import android.content.Intent +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.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class PromotedNotificationContentExtractorTest : SysuiTestCase() { + private val kosmos = testKosmos() + + private val provider = + FakePromotedNotificationsProvider().also { kosmos.promotedNotificationsProvider = it } + + private val underTest = kosmos.promotedNotificationContentExtractor + + @Test + @DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun shouldNotExtract_bothFlagsDisabled() { + val notif = createEntry().also { provider.promotedEntries.add(it) } + val content = extractContent(notif) + assertThat(content).isNull() + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME) + @DisableFlags(StatusBarNotifChips.FLAG_NAME) + fun shouldExtract_promotedNotificationUiFlagEnabled() { + val entry = createEntry().also { provider.promotedEntries.add(it) } + val content = extractContent(entry) + assertThat(content).isNotNull() + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + @DisableFlags(PromotedNotificationUi.FLAG_NAME) + fun shouldExtract_statusBarNotifChipsFlagEnabled() { + val entry = createEntry().also { provider.promotedEntries.add(it) } + val content = extractContent(entry) + assertThat(content).isNotNull() + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun shouldExtract_bothFlagsEnabled() { + val entry = createEntry().also { provider.promotedEntries.add(it) } + val content = extractContent(entry) + assertThat(content).isNotNull() + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun shouldNotExtract_providerDidNotPromote() { + val entry = createEntry().also { provider.promotedEntries.remove(it) } + val content = extractContent(entry) + assertThat(content).isNull() + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_commonFields() { + val entry = + createEntry { + setSubText(TEST_SUB_TEXT) + setContentTitle(TEST_CONTENT_TITLE) + setContentText(TEST_CONTENT_TEXT) + } + .also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.subText).isEqualTo(TEST_SUB_TEXT) + assertThat(content?.title).isEqualTo(TEST_CONTENT_TITLE) + assertThat(content?.text).isEqualTo(TEST_CONTENT_TEXT) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_fromBigPictureStyle() { + val entry = + createEntry { setStyle(BigPictureStyle()) }.also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.style).isEqualTo(Style.BigPicture) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_fromBigTextStyle() { + val entry = + createEntry { setStyle(BigTextStyle()) }.also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.style).isEqualTo(Style.BigText) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_fromCallStyle() { + val hangUpIntent = + PendingIntent.getBroadcast(context, 0, Intent("hangup"), PendingIntent.FLAG_IMMUTABLE) + + val entry = + createEntry { setStyle(CallStyle.forOngoingCall(TEST_PERSON, hangUpIntent)) } + .also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.style).isEqualTo(Style.Call) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_fromProgressStyle() { + val entry = + createEntry { + setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75)) + } + .also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.style).isEqualTo(Style.Progress) + assertThat(content?.progress).isNotNull() + assertThat(content?.progress?.progress).isEqualTo(75) + assertThat(content?.progress?.progressMax).isEqualTo(100) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractContent_fromIneligibleStyle() { + val entry = + createEntry { + setStyle( + MessagingStyle(TEST_PERSON).addMessage("message text", 0L, TEST_PERSON) + ) + } + .also { provider.promotedEntries.add(it) } + + val content = extractContent(entry) + + assertThat(content).isNotNull() + assertThat(content?.style).isEqualTo(Style.Ineligible) + } + + private fun extractContent(entry: NotificationEntry): PromotedNotificationContentModel? { + val recoveredBuilder = Notification.Builder(context, entry.sbn.notification) + return underTest.extractContent(entry, recoveredBuilder) + } + + private fun createEntry(builderBlock: Notification.Builder.() -> Unit = {}): NotificationEntry { + val notif = Notification.Builder(context, "a").also(builderBlock).build() + return NotificationEntryBuilder().setNotification(notif).build() + } + + companion object { + private const val TEST_SUB_TEXT = "sub text" + private const val TEST_CONTENT_TITLE = "content title" + private const val TEST_CONTENT_TEXT = "content text" + + private const val TEST_PERSON_NAME = "person name" + private const val TEST_PERSON_KEY = "person key" + private val TEST_PERSON = + Person.Builder().setKey(TEST_PERSON_KEY).setName(TEST_PERSON_NAME).build() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt index 657e9df0029b..ca0f9ef5f2b0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.notification.collection.provider.Notificat import com.android.systemui.statusbar.notification.collection.render.FakeNodeController import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController.BUBBLES_SETTING_URI import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer @@ -49,7 +50,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationChildrenCon import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger import com.android.systemui.statusbar.phone.KeyguardBypassController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.SmartReplyConstants import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent import com.android.systemui.util.mockito.any diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java index 1c5f37cc60c3..979a1d0801f0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java @@ -40,8 +40,9 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.shade.ShadeController; +import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import org.junit.Before; import org.junit.Test; @@ -91,7 +92,7 @@ public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase { ExpandableNotificationRowDragController controller = createSpyController(); mRow.setDragController(controller); mRow.setHeadsUp(true); - mRow.setPinned(true); + mRow.setPinnedStatus(PinnedStatus.PinnedBySystem); mRow.doLongClickCallback(0, 0); mRow.doDragCallback(0, 0); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java index b278f1a48b3d..6eb2764165b5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java @@ -30,6 +30,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -42,6 +43,7 @@ import android.os.CancellationSignal; import android.os.Handler; import android.os.Looper; import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.TypedValue; @@ -56,8 +58,12 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.media.controls.util.MediaFeatureFlag; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi; +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -99,6 +105,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { @Mock private NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider; @Mock private HeadsUpStyleProvider mHeadsUpStyleProvider; @Mock private NotifLayoutInflaterFactory mNotifLayoutInflaterFactory; + @Mock private PromotedNotificationContentExtractor mPromotedNotificationContentExtractor; private final SmartReplyStateInflater mSmartReplyStateInflater = new SmartReplyStateInflater() { @@ -142,6 +149,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { mSmartReplyStateInflater, mNotifLayoutInflaterFactoryProvider, mHeadsUpStyleProvider, + mPromotedNotificationContentExtractor, mock(NotificationRowContentBinderLogger.class)); } @@ -382,6 +390,75 @@ public class NotificationContentInflaterTest extends SysuiTestCase { verify(mRow, times(0)).onNotificationUpdated(); } + @Test + @DisableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME}) + public void testExtractsPromotedContent_notWhenBothFlagsDisabled() throws Exception { + final PromotedNotificationContentModel content = + new PromotedNotificationContentModel.Builder("key").build(); + when(mPromotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content); + + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); + + verify(mPromotedNotificationContentExtractor, never()).extractContent(any(), any()); + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME) + @DisableFlags(StatusBarNotifChips.FLAG_NAME) + public void testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled() + throws Exception { + final PromotedNotificationContentModel content = + new PromotedNotificationContentModel.Builder("key").build(); + when(mPromotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content); + + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); + + verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any()); + assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel()); + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + @DisableFlags(PromotedNotificationUi.FLAG_NAME) + public void testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() throws Exception { + final PromotedNotificationContentModel content = + new PromotedNotificationContentModel.Builder("key").build(); + when(mPromotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content); + + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); + + verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any()); + assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel()); + } + + @Test + @EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME}) + public void testExtractsPromotedContent_whenBothFlagsEnabled() throws Exception { + final PromotedNotificationContentModel content = + new PromotedNotificationContentModel.Builder("key").build(); + when(mPromotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content); + + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); + + verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any()); + assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel()); + } + + @Test + @EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME}) + public void testExtractsPromotedContent_null() throws Exception { + when(mPromotedNotificationContentExtractor.extractContent(any(), any())).thenReturn(null); + + inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); + + verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any()); + assertNull(mRow.getEntry().getPromotedNotificationContentModel()); + } + private static void inflateAndWait(NotificationContentInflater inflater, @InflationFlag int contentToInflate, ExpandableNotificationRow row) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt index b16d3ea5337b..6a0a5bb3b191 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt @@ -71,10 +71,10 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.testKosmos import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.wmshell.BubblesManager diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt index 48608ebd6de0..18517998096a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt @@ -21,6 +21,7 @@ import android.content.Context import android.os.AsyncTask import android.os.Build import android.os.CancellationSignal +import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper.RunWithLooper import android.util.TypedValue @@ -33,8 +34,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.notification.ConversationNotificationProcessor import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED @@ -62,6 +67,7 @@ import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.never import org.mockito.kotlin.spy import org.mockito.kotlin.times import org.mockito.kotlin.verify @@ -82,7 +88,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { object : NotifLayoutInflaterFactory.Provider { override fun provide( row: ExpandableNotificationRow, - layoutType: Int + layoutType: Int, ): NotifLayoutInflaterFactory = mock() } private val smartReplyStateInflater: SmartReplyStateInflater = @@ -95,7 +101,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { notifPackageContext: Context, entry: NotificationEntry, existingSmartReplyState: InflatedSmartReplyState?, - newSmartReplyState: InflatedSmartReplyState + newSmartReplyState: InflatedSmartReplyState, ): InflatedSmartReplyViewHolder { return inflatedSmartReplies } @@ -104,6 +110,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { return inflatedSmartReplyState } } + private val promotedNotificationContentExtractor: PromotedNotificationContentExtractor = mock() @Before fun setUp() { @@ -125,7 +132,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { smartReplyStateInflater, layoutInflaterFactoryProvider, mock<HeadsUpStyleProvider>(), - mock() + promotedNotificationContentExtractor, + mock(), ) } @@ -142,7 +150,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { FLAG_CONTENT_VIEW_ALL, builder, mContext, - smartReplyStateInflater + smartReplyStateInflater, + mock(), ) verify(builder).createHeadsUpContentView(true) } @@ -160,7 +169,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { FLAG_CONTENT_VIEW_ALL, builder, mContext, - smartReplyStateInflater + smartReplyStateInflater, + mock(), ) verify(builder).createContentView(true) } @@ -187,7 +197,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { true /* expectingException */, notificationInflater, FLAG_CONTENT_VIEW_ALL, - row + row, ) Assert.assertTrue(row.privateLayout.childCount == 0) verify(row, times(0)).onNotificationUpdated() @@ -210,7 +220,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { FLAG_CONTENT_VIEW_ALL, BindParams(), false /* forceInflate */, - null /* callback */ + null, /* callback */ ) Assert.assertNull(row.entry.runningTask) } @@ -223,7 +233,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { NotificationRowContentBinderImpl.InflationProgress( packageContext = mContext, remoteViews = NewRemoteViews(), - contentModel = NotificationContentModel(headsUpStatusBarModel) + contentModel = NotificationContentModel(headsUpStatusBarModel), + extractedPromotedNotificationContentModel = null, ) val countDownLatch = CountDownLatch(1) NotificationRowContentBinderImpl.applyRemoteView( @@ -261,7 +272,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { get() = AsyncFailRemoteView( mContext.packageName, - com.android.systemui.tests.R.layout.custom_view_dark + com.android.systemui.tests.R.layout.custom_view_dark, ) }, logger = mock(), @@ -280,7 +291,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { val decoratedMediaView = builder.createContentView() Assert.assertFalse( "The decorated media style doesn't allow a view to be reapplied!", - NotificationRowContentBinderImpl.canReapplyRemoteView(mediaView, decoratedMediaView) + NotificationRowContentBinderImpl.canReapplyRemoteView(mediaView, decoratedMediaView), ) } @@ -304,7 +315,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { Assert.assertEquals( "Binder inflated a new view even though the old one was cached and usable.", view, - row.privateLayout.contractedChild + row.privateLayout.contractedChild, ) } @@ -327,7 +338,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { Assert.assertNotEquals( "Binder (somehow) used the same view when inflating.", view, - row.privateLayout.contractedChild + row.privateLayout.contractedChild, ) } @@ -396,7 +407,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { private fun getValidationError( measuredHeightDp: Float, targetSdk: Int, - contentView: RemoteViews? + contentView: RemoteViews?, ): String? { val view: View = mock() whenever(view.measuredHeight) @@ -404,7 +415,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { TypedValue.applyDimension( COMPLEX_UNIT_SP, measuredHeightDp, - mContext.resources.displayMetrics + mContext.resources.displayMetrics, ) .toInt() ) @@ -419,7 +430,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { row.entry.sbn.notification.contentView = RemoteViews( mContext.packageName, - com.android.systemui.tests.R.layout.invalid_notification_height + com.android.systemui.tests.R.layout.invalid_notification_height, ) inflateAndWait(true, notificationInflater, FLAG_CONTENT_VIEW_ALL, row) Assert.assertEquals(0, row.privateLayout.childCount.toLong()) @@ -451,7 +462,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { false, notificationInflater, FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, - messagingRow + messagingRow, ) Assert.assertNotNull(messagingRow.publicLayout.mSingleLineView) // assert this is the conversation layout @@ -460,6 +471,59 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { ) } + @Test + @DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun testExtractsPromotedContent_notWhenBothFlagsDisabled() { + val content = PromotedNotificationContentModel.Builder("key").build() + whenever(promotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content) + + inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) + + verify(promotedNotificationContentExtractor, never()).extractContent(any(), any()) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME) + @DisableFlags(StatusBarNotifChips.FLAG_NAME) + fun testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled() { + val content = PromotedNotificationContentModel.Builder("key").build() + whenever(promotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content) + + inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) + + verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any()) + Assert.assertEquals(content, row.entry.promotedNotificationContentModel) + } + + @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME) + @DisableFlags(PromotedNotificationUi.FLAG_NAME) + fun testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() { + val content = PromotedNotificationContentModel.Builder("key").build() + whenever(promotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content) + + inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) + + verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any()) + Assert.assertEquals(content, row.entry.promotedNotificationContentModel) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun testExtractsPromotedContent_whenBothFlagsEnabled() { + val content = PromotedNotificationContentModel.Builder("key").build() + whenever(promotedNotificationContentExtractor.extractContent(any(), any())) + .thenReturn(content) + + inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) + + verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any()) + Assert.assertEquals(content, row.entry.promotedNotificationContentModel) + } + private class ExceptionHolder { var exception: Exception? = null } @@ -476,7 +540,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { parent: ViewGroup, executor: Executor, listener: OnViewAppliedListener, - handler: InteractionHandler? + handler: InteractionHandler?, ): CancellationSignal { executor.execute { listener.onError(RuntimeException("Failed to inflate async")) } return CancellationSignal() @@ -486,7 +550,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { context: Context, parent: ViewGroup, executor: Executor, - listener: OnViewAppliedListener + listener: OnViewAppliedListener, ): CancellationSignal { return applyAsync(context, parent, executor, listener, null) } @@ -496,7 +560,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { private fun inflateAndWait( inflater: NotificationRowContentBinderImpl, @InflationFlag contentToInflate: Int, - row: ExpandableNotificationRow + row: ExpandableNotificationRow, ) { inflateAndWait(false /* expectingException */, inflater, contentToInflate, row) } @@ -535,7 +599,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { contentToInflate, BindParams(), false /* forceInflate */, - callback /* callback */ + callback, /* callback */ ) Assert.assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS)) exceptionHolder.exception?.let { throw it } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 2340d0289db4..080ac3f8c697 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -82,13 +82,14 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.icon.IconBuilder; import com.android.systemui.statusbar.notification.icon.IconManager; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder; import com.android.systemui.statusbar.policy.SmartReplyConstants; @@ -204,6 +205,7 @@ public class NotificationTestHelper { new MockSmartReplyInflater(), mock(NotifLayoutInflaterFactory.Provider.class), mock(HeadsUpStyleProvider.class), + mock(PromotedNotificationContentExtractor.class), mock(NotificationRowContentBinderLogger.class)) : new NotificationContentInflater( mock(NotifRemoteViewCache.class), @@ -214,6 +216,7 @@ public class NotificationTestHelper { new MockSmartReplyInflater(), mock(NotifLayoutInflaterFactory.Provider.class), mock(HeadsUpStyleProvider.class), + mock(PromotedNotificationContentExtractor.class), mock(NotificationRowContentBinderLogger.class)); contentBinder.setInflateSynchronously(true); mBindStage = new RowContentBindStage(contentBinder, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt index 07935e47845e..92b8c3aa84d6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt @@ -24,8 +24,8 @@ import com.android.systemui.flags.DisableSceneContainer import com.android.systemui.flags.andSceneContainer import com.android.systemui.shade.transition.LargeScreenShadeInterpolator import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.headsup.AvalancheController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager -import com.android.systemui.statusbar.policy.AvalancheController import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 6425da46fc67..de40abb4d9d8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -83,7 +83,7 @@ import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ColorUpdateLogger; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -107,7 +107,7 @@ import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.statusbar.policy.ZenModeController; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt index b877456ab604..2b7e95062716 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt @@ -27,11 +27,11 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView import com.android.systemui.statusbar.notification.footer.ui.view.FooterView import com.android.systemui.statusbar.notification.footer.ui.view.FooterView.FooterViewState +import com.android.systemui.statusbar.notification.headsup.AvalancheController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager -import com.android.systemui.statusbar.policy.AvalancheController import com.google.common.truth.Expect import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi 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 bf144729dea3..e592e4b319e3 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 @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.notification.data.repository.activeNotific import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor +import com.android.systemui.statusbar.notification.headsup.PinnedStatus 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.fakeConfigurationController @@ -522,21 +523,21 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas assertThat(pinnedHeadsUpRows).isEmpty() // WHEN a row gets pinned - rows[0].isPinned.value = true + rows[0].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() // THEN it's added to the list assertThat(pinnedHeadsUpRows).containsExactly(rows[0]) // WHEN more rows are pinned - rows[1].isPinned.value = true + rows[1].pinnedStatus.value = PinnedStatus.PinnedBySystem runCurrent() // THEN they are all in the list assertThat(pinnedHeadsUpRows).containsExactly(rows[0], rows[1]) // WHEN a row gets unpinned - rows[0].isPinned.value = false + rows[0].pinnedStatus.value = PinnedStatus.NotPinned runCurrent() // THEN it's removed from the list diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index a940ed4d70f9..f48fd3c998b1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -838,27 +838,26 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S testScope.runTest { var notificationCount = 10 val calculateSpace = { space: Float, useExtraShelfSpace: Boolean -> notificationCount } - val maxNotifications by collectLastValue(underTest.getMaxNotifications(calculateSpace)) + val config by collectLastValue(underTest.getLockscreenDisplayConfig(calculateSpace)) advanceTimeBy(50L) showLockscreen() shadeTestUtil.setSplitShade(false) configurationRepository.onAnyConfigurationChange() - assertThat(maxNotifications).isEqualTo(10) + assertThat(config?.maxNotifications).isEqualTo(10) // Also updates when directly requested (as it would from NotificationStackScrollLayout) notificationCount = 25 sharedNotificationContainerInteractor.notificationStackChanged() advanceTimeBy(50L) - assertThat(maxNotifications).isEqualTo(25) + assertThat(config?.maxNotifications).isEqualTo(25) // Also ensure another collection starts with the same value. As an example, folding // then unfolding will restart the coroutine and it must get the last value immediately. - val newMaxNotifications by - collectLastValue(underTest.getMaxNotifications(calculateSpace)) + val newConfig by collectLastValue(underTest.getLockscreenDisplayConfig(calculateSpace)) advanceTimeBy(50L) - assertThat(newMaxNotifications).isEqualTo(25) + assertThat(newConfig?.maxNotifications).isEqualTo(25) } @Test @@ -866,18 +865,18 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S testScope.runTest { var notificationCount = 10 val calculateSpace = { space: Float, useExtraShelfSpace: Boolean -> notificationCount } - val maxNotifications by collectLastValue(underTest.getMaxNotifications(calculateSpace)) + val config by collectLastValue(underTest.getLockscreenDisplayConfig(calculateSpace)) advanceTimeBy(50L) showLockscreen() shadeTestUtil.setSplitShade(false) configurationRepository.onAnyConfigurationChange() - assertThat(maxNotifications).isEqualTo(10) + assertThat(config?.maxNotifications).isEqualTo(10) // Shade expanding... still 10 shadeTestUtil.setLockscreenShadeExpansion(0.5f) - assertThat(maxNotifications).isEqualTo(10) + assertThat(config?.maxNotifications).isEqualTo(10) notificationCount = 25 @@ -885,20 +884,20 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S shadeTestUtil.setLockscreenShadeTracking(true) // Should still be 10, since the user is interacting - assertThat(maxNotifications).isEqualTo(10) + assertThat(config?.maxNotifications).isEqualTo(10) shadeTestUtil.setLockscreenShadeTracking(false) shadeTestUtil.setLockscreenShadeExpansion(0f) // Stopped tracking, show 25 - assertThat(maxNotifications).isEqualTo(25) + assertThat(config?.maxNotifications).isEqualTo(25) } @Test fun maxNotificationsOnShade() = testScope.runTest { val calculateSpace = { space: Float, useExtraShelfSpace: Boolean -> 10 } - val maxNotifications by collectLastValue(underTest.getMaxNotifications(calculateSpace)) + val config by collectLastValue(underTest.getLockscreenDisplayConfig(calculateSpace)) advanceTimeBy(50L) // Show lockscreen with shade expanded @@ -908,7 +907,7 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S configurationRepository.onAnyConfigurationChange() // -1 means No Limit - assertThat(maxNotifications).isEqualTo(-1) + assertThat(config?.maxNotifications).isEqualTo(-1) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java index d9e94953e757..f76f1ce48a5c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java @@ -57,7 +57,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java index 64414050e0f4..ad8b67513b9f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -53,7 +53,7 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index dd03ab393ce9..f9f2cd328094 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -45,13 +45,14 @@ import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor; +import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.Clock; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Assert; @@ -131,13 +132,13 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { @Test public void testShowinEntryUpdated() { - mRow.setPinned(true); + mRow.setPinnedStatus(PinnedStatus.PinnedBySystem); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry); mHeadsUpAppearanceController.onHeadsUpPinned(mEntry); assertEquals(mRow.getEntry(), mHeadsUpStatusBarView.getShowingEntry()); - mRow.setPinned(false); + mRow.setPinnedStatus(PinnedStatus.NotPinned); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry); assertEquals(null, mHeadsUpStatusBarView.getShowingEntry()); @@ -145,13 +146,13 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { @Test public void testShownUpdated() { - mRow.setPinned(true); + mRow.setPinnedStatus(PinnedStatus.PinnedBySystem); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry); mHeadsUpAppearanceController.onHeadsUpPinned(mEntry); assertTrue(mHeadsUpAppearanceController.isShown()); - mRow.setPinned(false); + mRow.setPinnedStatus(PinnedStatus.NotPinned); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry); Assert.assertFalse(mHeadsUpAppearanceController.isShown()); @@ -160,13 +161,13 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { @Test @DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME) public void testHeaderUpdated() { - mRow.setPinned(true); + mRow.setPinnedStatus(PinnedStatus.PinnedBySystem); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry); mHeadsUpAppearanceController.onHeadsUpPinned(mEntry); assertEquals(mRow.getHeaderVisibleAmount(), 0.0f, 0.0f); - mRow.setPinned(false); + mRow.setPinnedStatus(PinnedStatus.NotPinned); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry); assertEquals(mRow.getHeaderVisibleAmount(), 1.0f, 0.0f); @@ -176,13 +177,13 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { public void testOperatorNameViewUpdated() { mHeadsUpAppearanceController.setAnimationsEnabled(false); - mRow.setPinned(true); + mRow.setPinnedStatus(PinnedStatus.PinnedBySystem); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry); mHeadsUpAppearanceController.onHeadsUpPinned(mEntry); assertEquals(View.INVISIBLE, mOperatorNameView.getVisibility()); - mRow.setPinned(false); + mRow.setPinnedStatus(PinnedStatus.NotPinned); when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry); assertEquals(View.VISIBLE, mOperatorNameView.getVisibility()); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt new file mode 100644 index 000000000000..90506a1b9a7f --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.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.statusbar.phone + +import android.platform.test.annotations.EnableFlags +import android.view.Display.DEFAULT_DISPLAY +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.kosmos.testScope +import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays +import com.android.systemui.testKosmos +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.never +import org.mockito.kotlin.verify + +@SmallTest +@RunWith(AndroidJUnit4::class) +@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) +class MultiDisplayAutoHideControllerStoreTest : SysuiTestCase() { + + private val kosmos = testKosmos().useUnconfinedTestDispatcher() + private val testScope = kosmos.testScope + private val fakeDisplayRepository = kosmos.displayRepository + + // Lazy so that @EnableFlags has time to run before underTest is instantiated. + private val underTest by lazy { kosmos.multiDisplayAutoHideControllerStore } + + @Before fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY) } + + @Before + fun start() { + underTest.start() + } + + @Test + fun beforeDisplayRemoved_doesNotStopInstances() = + testScope.runTest { + val instance = underTest.forDisplay(DEFAULT_DISPLAY) + + verify(instance, never()).stop() + } + + @Test + fun displayRemoved_stopsInstance() = + testScope.runTest { + val instance = underTest.forDisplay(DEFAULT_DISPLAY) + + fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) + + verify(instance).stop() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 95472cad4b90..41782a123f14 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -73,7 +73,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt index 20b273ae7f4e..d5651ec8ce6c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt @@ -22,21 +22,25 @@ import android.content.Context import android.content.pm.UserInfo import android.os.UserHandle import android.os.UserManager +import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.GuestResetOrExitSessionReceiver import com.android.systemui.GuestResumeSessionReceiver import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.runTest +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.domain.model.ShowDialogRequestModel import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.kotlinArgumentCaptor import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.TestCoroutineScope import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -48,6 +52,7 @@ import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper class GuestUserInteractorTest : SysuiTestCase() { @Mock private lateinit var manager: UserManager @@ -64,16 +69,15 @@ class GuestUserInteractorTest : SysuiTestCase() { private lateinit var underTest: GuestUserInteractor - private lateinit var scope: TestCoroutineScope - private lateinit var repository: FakeUserRepository + private val kosmos = testKosmos().useUnconfinedTestDispatcher() + private val scope = kosmos.testScope + private val repository = FakeUserRepository() @Before fun setUp() { MockitoAnnotations.initMocks(this) whenever(manager.createGuest(any())).thenReturn(GUEST_USER_INFO) - scope = TestCoroutineScope() - repository = FakeUserRepository() repository.setUserInfos(ALL_USERS) underTest = initGuestUserInteractor(context) @@ -83,8 +87,8 @@ class GuestUserInteractorTest : SysuiTestCase() { GuestUserInteractor( applicationContext = context, applicationScope = scope, - mainDispatcher = IMMEDIATE, - backgroundDispatcher = IMMEDIATE, + mainDispatcher = kosmos.testDispatcher, + backgroundDispatcher = kosmos.testDispatcher, manager = manager, repository = repository, deviceProvisionedController = deviceProvisionedController, @@ -92,7 +96,7 @@ class GuestUserInteractorTest : SysuiTestCase() { refreshUsersScheduler = RefreshUsersScheduler( applicationScope = scope, - mainDispatcher = IMMEDIATE, + mainDispatcher = kosmos.testDispatcher, repository = repository, ), uiEventLogger = uiEventLogger, @@ -118,7 +122,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun onDeviceBootCompleted_allowedToAdd_createGuest() = - runBlocking(IMMEDIATE) { + kosmos.runTest { setAllowedToAdd() underTest.onDeviceBootCompleted() @@ -129,7 +133,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun onDeviceBootCompleted_awaitProvisioning_andCreateGuest() = - runBlocking(IMMEDIATE) { + kosmos.runTest { setAllowedToAdd(isAllowed = false) underTest.onDeviceBootCompleted() val captor = @@ -145,7 +149,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun createAndSwitchTo() = - runBlocking(IMMEDIATE) { + kosmos.runTest { underTest.createAndSwitchTo( showDialog = showDialog, dismissDialog = dismissDialog, @@ -160,7 +164,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun createAndSwitchTo_failsToCreate_doesNotSwitchTo() = - runBlocking(IMMEDIATE) { + kosmos.runTest { whenever(manager.createGuest(any())).thenReturn(null) underTest.createAndSwitchTo( @@ -177,7 +181,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun exit_returnsToTargetUser() = - runBlocking(IMMEDIATE) { + kosmos.runTest { repository.setSelectedUserInfo(GUEST_USER_INFO) val targetUserId = NON_GUEST_USER_INFO.id @@ -197,7 +201,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun exit_returnsToLastNonGuest() = - runBlocking(IMMEDIATE) { + kosmos.runTest { val expectedUserId = NON_GUEST_USER_INFO.id whenever(manager.getUserInfo(expectedUserId)).thenReturn(NON_GUEST_USER_INFO) repository.lastSelectedNonGuestUserId = expectedUserId @@ -219,7 +223,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun exit_lastNonGuestWasRemoved_returnsToMainUser() = - runBlocking(IMMEDIATE) { + kosmos.runTest { val removedUserId = 310 val mainUserId = 10 repository.lastSelectedNonGuestUserId = removedUserId @@ -242,7 +246,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun exit_guestWasEphemeral_itIsRemoved() = - runBlocking(IMMEDIATE) { + kosmos.runTest { whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true) repository.setUserInfos(listOf(NON_GUEST_USER_INFO, EPHEMERAL_GUEST_USER_INFO)) repository.setSelectedUserInfo(EPHEMERAL_GUEST_USER_INFO) @@ -265,7 +269,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun exit_forceRemoveGuest_itIsRemoved() = - runBlocking(IMMEDIATE) { + kosmos.runTest { whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true) repository.setSelectedUserInfo(GUEST_USER_INFO) val targetUserId = NON_GUEST_USER_INFO.id @@ -287,7 +291,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun exit_selectedDifferentFromGuestUser_doNothing() = - runBlocking(IMMEDIATE) { + kosmos.runTest { repository.setSelectedUserInfo(NON_GUEST_USER_INFO) underTest.exit( @@ -304,7 +308,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun exit_selectedIsActuallyNotAguestUser_doNothing() = - runBlocking(IMMEDIATE) { + kosmos.runTest { repository.setSelectedUserInfo(NON_GUEST_USER_INFO) underTest.exit( @@ -321,7 +325,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun remove_returnsToTargetUser() = - runBlocking(IMMEDIATE) { + kosmos.runTest { whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true) repository.setSelectedUserInfo(GUEST_USER_INFO) @@ -342,7 +346,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun remove_selectedDifferentFromGuestUser_doNothing() = - runBlocking(IMMEDIATE) { + kosmos.runTest { whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true) repository.setSelectedUserInfo(NON_GUEST_USER_INFO) @@ -359,7 +363,7 @@ class GuestUserInteractorTest : SysuiTestCase() { @Test fun remove_selectedIsActuallyNotAguestUser_doNothing() = - runBlocking(IMMEDIATE) { + kosmos.runTest { whenever(manager.markGuestForDeletion(anyInt())).thenReturn(true) repository.setSelectedUserInfo(NON_GUEST_USER_INFO) @@ -395,11 +399,7 @@ class GuestUserInteractorTest : SysuiTestCase() { companion object { private val IMMEDIATE = Dispatchers.Main.immediate private val NON_GUEST_USER_INFO = - UserInfo( - /* id= */ 818, - /* name= */ "non_guest", - /* flags= */ UserInfo.FLAG_FULL, - ) + UserInfo(/* id= */ 818, /* name= */ "non_guest", /* flags= */ UserInfo.FLAG_FULL) private val GUEST_USER_INFO = UserInfo( /* id= */ 669, @@ -416,10 +416,6 @@ class GuestUserInteractorTest : SysuiTestCase() { /* flags= */ UserInfo.FLAG_EPHEMERAL or UserInfo.FLAG_FULL, UserManager.USER_TYPE_FULL_GUEST, ) - private val ALL_USERS = - listOf( - NON_GUEST_USER_INFO, - GUEST_USER_INFO, - ) + private val ALL_USERS = listOf(NON_GUEST_USER_INFO, GUEST_USER_INFO) } } diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp index 6212e2be4911..2cd334693486 100644 --- a/packages/SystemUI/plugin/Android.bp +++ b/packages/SystemUI/plugin/Android.bp @@ -55,6 +55,8 @@ java_library { "SystemUICommon", "SystemUILogLib", "androidx.annotation_annotation", + "androidx.compose.ui_ui", + "androidx.compose.runtime_runtime", ], } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockAnimations.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockAnimations.kt new file mode 100644 index 000000000000..2df14a86e77c --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockAnimations.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.plugins.clocks + +import android.view.View +import com.android.systemui.plugins.annotations.ProtectedInterface + +/** Methods which trigger various clock animations */ +@ProtectedInterface +interface ClockAnimations { + /** Runs an enter animation (if any) */ + fun enter() + + /** Sets how far into AOD the device currently is. */ + fun doze(fraction: Float) + + /** Sets how far into the folding animation the device is. */ + fun fold(fraction: Float) + + /** Runs the battery animation (if any). */ + fun charge() + + /** + * Runs when the clock's position changed during the move animation. + * + * @param fromLeft the [View.getLeft] position of the clock, before it started moving. + * @param direction the direction in which it is moving. A positive number means right, and + * negative means left. + * @param fraction fraction of the clock movement. 0 means it is at the beginning, and 1 means + * it finished moving. + * @deprecated use {@link #onPositionUpdated(float, float)} instead. + */ + fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) + + /** + * Runs when the clock's position changed during the move animation. + * + * @param distance is the total distance in pixels to offset the glyphs when animation + * completes. Negative distance means we are animating the position towards the center. + * @param fraction fraction of the clock movement. 0 means it is at the beginning, and 1 means + * it finished moving. + */ + fun onPositionUpdated(distance: Float, fraction: Float) + + /** + * Runs when swiping clock picker, swipingFraction: 1.0 -> clock is scaled up in the preview, + * 0.0 -> clock is scaled down in the shade; previewRatio is previewSize / screenSize + */ + fun onPickerCarouselSwiping(swipingFraction: Float) +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockConfig.kt new file mode 100644 index 000000000000..d84d89087349 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockConfig.kt @@ -0,0 +1,65 @@ +/* + * 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.plugins.clocks + +/** + * Exposes the rendering capabilities of this clock to SystemUI so that it can be hosted and render + * correctly in SystemUI's process. Ideally all clocks could be rendered identically, but in + * practice we different clocks require different behavior from SystemUI. + */ +data class ClockConfig( + val id: ClockId, + + /** Localized name of the clock */ + val name: String, + + /** Localized accessibility description for the clock */ + val description: String, + + /** Transition to AOD should move smartspace like large clock instead of small clock */ + val useAlternateSmartspaceAODTransition: Boolean = false, + + /** Deprecated version of isReactiveToTone; moved to ClockPickerConfig */ + @Deprecated("TODO(b/352049256): Remove in favor of ClockPickerConfig.isReactiveToTone") + val isReactiveToTone: Boolean = true, + + /** True if the clock is large frame clock, which will use weather in compose. */ + val useCustomClockScene: Boolean = false, +) + +/** Render configuration options for a specific clock face. */ +data class ClockFaceConfig( + /** Expected interval between calls to onTimeTick. Can always reduce to PER_MINUTE in AOD. */ + val tickRate: ClockTickRate = ClockTickRate.PER_MINUTE, + + /** Call to check whether the clock consumes weather data */ + val hasCustomWeatherDataDisplay: Boolean = false, + + /** + * Whether this clock has a custom position update animation. If true, the keyguard will call + * `onPositionUpdated` to notify the clock of a position update animation. If false, a default + * animation will be used (e.g. a simple translation). + */ + val hasCustomPositionUpdatedAnimation: Boolean = false, + + /** True if the clock is large frame clock, which will use weatherBlueprint in compose. */ + val useCustomClockScene: Boolean = false, +) + +/** Tick rates for clocks */ +enum class ClockTickRate(val value: Int) { + PER_MINUTE(2), // Update the clock once per minute. + PER_SECOND(1), // Update the clock once per second. + PER_FRAME(0), // Update the clock every second. +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt new file mode 100644 index 000000000000..32fec3277f18 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockController.kt @@ -0,0 +1,44 @@ +/* + * 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.plugins.clocks + +import com.android.systemui.plugins.annotations.ProtectedInterface +import com.android.systemui.plugins.annotations.SimpleProperty +import java.io.PrintWriter + +/** Interface for controlling an active clock */ +@ProtectedInterface +interface ClockController { + @get:SimpleProperty + /** A small version of the clock, appropriate for smaller viewports */ + val smallClock: ClockFaceController + + @get:SimpleProperty + /** A large version of the clock, appropriate when a bigger viewport is available */ + val largeClock: ClockFaceController + + @get:SimpleProperty + /** Determines the way the hosting app should behave when rendering either clock face */ + val config: ClockConfig + + @get:SimpleProperty + /** Events that clocks may need to respond to */ + val events: ClockEvents + + /** Initializes various rendering parameters. If never called, provides reasonable defaults. */ + fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) + + /** Optional method for dumping debug information */ + fun dump(pw: PrintWriter) +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt new file mode 100644 index 000000000000..235475f6b202 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt @@ -0,0 +1,48 @@ +/* + * 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.plugins.clocks + +import com.android.systemui.plugins.annotations.ProtectedInterface +import com.android.systemui.plugins.annotations.ProtectedReturn +import java.util.Locale +import java.util.TimeZone + +/** Events that should call when various rendering parameters change */ +@ProtectedInterface +interface ClockEvents { + @get:ProtectedReturn("return false;") + /** Set to enable or disable swipe interaction */ + var isReactiveTouchInteractionEnabled: Boolean // TODO(b/364664388): Remove/Rename + + /** Call whenever timezone changes */ + fun onTimeZoneChanged(timeZone: TimeZone) + + /** Call whenever the text time format changes (12hr vs 24hr) */ + fun onTimeFormatChanged(is24Hr: Boolean) + + /** Call whenever the locale changes */ + fun onLocaleChanged(locale: Locale) + + /** Call whenever the weather data should update */ + fun onWeatherDataChanged(data: WeatherData) + + /** Call with alarm information */ + fun onAlarmDataChanged(data: AlarmData) + + /** Call with zen/dnd information */ + fun onZenDataChanged(data: ZenData) + + /** Update reactive axes for this clock */ + fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceController.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceController.kt new file mode 100644 index 000000000000..8a023f1c2388 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceController.kt @@ -0,0 +1,47 @@ +/* + * 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.plugins.clocks + +import android.view.View +import com.android.systemui.plugins.annotations.ProtectedInterface +import com.android.systemui.plugins.annotations.SimpleProperty + +/** Interface for a specific clock face version rendered by the clock */ +@ProtectedInterface +interface ClockFaceController { + @get:SimpleProperty + @Deprecated("Prefer use of layout") + /** View that renders the clock face */ + val view: View + + @get:SimpleProperty + /** Layout specification for this clock */ + val layout: ClockFaceLayout + + @get:SimpleProperty + /** Determines the way the hosting app should behave when rendering this clock face */ + val config: ClockFaceConfig + + @get:SimpleProperty + /** Current theme information the clock is using */ + val theme: ThemeConfig + + @get:SimpleProperty + /** Events specific to this clock face */ + val events: ClockFaceEvents + + @get:SimpleProperty + /** Triggers for various animations */ + val animations: ClockAnimations +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt new file mode 100644 index 000000000000..029e54658f60 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceEvents.kt @@ -0,0 +1,63 @@ +/* + * 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.plugins.clocks + +import android.graphics.Rect +import com.android.systemui.plugins.annotations.ProtectedInterface + +/** Events that have specific data about the related face */ +@ProtectedInterface +interface ClockFaceEvents { + /** Call every tick to update the rendered time */ + fun onTimeTick() + + /** + * Call whenever the theme or seedColor is updated + * + * Theme can be specific to the clock face. + * - isDarkTheme -> clock should be light + * - !isDarkTheme -> clock should be dark + */ + fun onThemeChanged(theme: ThemeConfig) + + /** + * Call whenever font settings change. Pass in a target font size in pixels. The specific clock + * design is allowed to ignore this target size on a case-by-case basis. + */ + fun onFontSettingChanged(fontSizePx: Float) + + /** + * Target region information for the clock face. For small clock, this will match the bounds of + * the parent view mostly, but have a target height based on the height of the default clock. + * For large clocks, the parent view is the entire device size, but most clocks will want to + * render within the centered targetRect to avoid obstructing other elements. The specified + * targetRegion is relative to the parent view. + */ + fun onTargetRegionChanged(targetRegion: Rect?) + + /** Called to notify the clock about its display. */ + fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) +} + +/** Contains Theming information for the clock face */ +data class ThemeConfig( + /** True if the clock should use dark theme (light text on dark background) */ + val isDarkTheme: Boolean, + + /** + * A clock specific seed color to use when theming, if any was specified by the user. A null + * value denotes that we should use the seed color for the current system theme. + */ + val seedColor: Int?, +) diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt new file mode 100644 index 000000000000..fb5ef02aa06a --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockFaceLayout.kt @@ -0,0 +1,174 @@ +/* + * 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.plugins.clocks + +import android.content.Context +import android.util.DisplayMetrics +import android.view.View +import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.ConstraintSet.BOTTOM +import androidx.constraintlayout.widget.ConstraintSet.END +import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID +import androidx.constraintlayout.widget.ConstraintSet.START +import androidx.constraintlayout.widget.ConstraintSet.TOP +import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT +import com.android.internal.policy.SystemBarUtils +import com.android.systemui.plugins.annotations.GeneratedImport +import com.android.systemui.plugins.annotations.ProtectedInterface +import com.android.systemui.plugins.annotations.ProtectedReturn + +/** Specifies layout information for the clock face */ +@ProtectedInterface +@GeneratedImport("java.util.ArrayList") +@GeneratedImport("android.view.View") +interface ClockFaceLayout { + @get:ProtectedReturn("return new ArrayList<View>();") + /** All clock views to add to the root constraint layout before applying constraints. */ + val views: List<View> + + @ProtectedReturn("return constraints;") + /** Custom constraints to apply to Lockscreen ConstraintLayout. */ + fun applyConstraints(constraints: ConstraintSet): ConstraintSet + + @ProtectedReturn("return constraints;") + /** Custom constraints to apply to preview ConstraintLayout. */ + fun applyPreviewConstraints( + clockPreviewConfig: ClockPreviewConfig, + constraints: ConstraintSet, + ): ConstraintSet + + /** Apply specified AOD BurnIn parameters to this layout */ + fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) +} + +/** Data class to contain AOD BurnIn information for correct aod rendering */ +data class AodClockBurnInModel( + /** Scale that the clock should render at to mitigate burnin */ + val scale: Float, + + /** X-Translation for the clock to mitigate burnin */ + val translationX: Float, + + /** Y-Translation for the clock to mitigate burnin */ + val translationY: Float, +) + +/** A ClockFaceLayout that applies the default lockscreen layout to a single view */ +class DefaultClockFaceLayout(val view: View) : ClockFaceLayout { + override val views = listOf(view) + + override fun applyConstraints(constraints: ConstraintSet): ConstraintSet { + if (views.size != 1) { + throw IllegalArgumentException( + "Should have only one container view when using DefaultClockFaceLayout" + ) + } + return constraints + } + + override fun applyPreviewConstraints( + clockPreviewConfig: ClockPreviewConfig, + constraints: ConstraintSet, + ): ConstraintSet { + return applyDefaultPreviewConstraints(clockPreviewConfig, constraints) + } + + override fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) { + // Default clock doesn't need detailed control of view + } + + companion object { + fun applyDefaultPreviewConstraints( + clockPreviewConfig: ClockPreviewConfig, + constraints: ConstraintSet, + ): ConstraintSet { + constraints.apply { + val context = clockPreviewConfig.previewContext + val lockscreenClockViewLargeId = getId(context, "lockscreen_clock_view_large") + constrainWidth(lockscreenClockViewLargeId, WRAP_CONTENT) + constrainHeight(lockscreenClockViewLargeId, WRAP_CONTENT) + constrainMaxHeight(lockscreenClockViewLargeId, 0) + + val largeClockTopMargin = + SystemBarUtils.getStatusBarHeight(context) + + getDimen(context, "small_clock_padding_top") + + getDimen(context, "keyguard_smartspace_top_offset") + + getDimen(context, "date_weather_view_height") + + getDimen(context, "enhanced_smartspace_height") + connect(lockscreenClockViewLargeId, TOP, PARENT_ID, TOP, largeClockTopMargin) + connect(lockscreenClockViewLargeId, START, PARENT_ID, START) + connect(lockscreenClockViewLargeId, END, PARENT_ID, END) + + // In preview, we'll show UDFPS icon for UDFPS devices + // and nothing for non-UDFPS devices, + // and we're not planning to add this vide in clockHostView + // so we only need position of device entry icon to constrain clock + // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection + val bottomPaddingPx = getDimen(context, "lock_icon_margin_bottom") + val defaultDensity = + DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() / + DisplayMetrics.DENSITY_DEFAULT.toFloat() + val lockIconRadiusPx = (defaultDensity * 36).toInt() + val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx + + connect(lockscreenClockViewLargeId, BOTTOM, PARENT_ID, BOTTOM, clockBottomMargin) + val smallClockViewId = getId(context, "lockscreen_clock_view") + constrainWidth(smallClockViewId, WRAP_CONTENT) + constrainHeight(smallClockViewId, getDimen(context, "small_clock_height")) + connect( + smallClockViewId, + START, + PARENT_ID, + START, + getDimen(context, "clock_padding_start") + + getDimen(context, "status_view_margin_horizontal"), + ) + val smallClockTopMargin = + getSmallClockTopPadding( + clockPreviewConfig = clockPreviewConfig, + SystemBarUtils.getStatusBarHeight(context), + ) + connect(smallClockViewId, TOP, PARENT_ID, TOP, smallClockTopMargin) + } + return constraints + } + + fun getId(context: Context, name: String): Int { + val packageName = context.packageName + val res = context.packageManager.getResourcesForApplication(packageName) + val id = res.getIdentifier(name, "id", packageName) + return id + } + + fun getDimen(context: Context, name: String): Int { + val packageName = context.packageName + val res = context.resources + val id = res.getIdentifier(name, "dimen", packageName) + return if (id == 0) 0 else res.getDimensionPixelSize(id) + } + + fun getSmallClockTopPadding( + clockPreviewConfig: ClockPreviewConfig, + statusBarHeight: Int, + ): Int { + return if (clockPreviewConfig.isShadeLayoutWide) { + getDimen(clockPreviewConfig.previewContext, "keyguard_split_shade_top_margin") - + if (clockPreviewConfig.isSceneContainerFlagEnabled) statusBarHeight else 0 + } else { + getDimen(clockPreviewConfig.previewContext, "keyguard_clock_top_margin") + + if (!clockPreviewConfig.isSceneContainerFlagEnabled) statusBarHeight else 0 + } + } + } +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockMessageBuffers.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockMessageBuffers.kt new file mode 100644 index 000000000000..bec589ae139e --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockMessageBuffers.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.plugins.clocks + +import com.android.systemui.log.core.MessageBuffer + +/** MessageBuffers for clocks that want to log information to SystemUI dumps */ +data class ClockMessageBuffers( + /** Message buffer for general infrastructure */ + val infraMessageBuffer: MessageBuffer, + + /** Message buffer for small clock rendering */ + val smallClockMessageBuffer: MessageBuffer, + + /** Message buffer for large clock rendering */ + val largeClockMessageBuffer: MessageBuffer, +) { + constructor(buffer: MessageBuffer) : this(buffer, buffer, buffer) {} +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt new file mode 100644 index 000000000000..1bc9367ce3c5 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPickerConfig.kt @@ -0,0 +1,87 @@ +/* + * 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.plugins.clocks + +import android.graphics.drawable.Drawable + +data class ClockPickerConfig +@JvmOverloads +constructor( + val id: String, + + /** Localized name of the clock */ + val name: String, + + /** Localized accessibility description for the clock */ + val description: String, + + /* Static & lightweight thumbnail version of the clock */ + val thumbnail: Drawable, + + /** True if the clock will react to tone changes in the seed color */ + val isReactiveToTone: Boolean = true, + + /** Font axes that can be modified on this clock */ + val axes: List<ClockFontAxis> = listOf(), +) + +/** Represents an Axis that can be modified */ +data class ClockFontAxis( + /** Axis key, not user renderable */ + val key: String, + + /** Intended mode of user interaction */ + val type: AxisType, + + /** Maximum value the axis supports */ + val maxValue: Float, + + /** Minimum value the axis supports */ + val minValue: Float, + + /** Current value the axis is set to */ + val currentValue: Float, + + /** User-renderable name of the axis */ + val name: String, + + /** Description of the axis */ + val description: String, +) { + fun toSetting() = ClockFontAxisSetting(key, currentValue) + + companion object { + fun merge( + fontAxes: List<ClockFontAxis>, + axisSettings: List<ClockFontAxisSetting>, + ): List<ClockFontAxis> { + val result = mutableListOf<ClockFontAxis>() + for (axis in fontAxes) { + val setting = axisSettings.firstOrNull { axis.key == it.key } + val output = setting?.let { axis.copy(currentValue = it.value) } ?: axis + result.add(output) + } + return result + } + } +} + +/** Axis user interaction modes */ +enum class AxisType { + /** Continuous range between minValue & maxValue. */ + Float, + + /** Only minValue & maxValue are valid. No intermediate values between them are allowed. */ + Boolean, +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt new file mode 100644 index 000000000000..544b705c55c5 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockPreviewConfig.kt @@ -0,0 +1,25 @@ +/* + * 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.plugins.clocks + +import android.content.Context + +data class ClockPreviewConfig( + val previewContext: Context, + val isShadeLayoutWide: Boolean, + val isSceneContainerFlagEnabled: Boolean = false, +) diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt index fb9e96c820cf..7426f061b84c 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt @@ -13,35 +13,11 @@ */ package com.android.systemui.plugins.clocks -import android.content.Context -import android.graphics.Rect -import android.graphics.drawable.Drawable -import android.util.DisplayMetrics -import android.view.View -import androidx.constraintlayout.widget.ConstraintSet -import androidx.constraintlayout.widget.ConstraintSet.BOTTOM -import androidx.constraintlayout.widget.ConstraintSet.END -import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID -import androidx.constraintlayout.widget.ConstraintSet.START -import androidx.constraintlayout.widget.ConstraintSet.TOP -import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT -import com.android.internal.annotations.Keep -import com.android.internal.policy.SystemBarUtils -import com.android.systemui.log.core.MessageBuffer import com.android.systemui.plugins.Plugin import com.android.systemui.plugins.annotations.GeneratedImport import com.android.systemui.plugins.annotations.ProtectedInterface import com.android.systemui.plugins.annotations.ProtectedReturn import com.android.systemui.plugins.annotations.ProvidesInterface -import com.android.systemui.plugins.annotations.SimpleProperty -import java.io.PrintWriter -import java.util.Locale -import java.util.TimeZone -import org.json.JSONArray -import org.json.JSONObject - -/** Identifies a clock design */ -typealias ClockId = String /** A Plugin which exposes the ClockProvider interface */ @ProtectedInterface @@ -74,509 +50,8 @@ interface ClockProvider { fun getClockPickerConfig(settings: ClockSettings): ClockPickerConfig } -/** Interface for controlling an active clock */ -@ProtectedInterface -interface ClockController { - @get:SimpleProperty - /** A small version of the clock, appropriate for smaller viewports */ - val smallClock: ClockFaceController - - @get:SimpleProperty - /** A large version of the clock, appropriate when a bigger viewport is available */ - val largeClock: ClockFaceController - - @get:SimpleProperty - /** Determines the way the hosting app should behave when rendering either clock face */ - val config: ClockConfig - - @get:SimpleProperty - /** Events that clocks may need to respond to */ - val events: ClockEvents - - /** Initializes various rendering parameters. If never called, provides reasonable defaults. */ - fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) - - /** Optional method for dumping debug information */ - fun dump(pw: PrintWriter) -} - -/** Interface for a specific clock face version rendered by the clock */ -@ProtectedInterface -interface ClockFaceController { - @get:SimpleProperty - @Deprecated("Prefer use of layout") - /** View that renders the clock face */ - val view: View - - @get:SimpleProperty - /** Layout specification for this clock */ - val layout: ClockFaceLayout - - @get:SimpleProperty - /** Determines the way the hosting app should behave when rendering this clock face */ - val config: ClockFaceConfig - - @get:SimpleProperty - /** Current theme information the clock is using */ - val theme: ThemeConfig - - @get:SimpleProperty - /** Events specific to this clock face */ - val events: ClockFaceEvents - - @get:SimpleProperty - /** Triggers for various animations */ - val animations: ClockAnimations -} - -/** For clocks that want to report debug information */ -data class ClockMessageBuffers( - /** Message buffer for general infra */ - val infraMessageBuffer: MessageBuffer, - - /** Message buffer for small clock renering */ - val smallClockMessageBuffer: MessageBuffer, - - /** Message buffer for large clock rendering */ - val largeClockMessageBuffer: MessageBuffer, -) { - constructor(buffer: MessageBuffer) : this(buffer, buffer, buffer) {} -} - -data class AodClockBurnInModel(val scale: Float, val translationX: Float, val translationY: Float) - -/** Specifies layout information for the clock face */ -@ProtectedInterface -@GeneratedImport("java.util.ArrayList") -@GeneratedImport("android.view.View") -interface ClockFaceLayout { - @get:ProtectedReturn("return new ArrayList<View>();") - /** All clock views to add to the root constraint layout before applying constraints. */ - val views: List<View> - - @ProtectedReturn("return constraints;") - /** Custom constraints to apply to Lockscreen ConstraintLayout. */ - fun applyConstraints(constraints: ConstraintSet): ConstraintSet - - @ProtectedReturn("return constraints;") - /** Custom constraints to apply to preview ConstraintLayout. */ - fun applyPreviewConstraints(context: Context, constraints: ConstraintSet): ConstraintSet - - fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) -} - -/** A ClockFaceLayout that applies the default lockscreen layout to a single view */ -class DefaultClockFaceLayout(val view: View) : ClockFaceLayout { - // both small and large clock should have a container (RelativeLayout in - // SimpleClockFaceController) - override val views = listOf(view) - - override fun applyConstraints(constraints: ConstraintSet): ConstraintSet { - if (views.size != 1) { - throw IllegalArgumentException( - "Should have only one container view when using DefaultClockFaceLayout" - ) - } - return constraints - } - - override fun applyPreviewConstraints( - context: Context, - constraints: ConstraintSet, - ): ConstraintSet { - return applyDefaultPreviewConstraints(context, constraints) - } - - override fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) { - // Default clock doesn't need detailed control of view - } - - companion object { - fun applyDefaultPreviewConstraints( - context: Context, - constraints: ConstraintSet, - ): ConstraintSet { - constraints.apply { - val lockscreenClockViewLargeId = getId(context, "lockscreen_clock_view_large") - constrainWidth(lockscreenClockViewLargeId, WRAP_CONTENT) - constrainHeight(lockscreenClockViewLargeId, WRAP_CONTENT) - constrainMaxHeight(lockscreenClockViewLargeId, 0) - - val largeClockTopMargin = - SystemBarUtils.getStatusBarHeight(context) + - getDimen(context, "small_clock_padding_top") + - getDimen(context, "keyguard_smartspace_top_offset") + - getDimen(context, "date_weather_view_height") + - getDimen(context, "enhanced_smartspace_height") - connect(lockscreenClockViewLargeId, TOP, PARENT_ID, TOP, largeClockTopMargin) - connect(lockscreenClockViewLargeId, START, PARENT_ID, START) - connect(lockscreenClockViewLargeId, END, PARENT_ID, END) - - // In preview, we'll show UDFPS icon for UDFPS devices - // and nothing for non-UDFPS devices, - // and we're not planning to add this vide in clockHostView - // so we only need position of device entry icon to constrain clock - // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection - val bottomPaddingPx = getDimen(context, "lock_icon_margin_bottom") - val defaultDensity = - DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() / - DisplayMetrics.DENSITY_DEFAULT.toFloat() - val lockIconRadiusPx = (defaultDensity * 36).toInt() - val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx - - connect(lockscreenClockViewLargeId, BOTTOM, PARENT_ID, BOTTOM, clockBottomMargin) - val smallClockViewId = getId(context, "lockscreen_clock_view") - constrainWidth(smallClockViewId, WRAP_CONTENT) - constrainHeight(smallClockViewId, getDimen(context, "small_clock_height")) - connect( - smallClockViewId, - START, - PARENT_ID, - START, - getDimen(context, "clock_padding_start") + - getDimen(context, "status_view_margin_horizontal"), - ) - val smallClockTopMargin = - getDimen(context, "keyguard_clock_top_margin") + - SystemBarUtils.getStatusBarHeight(context) - connect(smallClockViewId, TOP, PARENT_ID, TOP, smallClockTopMargin) - } - return constraints - } - - fun getId(context: Context, name: String): Int { - val packageName = context.packageName - val res = context.packageManager.getResourcesForApplication(packageName) - val id = res.getIdentifier(name, "id", packageName) - return id - } - - fun getDimen(context: Context, name: String): Int { - val packageName = context.packageName - val res = context.packageManager.getResourcesForApplication(packageName) - val id = res.getIdentifier(name, "dimen", packageName) - return if (id == 0) 0 else res.getDimensionPixelSize(id) - } - } -} - -/** Events that should call when various rendering parameters change */ -@ProtectedInterface -interface ClockEvents { - @get:ProtectedReturn("return false;") - /** Set to enable or disable swipe interaction */ - var isReactiveTouchInteractionEnabled: Boolean // TODO(b/364664388): Remove/Rename - - /** Call whenever timezone changes */ - fun onTimeZoneChanged(timeZone: TimeZone) - - /** Call whenever the text time format changes (12hr vs 24hr) */ - fun onTimeFormatChanged(is24Hr: Boolean) - - /** Call whenever the locale changes */ - fun onLocaleChanged(locale: Locale) - - /** Call whenever the weather data should update */ - fun onWeatherDataChanged(data: WeatherData) - - /** Call with alarm information */ - fun onAlarmDataChanged(data: AlarmData) - - /** Call with zen/dnd information */ - fun onZenDataChanged(data: ZenData) - - /** Update reactive axes for this clock */ - fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) -} - -/** Axis setting value for a clock */ -data class ClockFontAxisSetting( - /** Axis key; matches ClockFontAxis.key */ - val key: String, - - /** Value to set this axis to */ - val value: Float, -) { - companion object { - private val KEY_AXIS_KEY = "key" - private val KEY_AXIS_VALUE = "value" - - fun toJson(setting: ClockFontAxisSetting): JSONObject { - return JSONObject().apply { - put(KEY_AXIS_KEY, setting.key) - put(KEY_AXIS_VALUE, setting.value) - } - } - - fun toJson(settings: List<ClockFontAxisSetting>): JSONArray { - return JSONArray().apply { - for (axis in settings) { - put(toJson(axis)) - } - } - } - - fun fromJson(jsonObj: JSONObject): ClockFontAxisSetting { - return ClockFontAxisSetting( - key = jsonObj.getString(KEY_AXIS_KEY), - value = jsonObj.getDouble(KEY_AXIS_VALUE).toFloat(), - ) - } - - fun fromJson(jsonArray: JSONArray): List<ClockFontAxisSetting> { - val result = mutableListOf<ClockFontAxisSetting>() - for (i in 0..jsonArray.length() - 1) { - val obj = jsonArray.getJSONObject(i) - if (obj == null) continue - result.add(fromJson(obj)) - } - return result - } - - fun toFVar(settings: List<ClockFontAxisSetting>): String { - val sb = StringBuilder() - for (axis in settings) { - if (sb.length > 0) sb.append(", ") - sb.append("'${axis.key}' ${axis.value.toInt()}") - } - return sb.toString() - } - } -} - -/** Methods which trigger various clock animations */ -@ProtectedInterface -interface ClockAnimations { - /** Runs an enter animation (if any) */ - fun enter() - - /** Sets how far into AOD the device currently is. */ - fun doze(fraction: Float) - - /** Sets how far into the folding animation the device is. */ - fun fold(fraction: Float) - - /** Runs the battery animation (if any). */ - fun charge() - - /** - * Runs when the clock's position changed during the move animation. - * - * @param fromLeft the [View.getLeft] position of the clock, before it started moving. - * @param direction the direction in which it is moving. A positive number means right, and - * negative means left. - * @param fraction fraction of the clock movement. 0 means it is at the beginning, and 1 means - * it finished moving. - * @deprecated use {@link #onPositionUpdated(float, float)} instead. - */ - fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) - - /** - * Runs when the clock's position changed during the move animation. - * - * @param distance is the total distance in pixels to offset the glyphs when animation - * completes. Negative distance means we are animating the position towards the center. - * @param fraction fraction of the clock movement. 0 means it is at the beginning, and 1 means - * it finished moving. - */ - fun onPositionUpdated(distance: Float, fraction: Float) - - /** - * Runs when swiping clock picker, swipingFraction: 1.0 -> clock is scaled up in the preview, - * 0.0 -> clock is scaled down in the shade; previewRatio is previewSize / screenSize - */ - fun onPickerCarouselSwiping(swipingFraction: Float) -} - -/** Events that have specific data about the related face */ -@ProtectedInterface -interface ClockFaceEvents { - /** Call every time tick */ - fun onTimeTick() - - /** - * Call whenever the theme or seedColor is updated - * - * Theme can be specific to the clock face. - * - isDarkTheme -> clock should be light - * - !isDarkTheme -> clock should be dark - */ - fun onThemeChanged(theme: ThemeConfig) - - /** - * Call whenever font settings change. Pass in a target font size in pixels. The specific clock - * design is allowed to ignore this target size on a case-by-case basis. - */ - fun onFontSettingChanged(fontSizePx: Float) - - /** - * Target region information for the clock face. For small clock, this will match the bounds of - * the parent view mostly, but have a target height based on the height of the default clock. - * For large clocks, the parent view is the entire device size, but most clocks will want to - * render within the centered targetRect to avoid obstructing other elements. The specified - * targetRegion is relative to the parent view. - */ - fun onTargetRegionChanged(targetRegion: Rect?) - - /** Called to notify the clock about its display. */ - fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) -} - -data class ThemeConfig(val isDarkTheme: Boolean, val seedColor: Int?) - -/** Tick rates for clocks */ -enum class ClockTickRate(val value: Int) { - PER_MINUTE(2), // Update the clock once per minute. - PER_SECOND(1), // Update the clock once per second. - PER_FRAME(0), // Update the clock every second. -} +/** Identifies a clock design */ +typealias ClockId = String /** Some data about a clock design */ data class ClockMetadata(val clockId: ClockId) - -data class ClockPickerConfig -@JvmOverloads -constructor( - val id: String, - - /** Localized name of the clock */ - val name: String, - - /** Localized accessibility description for the clock */ - val description: String, - - /* Static & lightweight thumbnail version of the clock */ - val thumbnail: Drawable, - - /** True if the clock will react to tone changes in the seed color */ - val isReactiveToTone: Boolean = true, - - /** Font axes that can be modified on this clock */ - val axes: List<ClockFontAxis> = listOf(), -) - -/** Represents an Axis that can be modified */ -data class ClockFontAxis( - /** Axis key, not user renderable */ - val key: String, - - /** Intended mode of user interaction */ - val type: AxisType, - - /** Maximum value the axis supports */ - val maxValue: Float, - - /** Minimum value the axis supports */ - val minValue: Float, - - /** Current value the axis is set to */ - val currentValue: Float, - - /** User-renderable name of the axis */ - val name: String, - - /** Description of the axis */ - val description: String, -) { - fun toSetting() = ClockFontAxisSetting(key, currentValue) - - companion object { - fun merge( - fontAxes: List<ClockFontAxis>, - axisSettings: List<ClockFontAxisSetting>, - ): List<ClockFontAxis> { - val result = mutableListOf<ClockFontAxis>() - for (axis in fontAxes) { - val setting = axisSettings.firstOrNull { axis.key == it.key } - val output = setting?.let { axis.copy(currentValue = it.value) } ?: axis - result.add(output) - } - return result - } - } -} - -/** Axis user interaction modes */ -enum class AxisType { - /** Continuous range between minValue & maxValue. */ - Float, - - /** Only minValue & maxValue are valid. No intermediate values between them are allowed. */ - Boolean, -} - -/** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */ -data class ClockConfig( - val id: String, - - /** Localized name of the clock */ - val name: String, - - /** Localized accessibility description for the clock */ - val description: String, - - /** Transition to AOD should move smartspace like large clock instead of small clock */ - val useAlternateSmartspaceAODTransition: Boolean = false, - - /** Deprecated version of isReactiveToTone; moved to ClockPickerConfig */ - @Deprecated("TODO(b/352049256): Remove in favor of ClockPickerConfig.isReactiveToTone") - val isReactiveToTone: Boolean = true, - - /** True if the clock is large frame clock, which will use weather in compose. */ - val useCustomClockScene: Boolean = false, -) - -/** Render configuration options for a clock face. Modifies the way SystemUI behaves. */ -data class ClockFaceConfig( - /** Expected interval between calls to onTimeTick. Can always reduce to PER_MINUTE in AOD. */ - val tickRate: ClockTickRate = ClockTickRate.PER_MINUTE, - - /** Call to check whether the clock consumes weather data */ - val hasCustomWeatherDataDisplay: Boolean = false, - - /** - * Whether this clock has a custom position update animation. If true, the keyguard will call - * `onPositionUpdated` to notify the clock of a position update animation. If false, a default - * animation will be used (e.g. a simple translation). - */ - val hasCustomPositionUpdatedAnimation: Boolean = false, - - /** True if the clock is large frame clock, which will use weatherBlueprint in compose. */ - val useCustomClockScene: Boolean = false, -) - -/** Structure for keeping clock-specific settings */ -@Keep -data class ClockSettings( - val clockId: ClockId? = null, - val seedColor: Int? = null, - val axes: List<ClockFontAxisSetting> = listOf(), -) { - // Exclude metadata from equality checks - var metadata: JSONObject = JSONObject() - - companion object { - private val KEY_CLOCK_ID = "clockId" - private val KEY_SEED_COLOR = "seedColor" - private val KEY_METADATA = "metadata" - private val KEY_AXIS_LIST = "axes" - - fun toJson(setting: ClockSettings): JSONObject { - return JSONObject().apply { - put(KEY_CLOCK_ID, setting.clockId) - put(KEY_SEED_COLOR, setting.seedColor) - put(KEY_METADATA, setting.metadata) - put(KEY_AXIS_LIST, ClockFontAxisSetting.toJson(setting.axes)) - } - } - - fun fromJson(json: JSONObject): ClockSettings { - val clockId = if (!json.isNull(KEY_CLOCK_ID)) json.getString(KEY_CLOCK_ID) else null - val seedColor = if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null - val axisList = json.optJSONArray(KEY_AXIS_LIST)?.let(ClockFontAxisSetting::fromJson) - return ClockSettings(clockId, seedColor, axisList ?: listOf()).apply { - metadata = json.optJSONObject(KEY_METADATA) ?: JSONObject() - } - } - } -} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt new file mode 100644 index 000000000000..6128c00f3843 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockSettings.kt @@ -0,0 +1,110 @@ +/* + * 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.plugins.clocks + +import com.android.internal.annotations.Keep +import org.json.JSONArray +import org.json.JSONObject + +@Keep +/** Structure for keeping clock-specific settings */ +data class ClockSettings( + val clockId: ClockId? = null, + val seedColor: Int? = null, + val axes: List<ClockFontAxisSetting> = listOf(), +) { + // Exclude metadata from equality checks + var metadata: JSONObject = JSONObject() + + companion object { + private val KEY_CLOCK_ID = "clockId" + private val KEY_SEED_COLOR = "seedColor" + private val KEY_METADATA = "metadata" + private val KEY_AXIS_LIST = "axes" + + fun toJson(setting: ClockSettings): JSONObject { + return JSONObject().apply { + put(KEY_CLOCK_ID, setting.clockId) + put(KEY_SEED_COLOR, setting.seedColor) + put(KEY_METADATA, setting.metadata) + put(KEY_AXIS_LIST, ClockFontAxisSetting.toJson(setting.axes)) + } + } + + fun fromJson(json: JSONObject): ClockSettings { + val clockId = if (!json.isNull(KEY_CLOCK_ID)) json.getString(KEY_CLOCK_ID) else null + val seedColor = if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null + val axisList = json.optJSONArray(KEY_AXIS_LIST)?.let(ClockFontAxisSetting::fromJson) + return ClockSettings(clockId, seedColor, axisList ?: listOf()).apply { + metadata = json.optJSONObject(KEY_METADATA) ?: JSONObject() + } + } + } +} + +@Keep +/** Axis setting value for a clock */ +data class ClockFontAxisSetting( + /** Axis key; matches ClockFontAxis.key */ + val key: String, + + /** Value to set this axis to */ + val value: Float, +) { + companion object { + private val KEY_AXIS_KEY = "key" + private val KEY_AXIS_VALUE = "value" + + fun toJson(setting: ClockFontAxisSetting): JSONObject { + return JSONObject().apply { + put(KEY_AXIS_KEY, setting.key) + put(KEY_AXIS_VALUE, setting.value) + } + } + + fun toJson(settings: List<ClockFontAxisSetting>): JSONArray { + return JSONArray().apply { + for (axis in settings) { + put(toJson(axis)) + } + } + } + + fun fromJson(jsonObj: JSONObject): ClockFontAxisSetting { + return ClockFontAxisSetting( + key = jsonObj.getString(KEY_AXIS_KEY), + value = jsonObj.getDouble(KEY_AXIS_VALUE).toFloat(), + ) + } + + fun fromJson(jsonArray: JSONArray): List<ClockFontAxisSetting> { + val result = mutableListOf<ClockFontAxisSetting>() + for (i in 0..jsonArray.length() - 1) { + val obj = jsonArray.getJSONObject(i) + if (obj == null) continue + result.add(fromJson(obj)) + } + return result + } + + fun toFVar(settings: List<ClockFontAxisSetting>): String { + val sb = StringBuilder() + for (axis in settings) { + if (sb.length > 0) sb.append(", ") + sb.append("'${axis.key}' ${axis.value.toInt()}") + } + return sb.toString() + } + } +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java index 73626b457dcf..e3cbd6643e5f 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java @@ -120,6 +120,11 @@ public interface QSTile { */ boolean isListening(); + /** + * Return this tile's {@link TileDetailsViewModel} to be used to render the TileDetailsView. + */ + default TileDetailsViewModel getDetailsViewModel() { return null; } + @ProvidesInterface(version = Callback.VERSION) interface Callback { static final int VERSION = 2; diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/TileDetailsViewModel.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/TileDetailsViewModel.kt new file mode 100644 index 000000000000..eab7d7913129 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/TileDetailsViewModel.kt @@ -0,0 +1,37 @@ +/* + * 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.plugins.qs + +import androidx.compose.runtime.Composable + +/** + * The base view model class for rendering the Tile's TileDetailsView. + */ +abstract class TileDetailsViewModel { + + // The view content of this tile details view. + @Composable + abstract fun GetContentView() + + // The callback when the settings button is clicked. Currently this is the same as the on tile + // long press callback + abstract fun clickOnSettingsButton() + + abstract fun getTitle(): String + + abstract fun getSubTitle(): String +} diff --git a/packages/SystemUI/res/drawable/volume_background_top_legacy.xml b/packages/SystemUI/res/drawable/volume_background_top_legacy.xml index 3cd87fc32061..58ae150f2539 100644 --- a/packages/SystemUI/res/drawable/volume_background_top_legacy.xml +++ b/packages/SystemUI/res/drawable/volume_background_top_legacy.xml @@ -16,7 +16,7 @@ --> <layer-list xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> - <item> + <item android:gravity="bottom|end"> <shape> <size android:width="@dimen/volume_dialog_panel_width" /> <solid android:color="?androidprv:attr/colorSurface" /> diff --git a/packages/SystemUI/res/layout/audio_sharing_dialog.xml b/packages/SystemUI/res/layout/audio_sharing_dialog.xml index 7534e159beb0..014b7f7b45df 100644 --- a/packages/SystemUI/res/layout/audio_sharing_dialog.xml +++ b/packages/SystemUI/res/layout/audio_sharing_dialog.xml @@ -84,7 +84,7 @@ android:id="@+id/share_audio_button" style="@style/SettingsLibActionButton" android:textColor="?androidprv:attr/textColorOnAccent" - android:background="@drawable/audio_sharing_rounded_bg_ripple" + android:background="@drawable/audio_sharing_rounded_bg_ripple_top" android:layout_marginBottom="4dp" android:layout_width="0dp" android:layout_height="wrap_content" @@ -101,7 +101,7 @@ android:id="@+id/switch_active_button" style="@style/SettingsLibActionButton" android:textColor="?androidprv:attr/textColorOnAccent" - android:background="@drawable/audio_sharing_rounded_bg_ripple" + android:background="@drawable/audio_sharing_rounded_bg_ripple_bottom" android:layout_marginBottom="20dp" android:layout_width="0dp" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/layout/custom_trace_settings_dialog.xml b/packages/SystemUI/res/layout/custom_trace_settings_dialog.xml index 9e84052956dc..17c2f8946a62 100644 --- a/packages/SystemUI/res/layout/custom_trace_settings_dialog.xml +++ b/packages/SystemUI/res/layout/custom_trace_settings_dialog.xml @@ -26,6 +26,8 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="@dimen/qqs_layout_margin_top" + android:minHeight="@dimen/min_clickable_item_size" + android:minWidth="@dimen/min_clickable_item_size" android:textAppearance="@style/TextAppearance.Dialog.Body.Message" /> <TextView @@ -34,6 +36,8 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginTop="@dimen/qqs_layout_margin_top" + android:minHeight="@dimen/min_clickable_item_size" + android:minWidth="@dimen/min_clickable_item_size" android:textAppearance="@style/TextAppearance.Dialog.Body.Message" /> <!-- Attach to Bugreport Switch --> @@ -58,9 +62,9 @@ <Switch android:id="@+id/attach_to_bugreport_switch" android:layout_width="wrap_content" - android:minHeight="@dimen/screenrecord_option_icon_size" android:layout_height="wrap_content" android:gravity="end" + style="@style/ScreenRecord.Switch" android:layout_gravity="fill_vertical" android:layout_weight="0" /> </LinearLayout> @@ -87,9 +91,9 @@ <Switch android:id="@+id/winscope_switch" android:layout_width="wrap_content" - android:minHeight="@dimen/screenrecord_option_icon_size" android:layout_height="wrap_content" android:gravity="end" + style="@style/ScreenRecord.Switch" android:layout_gravity="fill_vertical" android:layout_weight="0" /> </LinearLayout> @@ -116,9 +120,9 @@ <Switch android:id="@+id/trace_debuggable_apps_switch" android:layout_width="wrap_content" - android:minHeight="@dimen/screenrecord_option_icon_size" android:layout_height="wrap_content" android:gravity="end" + style="@style/ScreenRecord.Switch" android:layout_gravity="fill_vertical" android:layout_weight="0" /> </LinearLayout> @@ -145,9 +149,9 @@ <Switch android:id="@+id/long_traces_switch" android:layout_width="wrap_content" - android:minHeight="@dimen/screenrecord_option_icon_size" android:layout_height="wrap_content" android:gravity="end" + style="@style/ScreenRecord.Switch" android:layout_gravity="fill_vertical" android:layout_weight="0" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout/record_issue_dialog.xml b/packages/SystemUI/res/layout/record_issue_dialog.xml index e30ae6e695fe..b2a8c4ce96db 100644 --- a/packages/SystemUI/res/layout/record_issue_dialog.xml +++ b/packages/SystemUI/res/layout/record_issue_dialog.xml @@ -38,6 +38,8 @@ android:drawableEnd="@drawable/arrow_pointing_down" android:layout_marginTop="@dimen/qqs_layout_margin_top" android:focusable="false" + android:minHeight="@dimen/min_clickable_item_size" + android:minWidth="@dimen/min_clickable_item_size" android:clickable="true" /> <!-- Screen Record Switch --> @@ -72,10 +74,10 @@ <Switch android:id="@+id/screenrecord_switch" android:layout_width="wrap_content" - android:minHeight="@dimen/screenrecord_option_icon_size" android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="fill_vertical" + style="@style/ScreenRecord.Switch" android:layout_weight="0" android:contentDescription="@string/quick_settings_screen_record_label" /> </LinearLayout> @@ -112,11 +114,11 @@ <Switch android:id="@+id/bugreport_switch" android:layout_width="wrap_content" - android:minHeight="@dimen/screenrecord_option_icon_size" android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="fill_vertical" android:layout_weight="0" + style="@style/ScreenRecord.Switch" android:contentDescription="@string/qs_record_issue_bug_report" /> </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/volume_dialog_legacy.xml b/packages/SystemUI/res/layout/volume_dialog_legacy.xml index 9010ab764da0..d44b6a8534f2 100644 --- a/packages/SystemUI/res/layout/volume_dialog_legacy.xml +++ b/packages/SystemUI/res/layout/volume_dialog_legacy.xml @@ -93,9 +93,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/volume_background_bottom" - android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding" - android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding" - android:paddingRight="@dimen/volume_dialog_ringer_rows_padding"> + android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"> <com.android.keyguard.AlphaOptimizedImageButton android:id="@+id/settings" android:src="@drawable/horizontal_ellipsis" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 3dd49bfed385..731c4ef463fd 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -753,7 +753,7 @@ <!-- QuickSettings: Bluetooth dialog device in audio sharing default summary [CHAR LIMIT=50]--> <string name="quick_settings_bluetooth_device_audio_sharing">Audio Sharing</string> <!-- QuickSettings: Bluetooth dialog device summary for devices that are capable of audio sharing and switching to active[CHAR LIMIT=NONE]--> - <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active">Tap to switch or share audio</string> + <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active">Supports audio sharing</string> <!-- QuickSettings: Bluetooth dialog device saved default summary [CHAR LIMIT=NONE]--> <string name="quick_settings_bluetooth_device_saved">Saved</string> <!-- QuickSettings: Accessibility label to disconnect a device [CHAR LIMIT=NONE]--> @@ -3760,11 +3760,20 @@ is a component that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] --> <string name="shortcut_helper_customize_mode_title">Customize keyboard shortcuts</string> + <!-- Title at the top of the keyboard shortcut helper remove shortcut dialog. + The helper is a component that shows the user which keyboard shortcuts they can use. Also + allows the user to add/remove custom shortcuts.[CHAR LIMIT=NONE] --> + <string name="shortcut_customize_mode_remove_shortcut_dialog_title">Remove shortcut?</string> <!-- Sub title at the top of the keyboard shortcut helper customization dialog. Explains to the user what action they need to take in the customization dialog to assign a new custom shortcut. - The helper is a component that shows the user which keyboard shortcuts they can use. + The shortcut customize dialog allows users to add/remove custom shortcuts + [CHAR LIMIT=NONE] --> + <string name="shortcut_customize_mode_add_shortcut_description">Press key to assign shortcut</string> + <!-- Sub title at the top of the remove custom shortcut dialog. Explains to the user what action + they're about to take when they click remove shortcut. The shortcut customize dialog allows + users to add/remove custom shortcuts [CHAR LIMIT=NONE] --> - <string name="shortcut_helper_customize_mode_sub_title">Press key to assign shortcut</string> + <string name="shortcut_customize_mode_remove_shortcut_description">This will delete your custom shortcut permanently.</string> <!-- Placeholder text shown in the search box of the keyboard shortcut helper, when the user hasn't typed in anything in the search box yet. The helper is a component that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] --> @@ -3820,6 +3829,10 @@ confirm and assign key combination to selected shortcut. The helper is a component that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] --> <string name="shortcut_helper_customize_dialog_set_shortcut_button_label">Set shortcut</string> + <!-- Label on the remove shortcut button in keyboard shortcut helper customize dialog, that allows user to + confirm and remove previously added custom shortcut. The helper is a component that + shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] --> + <string name="shortcut_helper_customize_dialog_remove_button_label">Remove</string> <!-- Label on the cancel button in keyboard shortcut helper customize dialog, that allows user to cancel and exit shortcut customization dialog, returning to the main shortcut helper page. The helper is a component that shows the user which keyboard shortcuts they can use. diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index e14008a7773b..0381811e29ed 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -831,6 +831,11 @@ <item name="android:textColor">?attr/onSurfaceVariant</item> </style> + <style name="TextAppearance.QSEditTitle" > + <item name="android:fontFamily">"gsf-title-medium-emphasized"</item> + <item name="android:textColor">?attr/onSurfaceVariant</item> + </style> + <style name="QSCustomizeToolbar" parent="@*android:style/Widget.DeviceDefault.Toolbar"> <item name="android:textColor">?attr/onSurface</item> <item name="android:elevation">10dp</item> diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java index b116e297ccbf..dcbacec5b630 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static com.android.systemui.Flags.simPinUseSlotId; import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ACTIVE_DATA_SUB_CHANGED; import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ON_TELEPHONY_CAPABLE; import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_REFRESH_CARRIER_INFO; @@ -368,10 +369,12 @@ public class CarrierTextManager { for (int i = 0; i < numSubs; i++) { int subId = subs.get(i).getSubscriptionId(); + int slotId = subs.get(i).getSimSlotIndex(); carrierNames[i] = ""; subsIds[i] = subId; - subOrderBySlot[subs.get(i).getSimSlotIndex()] = i; - int simState = mKeyguardUpdateMonitor.getSimState(subId); + subOrderBySlot[slotId] = i; + int simState = simPinUseSlotId() ? mKeyguardUpdateMonitor.getSimStateForSlotId(slotId) + : mKeyguardUpdateMonitor.getSimState(subId); CharSequence carrierName = subs.get(i).getCarrierName(); CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName); mLogger.logUpdateLoopStart(subId, simState, String.valueOf(carrierName)); diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java index 8ed675c61edf..6ef7de4a32b5 100644 --- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java +++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java @@ -16,7 +16,10 @@ package com.android.keyguard; +import static com.android.systemui.Flags.gsfBouncer; + import android.content.Context; +import android.graphics.Typeface; import android.text.TextUtils; import android.util.AttributeSet; import android.view.MotionEvent; @@ -122,6 +125,9 @@ public class EmergencyButton extends Button { textId = com.android.internal.R.string.lockscreen_emergency_call; } setText(textId); + if (gsfBouncer()) { + setTypeface(Typeface.create("gsf-title-medium", Typeface.NORMAL)); + } } else { setVisibility(View.GONE); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 6bcacd023c74..fcaccd27a567 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -32,6 +32,7 @@ import static androidx.constraintlayout.widget.ConstraintSet.START; import static androidx.constraintlayout.widget.ConstraintSet.TOP; import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT; +import static com.android.systemui.Flags.gsfBouncer; import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; import static java.lang.Integer.max; @@ -51,6 +52,7 @@ import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Rect; +import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; @@ -1335,6 +1337,9 @@ public class KeyguardSecurityContainer extends ConstraintLayout { true); mUserSwitcherViewGroup = mView.findViewById(R.id.keyguard_bouncer_user_switcher); mUserSwitcher = mView.findViewById(R.id.user_switcher_header); + if (gsfBouncer()) { + mUserSwitcher.setTypeface(Typeface.create("gsf-label-medium", Typeface.NORMAL)); + } } interface UserSwitcherCallback { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 8ca0e807b31c..2c8fff83a821 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -44,6 +44,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.systemui.Flags.simPinBouncerReset; +import static com.android.systemui.Flags.simPinUseSlotId; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED; import android.annotation.AnyThread; @@ -172,7 +173,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -318,6 +318,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final Object mSimDataLockObject = new Object(); HashMap<Integer, SimData> mSimDatas = new HashMap<>(); + HashMap<Integer, SimData> mSimDatasBySlotId = new HashMap<>(); HashMap<Integer, ServiceState> mServiceStates = new HashMap<>(); private int mPhoneState; @@ -616,16 +617,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // It is possible for active subscriptions to become invalid (-1), and these will // not be present in the subscriptionInfo list synchronized (mSimDataLockObject) { - Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator(); + var iter = simPinUseSlotId() ? mSimDatasBySlotId.entrySet().iterator() + : mSimDatas.entrySet().iterator(); + while (iter.hasNext()) { - Map.Entry<Integer, SimData> simData = iter.next(); - if (!activeSubIds.contains(simData.getKey())) { - mSimLogger.logInvalidSubId(simData.getKey()); + SimData data = iter.next().getValue(); + if (!activeSubIds.contains(data.subId)) { + mSimLogger.logInvalidSubId(data.subId, data.slotId); iter.remove(); - SimData data = simData.getValue(); for (int j = 0; j < mCallbacks.size(); j++) { - KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get(); + var cb = mCallbacks.get(j).get(); if (cb != null) { cb.onSimStateChanged(data.subId, data.slotId, data.simState); } @@ -634,10 +636,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } for (int i = 0; i < changedSubscriptions.size(); i++) { - SimData data = mSimDatas.get( - changedSubscriptions.get(i).getSubscriptionId()); + SimData data; + if (simPinUseSlotId()) { + data = mSimDatasBySlotId.get(changedSubscriptions.get(i) + .getSimSlotIndex()); + } else { + data = mSimDatas.get(changedSubscriptions.get(i).getSubscriptionId()); + } for (int j = 0; j < mCallbacks.size(); j++) { - KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get(); + var cb = mCallbacks.get(j).get(); if (cb != null) { cb.onSimStateChanged(data.subId, data.slotId, data.simState); } @@ -3409,12 +3416,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ private void invalidateSlot(int slotId) { synchronized (mSimDataLockObject) { - Iterator<Map.Entry<Integer, SimData>> iter = mSimDatas.entrySet().iterator(); + var iter = simPinUseSlotId() ? mSimDatasBySlotId.entrySet().iterator() + : mSimDatas.entrySet().iterator(); while (iter.hasNext()) { SimData data = iter.next().getValue(); if (data.slotId == slotId && SubscriptionManager.isValidSubscriptionId(data.subId)) { - mSimLogger.logInvalidSubId(data.subId); + mSimLogger.logInvalidSubId(data.subId, data.slotId); iter.remove(); } } @@ -3427,7 +3435,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @VisibleForTesting void handleSimStateChange(int subId, int slotId, int state) { Assert.isMainThread(); - mSimLogger.logSimState(subId, slotId, state); + mSimLogger.logSimState(subId, slotId, TelephonyManager.simStateToString(state)); boolean becameAbsent = ABSENT_SIM_STATE_LIST.contains(state); if (!SubscriptionManager.isValidSubscriptionId(subId)) { @@ -3444,11 +3452,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // TODO(b/327476182): Preserve SIM_STATE_CARD_IO_ERROR sims in a separate data source. synchronized (mSimDataLockObject) { - SimData data = mSimDatas.get(subId); + SimData data = simPinUseSlotId() ? mSimDatasBySlotId.get(slotId) : mSimDatas.get(subId); final boolean changed; if (data == null) { data = new SimData(state, slotId, subId); - mSimDatas.put(subId, data); + if (simPinUseSlotId()) { + mSimDatasBySlotId.put(slotId, data); + } else { + mSimDatas.put(subId, data); + } changed = true; // no data yet; force update } else { changed = (data.simState != state || data.subId != subId || data.slotId != slotId); @@ -3727,7 +3739,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab callback.onTelephonyCapable(mTelephonyCapable); synchronized (mSimDataLockObject) { - for (Entry<Integer, SimData> data : mSimDatas.entrySet()) { + var simDatas = simPinUseSlotId() ? mSimDatasBySlotId : mSimDatas; + for (Entry<Integer, SimData> data : simDatas.entrySet()) { final SimData state = data.getValue(); callback.onSimStateChanged(state.subId, state.slotId, state.simState); } @@ -3860,7 +3873,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ public boolean isSimPinSecure() { synchronized (mSimDataLockObject) { - for (SimData data : mSimDatas.values()) { + var simDatas = simPinUseSlotId() ? mSimDatasBySlotId : mSimDatas; + for (SimData data : simDatas.values()) { if (isSimPinSecure(data.simState)) { return true; } @@ -3870,6 +3884,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } public int getSimState(int subId) { + if (simPinUseSlotId()) { + throw new UnsupportedOperationException("Method not supported with flag " + + "simPinUseSlotId"); + } synchronized (mSimDataLockObject) { if (mSimDatas.containsKey(subId)) { return mSimDatas.get(subId).simState; @@ -3879,12 +3897,32 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + /** + * Find the sim state for a slot id, or SIM_STATE_UNKNOWN if not found. + */ + public int getSimStateForSlotId(int slotId) { + if (!simPinUseSlotId()) { + throw new UnsupportedOperationException("Method not supported without flag " + + "simPinUseSlotId"); + } + synchronized (mSimDataLockObject) { + if (mSimDatasBySlotId.containsKey(slotId)) { + return mSimDatasBySlotId.get(slotId).simState; + } else { + return TelephonyManager.SIM_STATE_UNKNOWN; + } + } + } + private int getSlotId(int subId) { synchronized (mSimDataLockObject) { - if (!mSimDatas.containsKey(subId)) { - refreshSimState(subId, SubscriptionManager.getSlotIndex(subId)); + var simDatas = simPinUseSlotId() ? mSimDatasBySlotId : mSimDatas; + int slotId = SubscriptionManager.getSlotIndex(subId); + int index = simPinUseSlotId() ? slotId : subId; + if (!simDatas.containsKey(index)) { + refreshSimState(subId, slotId); } - SimData simData = mSimDatas.get(subId); + SimData simData = simDatas.get(index); return simData != null ? simData.slotId : SubscriptionManager.INVALID_SUBSCRIPTION_ID; } } @@ -3928,8 +3966,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private boolean refreshSimState(int subId, int slotId) { int state = mTelephonyManager.getSimState(slotId); synchronized (mSimDataLockObject) { - SimData data = mSimDatas.get(subId); - + SimData data = simPinUseSlotId() ? mSimDatasBySlotId.get(slotId) : mSimDatas.get(subId); if (!SubscriptionManager.isValidSubscriptionId(subId)) { invalidateSlot(slotId); } @@ -3937,7 +3974,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean changed; if (data == null) { data = new SimData(state, slotId, subId); - mSimDatas.put(subId, data); + if (simPinUseSlotId()) { + mSimDatasBySlotId.put(slotId, data); + } else { + mSimDatas.put(subId, data); + } changed = true; // no data yet; force update } else { changed = data.simState != state; @@ -4033,10 +4074,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab for (int i = 0; i < list.size(); i++) { final SubscriptionInfo info = list.get(i); final int id = info.getSubscriptionId(); - int slotId = getSlotId(id); - if (state == getSimState(id) && bestSlotId > slotId) { - resultId = id; - bestSlotId = slotId; + final int slotId = info.getSimSlotIndex(); + if (simPinUseSlotId()) { + if (state == getSimStateForSlotId(slotId) && bestSlotId > slotId) { + resultId = id; + bestSlotId = slotId; + } + } else { + if (state == getSimState(id) && bestSlotId > slotId) { + resultId = id; + bestSlotId = slotId; + } } } return resultId; diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 7fe4ec852045..ebde8a3057ce 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -15,11 +15,13 @@ */ package com.android.keyguard; +import static com.android.systemui.Flags.gsfBouncer; import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY; import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.os.PowerManager; @@ -158,6 +160,9 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener { int klondikeColor = Utils.getColorAttr(getContext(), android.R.attr.textColorSecondary) .getDefaultColor(); mDigitText.setTextColor(textColor); + if (gsfBouncer()) { + mDigitText.setTypeface(Typeface.create("gsf-label-large-emphasized", Typeface.NORMAL)); + } mKlondikeText.setTextColor(klondikeColor); if (mAnimator != null) mAnimator.reloadColors(getContext()); diff --git a/packages/SystemUI/src/com/android/keyguard/logging/SimLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/SimLogger.kt index a81698ba3387..fb26c6a00c27 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/SimLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/SimLogger.kt @@ -47,12 +47,15 @@ class SimLogger @Inject constructor(@SimLog private val logBuffer: LogBuffer) { fun log(@CompileTimeConstant msg: String, level: LogLevel) = logBuffer.log(TAG, level, msg) - fun logInvalidSubId(subId: Int) { + fun logInvalidSubId(subId: Int, slotId: Int) { logBuffer.log( TAG, INFO, - { int1 = subId }, - { "Previously active sub id $int1 is now invalid, will remove" }, + { + int1 = subId + int2 = slotId + }, + { "Previously active subId: $int1, slotId: $int2 is now invalid, will remove" }, ) } @@ -94,16 +97,16 @@ class SimLogger @Inject constructor(@SimLog private val logBuffer: LogBuffer) { ) } - fun logSimState(subId: Int, slotId: Int, state: Int) { + fun logSimState(subId: Int, slotId: Int, state: String) { logBuffer.log( TAG, DEBUG, { int1 = subId int2 = slotId - long1 = state.toLong() + str1 = state }, - { "handleSimStateChange(subId=$int1, slotId=$int2, state=$long1)" }, + { "handleSimStateChange(subId=$int1, slotId=$int2, state=$str1)" }, ) } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 7d5cf232bcb9..3794e7bf6b55 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -283,7 +283,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold com.android.internal.R.integer.config_shortAnimTime)); updateDimensions(); - final Size windowFrameSize = restoreMagnificationWindowFrameSizeIfPossible(); + final Size windowFrameSize = restoreMagnificationWindowFrameIndexAndSizeIfPossible(); setMagnificationFrame(windowFrameSize.getWidth(), windowFrameSize.getHeight(), mWindowBounds.width() / 2, mWindowBounds.height() / 2); computeBounceAnimationScale(); @@ -541,7 +541,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return false; } mWindowBounds.set(currentWindowBounds); - final Size windowFrameSize = restoreMagnificationWindowFrameSizeIfPossible(); + final Size windowFrameSize = restoreMagnificationWindowFrameIndexAndSizeIfPossible(); final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width(); final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height(); @@ -787,18 +787,6 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mMagnificationFrame.set(initX, initY, initX + width, initY + height); } - private Size restoreMagnificationWindowFrameSizeIfPossible() { - if (Flags.saveAndRestoreMagnificationSettingsButtons()) { - return restoreMagnificationWindowFrameIndexAndSizeIfPossible(); - } - - if (!mWindowMagnificationFrameSizePrefs.isPreferenceSavedForCurrentDensity()) { - return getDefaultMagnificationWindowFrameSize(); - } - - return mWindowMagnificationFrameSizePrefs.getSizeForCurrentDensity(); - } - private Size restoreMagnificationWindowFrameIndexAndSizeIfPossible() { if (!mWindowMagnificationFrameSizePrefs.isPreferenceSavedForCurrentDensity()) { notifyWindowSizeRestored(MagnificationSize.DEFAULT); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java index ee36c6e8ad35..558c87c456ef 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java @@ -22,8 +22,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.util.Size; -import com.android.systemui.Flags; - /** * Class to handle SharedPreference for window magnification size. */ @@ -52,14 +50,10 @@ final class WindowMagnificationFrameSizePrefs { * Saves the window frame size for current screen density. */ public void saveIndexAndSizeForCurrentDensity(int index, Size size) { - if (Flags.saveAndRestoreMagnificationSettingsButtons()) { - mWindowMagnificationSizePreferences.edit() - .putString(getKey(), - WindowMagnificationFrameSpec.serialize(index, size)).apply(); - } else { - mWindowMagnificationSizePreferences.edit() - .putString(getKey(), size.toString()).apply(); - } + mWindowMagnificationSizePreferences + .edit() + .putString(getKey(), WindowMagnificationFrameSpec.serialize(index, size)) + .apply(); } /** @@ -91,13 +85,9 @@ final class WindowMagnificationFrameSizePrefs { * Gets the size preference for current screen density. */ public Size getSizeForCurrentDensity() { - if (Flags.saveAndRestoreMagnificationSettingsButtons()) { - return WindowMagnificationFrameSpec - .deserialize(mWindowMagnificationSizePreferences.getString(getKey(), null)) - .getSize(); - } else { - return Size.parseSize(mWindowMagnificationSizePreferences.getString(getKey(), null)); - } + return WindowMagnificationFrameSpec.deserialize( + mWindowMagnificationSizePreferences.getString(getKey(), null)) + .getSize(); } } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java index 2f0ca6e6bf9d..9525822ae141 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java @@ -59,7 +59,6 @@ import android.widget.Switch; import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; -import com.android.systemui.Flags; import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView; import com.android.systemui.res.R; import com.android.systemui.util.settings.SecureSettings; @@ -460,12 +459,8 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest mAllowDiagonalScrollingView.setVisibility(View.VISIBLE); mFullScreenButton.setVisibility(View.GONE); if (selectedButtonIndex == MagnificationSize.FULLSCREEN) { - if (Flags.saveAndRestoreMagnificationSettingsButtons()) { - selectedButtonIndex = - windowMagnificationFrameSizePrefs.getIndexForCurrentDensity(); - } else { - selectedButtonIndex = MagnificationSize.CUSTOM; - } + selectedButtonIndex = + windowMagnificationFrameSizePrefs.getIndexForCurrentDensity(); } break; @@ -482,10 +477,8 @@ 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(); - } + selectedButtonIndex = + windowMagnificationFrameSizePrefs.getIndexForCurrentDensity(); } break; diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index e63472684585..1176cb0523c1 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -19,6 +19,7 @@ import static android.provider.Settings.System.SHOW_BATTERY_PERCENT; import static com.android.settingslib.flags.Flags.newStatusBarIcons; import static com.android.systemui.DejankUtils.whitelistIpcs; +import static com.android.systemui.Flags.gsfQuickSettings; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -33,6 +34,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Rect; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.provider.Settings; @@ -384,6 +386,9 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { } float fontHeight = mBatteryPercentView.getPaint().getFontMetricsInt(null); mBatteryPercentView.setLineHeight(TypedValue.COMPLEX_UNIT_PX, fontHeight); + if (gsfQuickSettings()) { + mBatteryPercentView.setTypeface(Typeface.create("gsf-label-large", Typeface.NORMAL)); + } if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor); addView(mBatteryPercentView, new LayoutParams( LayoutParams.WRAP_CONTENT, diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt index 13c72097c06e..4dc2a13480f5 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorImpl.kt @@ -27,7 +27,6 @@ import com.android.settingslib.bluetooth.HearingAidProfile import com.android.settingslib.bluetooth.LeAudioProfile import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast import com.android.settingslib.bluetooth.LocalBluetoothManager -import com.android.settingslib.flags.Flags.audioSharingQsDialogImprovement import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background @@ -69,7 +68,7 @@ constructor( } deviceItem.type == DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE -> { - if (audioSharingQsDialogImprovement()) { + if (audioSharingInteractor.qsDialogImprovementAvailable()) { withContext(mainDispatcher) { delegateFactory .create(deviceItem.cachedBluetoothDevice) diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt index 65f110533573..c4f26cd46bf8 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt @@ -16,10 +16,13 @@ package com.android.systemui.bluetooth.qsdialog +import android.content.Context +import androidx.annotation.WorkerThread import com.android.settingslib.bluetooth.BluetoothUtils import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.LocalBluetoothManager -import com.android.settingslib.bluetooth.onPlaybackStarted +import com.android.settingslib.bluetooth.onBroadcastMetadataChanged +import com.android.settingslib.flags.Flags.audioSharingQsDialogImprovement import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import javax.inject.Inject @@ -52,6 +55,8 @@ interface AudioSharingInteractor { suspend fun startAudioSharing() suspend fun audioSharingAvailable(): Boolean + + suspend fun qsDialogImprovementAvailable(): Boolean } @SysUISingleton @@ -59,11 +64,14 @@ interface AudioSharingInteractor { class AudioSharingInteractorImpl @Inject constructor( + private val context: Context, private val localBluetoothManager: LocalBluetoothManager?, private val audioSharingRepository: AudioSharingRepository, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : AudioSharingInteractor { + private var previewEnabled: Boolean? = null + override val isAudioSharingOn: Flow<Boolean> = flow { emit(audioSharingAvailable()) } .flatMapLatest { isEnabled -> @@ -93,10 +101,10 @@ constructor( isAudioSharingOn .mapNotNull { audioSharingOn -> if (audioSharingOn) { - // onPlaybackStarted could emit multiple times during one - // audio sharing session, we only perform add source on the - // first time - profile.onPlaybackStarted.firstOrNull() + // onBroadcastMetadataChanged could emit multiple times during one + // audio sharing session, we only perform add source on the first + // time + profile.onBroadcastMetadataChanged.firstOrNull() } else { null } @@ -141,6 +149,20 @@ constructor( override suspend fun audioSharingAvailable(): Boolean { return audioSharingRepository.audioSharingAvailable() } + + override suspend fun qsDialogImprovementAvailable(): Boolean { + return withContext(backgroundDispatcher) { + audioSharingQsDialogImprovement() || isAudioSharingPreviewEnabled() + } + } + + @WorkerThread + private fun isAudioSharingPreviewEnabled(): Boolean { + if (previewEnabled == null) { + previewEnabled = BluetoothUtils.isAudioSharingPreviewEnabled(context.contentResolver) + } + return previewEnabled ?: false + } } @SysUISingleton @@ -160,4 +182,6 @@ class AudioSharingInteractorEmptyImpl @Inject constructor() : AudioSharingIntera override suspend fun startAudioSharing() {} override suspend fun audioSharingAvailable(): Boolean = false + + override suspend fun qsDialogImprovementAvailable(): Boolean = false } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt index b255a4f55220..497d8cf2e159 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt @@ -26,10 +26,10 @@ import android.view.ViewGroup import androidx.annotation.DimenRes import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.UiEventLogger import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast -import com.android.settingslib.flags.Flags.audioSharingQsDialogImprovement import com.android.systemui.Prefs import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogTransitionAnimator @@ -56,7 +56,6 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach -import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** ViewModel for Bluetooth Dialog after clicking on the Bluetooth QS tile. */ @@ -145,7 +144,7 @@ constructor( bluetoothDeviceMetadataInteractor.metadataUpdate, if ( audioSharingInteractor.audioSharingAvailable() && - audioSharingQsDialogImprovement() + audioSharingInteractor.qsDialogImprovementAvailable() ) { audioSharingInteractor.audioSourceStateUpdate } else { @@ -165,7 +164,7 @@ constructor( .launchIn(this) if (audioSharingInteractor.audioSharingAvailable()) { - if (audioSharingQsDialogImprovement()) { + if (audioSharingInteractor.qsDialogImprovementAvailable()) { launch { audioSharingInteractor.handleAudioSourceWhenReady() } } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/dagger/AudioSharingModule.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/dagger/AudioSharingModule.kt index 8b5fde384837..afe9a1eec0b6 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/dagger/AudioSharingModule.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/dagger/AudioSharingModule.kt @@ -55,7 +55,10 @@ interface AudioSharingModule { settingsLibAudioSharingRepository: SettingsLibAudioSharingRepository, @Background backgroundDispatcher: CoroutineDispatcher, ): AudioSharingRepository = - if (Flags.enableLeAudioSharing() && localBluetoothManager != null) { + if ( + (Flags.enableLeAudioSharing() || Flags.audioSharingDeveloperOption()) && + localBluetoothManager != null + ) { AudioSharingRepositoryImpl( localBluetoothManager, settingsLibAudioSharingRepository, @@ -72,7 +75,10 @@ interface AudioSharingModule { impl: Lazy<AudioSharingInteractorImpl>, emptyImpl: Lazy<AudioSharingInteractorEmptyImpl>, ): AudioSharingInteractor = - if (Flags.enableLeAudioSharing() && localBluetoothManager != null) { + if ( + (Flags.enableLeAudioSharing() || Flags.audioSharingDeveloperOption()) && + localBluetoothManager != null + ) { impl.get() } else { emptyImpl.get() @@ -85,7 +91,10 @@ interface AudioSharingModule { audioSharingImpl: Lazy<AudioSharingDeviceItemActionInteractorImpl>, impl: Lazy<DeviceItemActionInteractorImpl>, ): DeviceItemActionInteractor = - if (Flags.enableLeAudioSharing() && localBluetoothManager != null) { + if ( + (Flags.enableLeAudioSharing() || Flags.audioSharingDeveloperOption()) && + localBluetoothManager != null + ) { audioSharingImpl.get() } else { impl.get() @@ -97,7 +106,10 @@ interface AudioSharingModule { localBluetoothManager: LocalBluetoothManager? ): List<DeviceItemFactory> = buildList { add(ActiveMediaDeviceItemFactory()) - if (Flags.enableLeAudioSharing() && localBluetoothManager != null) { + if ( + (Flags.enableLeAudioSharing() || Flags.audioSharingDeveloperOption()) && + localBluetoothManager != null + ) { add(AudioSharingMediaDeviceItemFactory(localBluetoothManager)) add(AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)) } @@ -112,7 +124,10 @@ interface AudioSharingModule { localBluetoothManager: LocalBluetoothManager? ): List<DeviceItemType> = buildList { add(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE) - if (Flags.enableLeAudioSharing() && localBluetoothManager != null) { + if ( + (Flags.enableLeAudioSharing() || Flags.audioSharingDeveloperOption()) && + localBluetoothManager != null + ) { add(DeviceItemType.AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE) add(DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE) } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt index 79a11ee88826..ad49fd03c577 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt @@ -17,11 +17,13 @@ package com.android.systemui.bouncer.ui import android.content.Context +import android.graphics.Typeface import android.util.AttributeSet import android.widget.LinearLayout import com.android.keyguard.BouncerKeyguardMessageArea import com.android.keyguard.KeyguardMessageArea import com.android.keyguard.KeyguardMessageAreaController +import com.android.systemui.Flags import com.android.systemui.res.R class BouncerMessageView : LinearLayout { @@ -37,10 +39,20 @@ class BouncerMessageView : LinearLayout { var secondaryMessageView: BouncerKeyguardMessageArea? = null var primaryMessage: KeyguardMessageAreaController<KeyguardMessageArea>? = null var secondaryMessage: KeyguardMessageAreaController<KeyguardMessageArea>? = null + override fun onFinishInflate() { super.onFinishInflate() primaryMessageView = findViewById(R.id.bouncer_primary_message_area) secondaryMessageView = findViewById(R.id.bouncer_secondary_message_area) + + if (Flags.gsfBouncer()) { + primaryMessageView?.apply { + typeface = Typeface.create("gsf-title-large-emphasized", Typeface.NORMAL) + } + secondaryMessageView?.apply { + typeface = Typeface.create("gsf-title-medium-emphasized", Typeface.NORMAL) + } + } } fun init(factory: KeyguardMessageAreaController.Factory) { diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt index ccd953de7d03..5bad9fc9c5d7 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt @@ -16,6 +16,7 @@ package com.android.systemui.brightness.ui.compose +import android.view.MotionEvent import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation @@ -41,6 +42,7 @@ import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource @@ -221,7 +223,16 @@ fun BrightnessSliderContainer( ) .then(if (viewModel.showMirror) Modifier.drawInOverlay() else Modifier) .sliderBackground(containerColor) - .fillMaxWidth(), + .fillMaxWidth() + .pointerInteropFilter { + if ( + it.actionMasked == MotionEvent.ACTION_UP || + it.actionMasked == MotionEvent.ACTION_CANCEL + ) { + viewModel.emitBrightnessTouchForFalsing() + } + false + }, formatter = viewModel::formatValue, hapticsViewModelFactory = viewModel.hapticsViewModelFactory, ) diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt index 1630ee58449f..7df71550d43d 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt @@ -16,12 +16,14 @@ package com.android.systemui.brightness.ui.viewmodel -import androidx.compose.runtime.getValue import android.content.Context import androidx.annotation.StringRes +import androidx.compose.runtime.getValue import com.android.systemui.brightness.domain.interactor.BrightnessPolicyEnforcementInteractor import com.android.systemui.brightness.domain.interactor.ScreenBrightnessInteractor import com.android.systemui.brightness.shared.model.GammaBrightness +import com.android.systemui.classifier.Classifier +import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text @@ -52,6 +54,7 @@ constructor( private val brightnessPolicyEnforcementInteractor: BrightnessPolicyEnforcementInteractor, val hapticsViewModelFactory: SliderHapticsViewModel.Factory, private val brightnessMirrorShowingInteractor: BrightnessMirrorShowingInteractor, + private val falsingInteractor: FalsingInteractor, @Assisted private val supportsMirroring: Boolean, private val brightnessWarningToast: BrightnessWarningToast, ) : ExclusiveActivatable() { @@ -87,6 +90,10 @@ constructor( brightnessWarningToast.show(viewContext, resId) } + fun emitBrightnessTouchForFalsing() { + falsingInteractor.isFalseTouch(Classifier.BRIGHTNESS_SLIDER) + } + /** * As a brightness slider is dragged, the corresponding events should be sent using this method. */ diff --git a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt index 57e9ade30bb5..074b64e0fab0 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt @@ -23,6 +23,7 @@ import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollectorActual import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.FalsingManager +import com.android.systemui.plugins.FalsingManager.Penalty import javax.inject.Inject /** @@ -63,12 +64,13 @@ constructor( * Inserts the given [result] into the falsing system, affecting future runs of the classifier * as if this was a result that had organically happened before. */ - fun updateFalseConfidence( - result: FalsingClassifier.Result, - ) = collector.updateFalseConfidence(result) + fun updateFalseConfidence(result: FalsingClassifier.Result) = + collector.updateFalseConfidence(result) /** Returns `true` if the gesture should be rejected. */ - fun isFalseTouch( - @Classifier.InteractionType interactionType: Int, - ): Boolean = manager.isFalseTouch(interactionType) + fun isFalseTouch(@Classifier.InteractionType interactionType: Int): Boolean = + manager.isFalseTouch(interactionType) + + /** Returns `true` if the tap gesture should be rejected */ + fun isFalseTap(@Penalty penalty: Int): Boolean = manager.isFalseTap(penalty) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index db9a7f5e81ae..d648b9c6442b 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -23,6 +23,7 @@ import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey import com.android.internal.logging.UiEventLogger import com.android.systemui.CoreStartable +import com.android.systemui.Flags.communalHubOnMobile import com.android.systemui.Flags.communalSceneKtfRefactor import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor @@ -216,6 +217,9 @@ constructor( communalSceneInteractor.changeScene( newScene = CommunalScenes.Blank, loggingReason = "hub timeout", + transitionKey = + if (communalHubOnMobile()) CommunalTransitionKeys.SimpleFade + else null, ) uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_TIMEOUT) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt index 5ecf2e6b2551..a339af3694e7 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt @@ -195,7 +195,7 @@ abstract class BaseCommunalViewModel( open fun onDismissCtaTile() {} /** Called as the user starts dragging a widget to reorder. */ - open fun onReorderWidgetStart() {} + open fun onReorderWidgetStart(draggingItemKey: String) {} /** Called as the user finishes dragging a widget to reorder. */ open fun onReorderWidgetEnd() {} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt index 736ed5c7d336..52bf0004cbe4 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt @@ -164,9 +164,8 @@ constructor( ) } - override fun onReorderWidgetStart() { - // Clear selection status - setSelectedKey(null) + override fun onReorderWidgetStart(draggingItemKey: String) { + setSelectedKey(draggingItemKey) _reorderingWidgets.value = true uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START) } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 2e323d40edcd..1fc549469b55 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -75,7 +75,7 @@ import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule; import com.android.systemui.statusbar.notification.dagger.ReferenceNotificationsModule; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.DozeServiceHost; -import com.android.systemui.statusbar.phone.HeadsUpModule; +import com.android.systemui.statusbar.notification.headsup.HeadsUpModule; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentStartableModule; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 7b91eaecd33a..fcc3ea9f7d58 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -25,6 +25,8 @@ import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.biometrics.BiometricNotificationService import com.android.systemui.bouncer.domain.startable.BouncerStartable import com.android.systemui.clipboardoverlay.ClipboardListener +import com.android.systemui.complication.ComplicationTypesUpdater +import com.android.systemui.complication.DreamClockTimeComplication import com.android.systemui.controls.dagger.StartControlsStartableModule import com.android.systemui.dagger.qualifiers.PerUser import com.android.systemui.dreams.AssistantAttentionMonitor @@ -52,7 +54,7 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.ImmersiveModeConfirmation import com.android.systemui.statusbar.gesture.GesturePointerEventListener import com.android.systemui.statusbar.notification.InstantAppNotifier -import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener +import com.android.systemui.statusbar.notification.headsup.StatusBarHeadsUpChangeListener import com.android.systemui.statusbar.policy.BatteryControllerStartable import com.android.systemui.stylus.StylusUsiPowerStartable import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator @@ -337,4 +339,18 @@ abstract class SystemUICoreStartableModule { abstract fun bindDreamOverlayRegistrant( dreamOverlayRegistrant: DreamOverlayRegistrant ): CoreStartable + + /** Inject into DreamClockTimeComplication.Registrant */ + @Binds + @IntoMap + @ClassKey(DreamClockTimeComplication.Registrant::class) + abstract fun bindDreamClockTimeComplicationRegistrant( + registrant: DreamClockTimeComplication.Registrant + ): CoreStartable + + /** Inject into ComplicationTypesUpdater. */ + @Binds + @IntoMap + @ClassKey(ComplicationTypesUpdater::class) + abstract fun bindComplicationTypesUpdater(updater: ComplicationTypesUpdater): CoreStartable } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index dc8ef3f6a827..d6f8957ace33 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -141,7 +141,7 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.ConfigurationControllerModule; import com.android.systemui.statusbar.phone.LetterboxModule; import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.PolicyModule; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 400d09742d13..2e1096fcae36 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -84,30 +84,53 @@ constructor( ) /** + * Emits `true` when the current scene switches to [Scenes.Gone] for the first time after having + * been on [Scenes.Lockscreen]. + * + * Different from [isDeviceEntered] such that the current scene must actually go through + * [Scenes.Gone] to produce a `true`. [isDeviceEntered] also takes into account the navigation + * back stack and will produce a `true` value even when the current scene is still not + * [Scenes.Gone] but the bottommost entry of the navigation back stack switched from + * [Scenes.Lockscreen] to [Scenes.Gone] while the user is staring at another scene. + */ + val isDeviceEnteredDirectly: StateFlow<Boolean> = + sceneInteractor.currentScene + .filter { currentScene -> + currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen + } + .mapLatestConflated { scene -> + if (scene == Scenes.Gone) { + // Make sure device unlock status is definitely unlocked before we + // consider the device "entered". + deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked } + true + } else { + false + } + } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = false, + ) + + /** * Whether the device has been entered (i.e. the lockscreen has been dismissed, by any method). * This can be `false` when the device is unlocked, e.g. when the user still needs to swipe away * the non-secure lockscreen, even though they've already authenticated. * * Note: This does not imply that the lockscreen is visible or not. + * + * Different from [isDeviceEnteredDirectly] such that the current scene doesn't actually have to + * go through [Scenes.Gone] to produce a `true`. [isDeviceEnteredDirectly] doesn't take the + * navigation back stack into account and will only produce a `true` value even when the current + * scene is actually [Scenes.Gone]. */ val isDeviceEntered: StateFlow<Boolean> = combine( // This flow emits true when the currentScene switches to Gone for the first time // after having been on Lockscreen. - sceneInteractor.currentScene - .filter { currentScene -> - currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen - } - .mapLatestConflated { scene -> - if (scene == Scenes.Gone) { - // Make sure device unlock status is definitely unlocked before we - // consider the device "entered". - deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked } - true - } else { - false - } - }, + isDeviceEnteredDirectly, // This flow emits true only if the bottom of the navigation back stack has been // switched from Lockscreen to Gone. In other words, only if the device was unlocked // while visiting at least one scene "above" the Lockscreen scene. diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 408fe831d74c..43b7cedcd767 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -18,6 +18,7 @@ package com.android.systemui.dreams; import static android.service.dreams.Flags.dreamWakeRedirect; +import static com.android.systemui.Flags.communalHubOnMobile; import static com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming; import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_WINDOW_TITLE; import static com.android.systemui.dreams.dagger.DreamModule.DREAM_TOUCH_INSET_MANAGER; @@ -54,12 +55,14 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.policy.PhoneWindow; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.ambient.touch.TouchHandler; import com.android.systemui.ambient.touch.TouchMonitor; import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent; import com.android.systemui.ambient.touch.scrim.ScrimManager; import com.android.systemui.communal.domain.interactor.CommunalInteractor; import com.android.systemui.communal.shared.log.CommunalUiEvent; import com.android.systemui.communal.shared.model.CommunalScenes; +import com.android.systemui.communal.shared.model.CommunalTransitionKeys; import com.android.systemui.complication.dagger.ComplicationComponent; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.dagger.DreamOverlayComponent; @@ -76,8 +79,8 @@ import com.android.systemui.util.concurrency.DelayableExecutor; import kotlinx.coroutines.Job; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.concurrent.CancellationException; import java.util.function.Consumer; @@ -483,11 +486,16 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ final DreamOverlayComponent dreamOverlayComponent = mDreamOverlayComponentFactory.create( mLifecycleOwner, complicationComponent.getComplicationHostViewController(), mTouchInsetManager); + + final ArrayList<TouchHandler> touchHandlers = new ArrayList<>( + List.of(dreamComplicationComponent.getHideComplicationTouchHandler())); + if (!communalHubOnMobile()) { + // Do not add the communal touch handler for glanceable hub v2 since there is no dream + // to hub swipe gesture. + touchHandlers.add(dreamOverlayComponent.getCommunalTouchHandler()); + } final AmbientTouchComponent ambientTouchComponent = mAmbientTouchComponentFactory.create( - mLifecycleOwner, - new HashSet<>(Arrays.asList( - dreamComplicationComponent.getHideComplicationTouchHandler(), - dreamOverlayComponent.getCommunalTouchHandler())), TAG); + mLifecycleOwner, new HashSet<>(touchHandlers), TAG); setLifecycleStateLocked(Lifecycle.State.STARTED); @@ -568,7 +576,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } else { mCommunalInteractor.changeScene(CommunalScenes.Communal, "dream wake requested", - null); + communalHubOnMobile() ? CommunalTransitionKeys.INSTANCE.getSimpleFade() : null); } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt index b37206a4fef7..160574fa2244 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.dreams.ui.viewmodel import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult +import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel @@ -38,6 +39,7 @@ import kotlinx.coroutines.flow.map class DreamUserActionsViewModel @AssistedInject constructor( + private val communalInteractor: CommunalInteractor, private val deviceUnlockedInteractor: DeviceUnlockedInteractor, private val shadeInteractor: ShadeInteractor, ) : UserActionsViewModel() { @@ -50,10 +52,13 @@ constructor( } else { combine( deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked }, + communalInteractor.isCommunalAvailable, shadeInteractor.shadeMode, - ) { isDeviceUnlocked, shadeMode -> + ) { isDeviceUnlocked, isCommunalAvailable, shadeMode -> buildList { - add(Swipe.Start to Scenes.Communal) + if (isCommunalAvailable) { + add(Swipe.Start to Scenes.Communal) + } val bouncerOrGone = if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt index 2a3729b1aeab..c49ba80c660b 100644 --- a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt @@ -27,6 +27,7 @@ import android.os.Bundle import android.os.UserHandle import android.view.accessibility.AccessibilityManager import androidx.core.app.NotificationCompat +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -39,7 +40,6 @@ import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutoria import com.android.systemui.res.R import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import com.android.app.tracing.coroutines.launchTraced as launch /** * A class to show contextual education on UI based on the edu produced from @@ -107,6 +107,7 @@ constructor( } private fun showDialog(model: ContextualEduToastViewModel) { + dialog?.dismiss() dialog = createDialog(model) dialog?.show() } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt index 092a25aa1cad..7bd09b915d1a 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.os.Build import android.os.UserHandle import com.android.systemui.CoreStartable import com.android.systemui.broadcast.BroadcastDispatcher @@ -52,13 +53,13 @@ constructor( receiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - applicationContext.startActivityAsUser( - Intent( - applicationContext, - KeyboardTouchpadTutorialActivity::class.java - ), - UserHandle.SYSTEM - ) + val activityIntent = + Intent(applicationContext, KeyboardTouchpadTutorialActivity::class.java) + if (Build.IS_DEBUGGABLE) { + // helpful for testing different cases but pointless for public builds + intent.extras?.let { activityIntent.putExtras(it) } + } + applicationContext.startActivityAsUser(activityIntent, UserHandle.SYSTEM) } }, filter = IntentFilter("com.android.systemui.action.KEYBOARD_TOUCHPAD_TUTORIAL"), diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialMetricsLogger.kt index 144c5ead1bb8..ae6cbbc4315e 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialMetricsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialMetricsLogger.kt @@ -18,8 +18,8 @@ package com.android.systemui.inputdevice.tutorial import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_ENTRY_POINT_CONTEXTUAL_EDU import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_ENTRY_POINT_SCHEDULER -import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEYBOARD -import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_TOUCHPAD +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_KEYBOARD +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_TOUCHPAD import com.android.systemui.shared.system.SysUiStatsLog import javax.inject.Inject @@ -37,9 +37,9 @@ class KeyboardTouchpadTutorialMetricsLogger @Inject constructor() { val tutorialType = when (tutorialTypeExtra) { - INTENT_TUTORIAL_TYPE_KEYBOARD -> + INTENT_TUTORIAL_SCOPE_KEYBOARD -> SysUiStatsLog.PERIPHERAL_TUTORIAL_LAUNCHED__TUTORIAL_TYPE__KEYBOARD - INTENT_TUTORIAL_TYPE_TOUCHPAD -> + INTENT_TUTORIAL_SCOPE_TOUCHPAD -> SysUiStatsLog.PERIPHERAL_TUTORIAL_LAUNCHED__TUTORIAL_TYPE__TOUCHPAD else -> SysUiStatsLog.PERIPHERAL_TUTORIAL_LAUNCHED__TUTORIAL_TYPE__BOTH } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt index 9dae64921d26..3cba70e39e66 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt @@ -34,10 +34,10 @@ import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSched import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_ENTRY_POINT_KEY import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_ENTRY_POINT_SCHEDULER -import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_BOTH -import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEY -import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEYBOARD -import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_TOUCHPAD +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_ALL +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_KEY +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_KEYBOARD +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_TOUCHPAD import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import javax.inject.Inject @@ -108,7 +108,7 @@ constructor( private fun createPendingIntent(tutorialType: String): PendingIntent { val intent = Intent(context, KeyboardTouchpadTutorialActivity::class.java).apply { - putExtra(INTENT_TUTORIAL_TYPE_KEY, tutorialType) + putExtra(INTENT_TUTORIAL_SCOPE_KEY, tutorialType) putExtra(INTENT_TUTORIAL_ENTRY_POINT_KEY, INTENT_TUTORIAL_ENTRY_POINT_SCHEDULER) flags = Intent.FLAG_ACTIVITY_NEW_TASK } @@ -128,13 +128,13 @@ constructor( NotificationInfo( context.getString(R.string.launch_keyboard_tutorial_notification_title), context.getString(R.string.launch_keyboard_tutorial_notification_content), - INTENT_TUTORIAL_TYPE_KEYBOARD, + INTENT_TUTORIAL_SCOPE_KEYBOARD, ) TutorialType.TOUCHPAD -> NotificationInfo( context.getString(R.string.launch_touchpad_tutorial_notification_title), context.getString(R.string.launch_touchpad_tutorial_notification_content), - INTENT_TUTORIAL_TYPE_TOUCHPAD, + INTENT_TUTORIAL_SCOPE_TOUCHPAD, ) TutorialType.BOTH -> NotificationInfo( @@ -144,7 +144,7 @@ constructor( context.getString( R.string.launch_keyboard_touchpad_tutorial_notification_content ), - INTENT_TUTORIAL_TYPE_BOTH, + INTENT_TUTORIAL_SCOPE_ALL, ) TutorialType.NONE -> null } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt index fee08b31bd93..67b307fb7a36 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt @@ -56,10 +56,13 @@ constructor( ) : ComponentActivity() { companion object { - const val INTENT_TUTORIAL_TYPE_KEY = "tutorial_type" - const val INTENT_TUTORIAL_TYPE_TOUCHPAD = "touchpad" - const val INTENT_TUTORIAL_TYPE_KEYBOARD = "keyboard" - const val INTENT_TUTORIAL_TYPE_BOTH = "both" + const val INTENT_TUTORIAL_SCOPE_KEY = "tutorial_scope" + const val INTENT_TUTORIAL_SCOPE_TOUCHPAD = "touchpad" + const val INTENT_TUTORIAL_SCOPE_TOUCHPAD_BACK = "touchpad_back" + const val INTENT_TUTORIAL_SCOPE_TOUCHPAD_HOME = "touchpad_home" + const val INTENT_TUTORIAL_SCOPE_KEYBOARD = "keyboard" + const val INTENT_TUTORIAL_SCOPE_ALL = "all" + const val INTENT_TUTORIAL_ENTRY_POINT_KEY = "entry_point" const val INTENT_TUTORIAL_ENTRY_POINT_SCHEDULER = "scheduler" const val INTENT_TUTORIAL_ENTRY_POINT_CONTEXTUAL_EDU = "contextual_edu" @@ -94,7 +97,7 @@ constructor( if (savedInstanceState == null) { metricsLogger.logPeripheralTutorialLaunched( intent.getStringExtra(INTENT_TUTORIAL_ENTRY_POINT_KEY), - intent.getStringExtra(INTENT_TUTORIAL_TYPE_KEY), + intent.getStringExtra(INTENT_TUTORIAL_SCOPE_KEY), ) logger.logOpenTutorial(TutorialContext.KEYBOARD_TOUCHPAD_TUTORIAL) } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt index 896bdc068154..eeb4b697a5fc 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt @@ -22,15 +22,21 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.domain.interactor.ConnectionState import com.android.systemui.inputdevice.tutorial.domain.interactor.KeyboardTouchpadConnectionInteractor -import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEY -import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEYBOARD +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_ALL +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_KEY +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_KEYBOARD +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_TOUCHPAD +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_TOUCHPAD_BACK +import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_SCOPE_TOUCHPAD_HOME import com.android.systemui.inputdevice.tutorial.ui.viewmodel.RequiredHardware.KEYBOARD import com.android.systemui.inputdevice.tutorial.ui.viewmodel.RequiredHardware.TOUCHPAD import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.ACTION_KEY import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTURE +import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -42,27 +48,23 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.runningFold -import com.android.app.tracing.coroutines.launchTraced as launch class KeyboardTouchpadTutorialViewModel( private val gesturesInteractor: Optional<TouchpadGesturesInteractor>, private val keyboardTouchpadConnectionInteractor: KeyboardTouchpadConnectionInteractor, private val hasTouchpadTutorialScreens: Boolean, private val logger: InputDeviceTutorialLogger, - handle: SavedStateHandle + handle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver { - private fun startingScreen(handle: SavedStateHandle): Screen { - val tutorialType: String? = handle[INTENT_TUTORIAL_TYPE_KEY] - return if (tutorialType == INTENT_TUTORIAL_TYPE_KEYBOARD) ACTION_KEY else BACK_GESTURE - } - private val _screen = MutableStateFlow(startingScreen(handle)) val screen: Flow<Screen> = _screen.filter { it.canBeShown() } private val _closeActivity: MutableStateFlow<Boolean> = MutableStateFlow(false) val closeActivity: StateFlow<Boolean> = _closeActivity + private val screenSequence: ScreenSequence = chooseScreenSequence(handle) + private val screensBackStack = ArrayDeque(listOf(_screen.value)) private var connectionState: ConnectionState = @@ -105,6 +107,33 @@ class KeyboardTouchpadTutorialViewModel( } } + private fun startingScreen(handle: SavedStateHandle): Screen { + val scope: String? = handle[INTENT_TUTORIAL_SCOPE_KEY] + return when (scope) { + INTENT_TUTORIAL_SCOPE_KEYBOARD -> ACTION_KEY + INTENT_TUTORIAL_SCOPE_TOUCHPAD_HOME -> HOME_GESTURE + INTENT_TUTORIAL_SCOPE_TOUCHPAD, + INTENT_TUTORIAL_SCOPE_ALL, + INTENT_TUTORIAL_SCOPE_TOUCHPAD_BACK -> BACK_GESTURE + else -> { + logger.w("Intent didn't specify tutorial scope, starting with default") + BACK_GESTURE + } + } + } + + private fun chooseScreenSequence(handle: SavedStateHandle): ScreenSequence { + val scope: String? = handle[INTENT_TUTORIAL_SCOPE_KEY] + return if ( + scope == INTENT_TUTORIAL_SCOPE_TOUCHPAD_HOME || + scope == INTENT_TUTORIAL_SCOPE_TOUCHPAD_BACK + ) { + SingleScreenOnly + } else { + AllSupportedScreens + } + } + override fun onCleared() { // this shouldn't be needed as onTutorialInvisible should already clear device state but // it'd be really bad if we'd block gestures/shortcuts after leaving tutorial so just to be @@ -121,13 +150,13 @@ class KeyboardTouchpadTutorialViewModel( } fun onDoneButtonClicked() { - var nextScreen = _screen.value.next() + var nextScreen = screenSequence.nextScreen(_screen.value) while (nextScreen != null) { if (requiredHardwarePresent(nextScreen)) { break } logger.logNextScreenMissingHardware(nextScreen) - nextScreen = nextScreen.next() + nextScreen = screenSequence.nextScreen(nextScreen) } if (nextScreen == null) { logger.d("Final screen reached, closing tutorial") @@ -192,33 +221,44 @@ class KeyboardTouchpadTutorialViewModel( override fun <T : ViewModel> create( key: String, modelClass: Class<T>, - handle: SavedStateHandle + handle: SavedStateHandle, ): T = KeyboardTouchpadTutorialViewModel( gesturesInteractor, keyboardTouchpadConnected, hasTouchpadTutorialScreens, logger, - handle + handle, ) as T } + + private interface ScreenSequence { + fun nextScreen(current: Screen): Screen? + } + + private object AllSupportedScreens : ScreenSequence { + override fun nextScreen(current: Screen): Screen? { + return when (current) { + BACK_GESTURE -> HOME_GESTURE + HOME_GESTURE -> ACTION_KEY + ACTION_KEY -> null + } + } + } + + private object SingleScreenOnly : ScreenSequence { + override fun nextScreen(current: Screen): Screen? = null + } } enum class RequiredHardware { TOUCHPAD, - KEYBOARD + KEYBOARD, } enum class Screen(val requiredHardware: RequiredHardware) { BACK_GESTURE(requiredHardware = TOUCHPAD), HOME_GESTURE(requiredHardware = TOUCHPAD), - ACTION_KEY(requiredHardware = KEYBOARD); - - fun next(): Screen? = - when (this) { - BACK_GESTURE -> HOME_GESTURE - HOME_GESTURE -> ACTION_KEY - ACTION_KEY -> null - } + ACTION_KEY(requiredHardware = KEYBOARD), } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt index 99cafd3daacb..321fd57d3e8b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt @@ -28,6 +28,7 @@ import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RES import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS import android.hardware.input.InputSettings import android.hardware.input.KeyGestureEvent +import android.hardware.input.KeyGestureEvent.KeyGestureType import android.util.Log import androidx.annotation.VisibleForTesting import androidx.compose.runtime.mutableStateOf @@ -44,6 +45,8 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomization import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.settings.UserTracker +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -52,8 +55,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext -import javax.inject.Inject -import kotlin.coroutines.CoroutineContext @SysUISingleton class CustomShortcutCategoriesRepository @@ -65,7 +66,7 @@ constructor( @Background private val bgCoroutineContext: CoroutineContext, private val shortcutCategoriesUtils: ShortcutCategoriesUtils, private val context: Context, - private val inputGestureMaps: InputGestureMaps + private val inputGestureMaps: InputGestureMaps, ) : ShortcutCategoriesRepository { private val userContext: Context @@ -130,9 +131,7 @@ constructor( emptyList() } else { val customInputGesturesForUser: List<InputGestureData> = - if (InputSettings.isCustomizableInputGesturesFeatureFlagEnabled()) { - inputManager.getCustomInputGestures(/* filter= */ null) - } else emptyList() + getCustomInputGestures() val sources = toInternalGroupSources(customInputGesturesForUser) val supportedKeyCodes = shortcutCategoriesUtils.fetchSupportedKeyCodes( @@ -173,16 +172,20 @@ constructor( .addKeyGestureTypeFromShortcutLabel() .addTriggerFromSelectedKeyCombination() .build() - // TODO(b/379648200) add app launch data for application categories shortcut after - // dynamic - // label/icon mapping implementation + // TODO(b/379648200) add app launch data after dynamic label/icon mapping implementation } catch (e: IllegalArgumentException) { Log.w(TAG, "could not add custom shortcut: $e") return null } } - suspend fun confirmAndSetShortcutCurrentlyBeingCustomized(): ShortcutCustomizationRequestResult { + private fun retrieveInputGestureDataForShortcutBeingDeleted(): InputGestureData? { + val keyGestureType = getKeyGestureTypeFromShortcutBeingDeletedLabel() + return getCustomInputGestures().firstOrNull { it.action.keyGestureType() == keyGestureType } + } + + suspend fun confirmAndSetShortcutCurrentlyBeingCustomized(): + ShortcutCustomizationRequestResult { return withContext(bgCoroutineContext) { val inputGestureData = buildInputGestureDataForShortcutBeingCustomized() @@ -201,24 +204,79 @@ constructor( } } + suspend fun deleteShortcutCurrentlyBeingCustomized(): + ShortcutCustomizationRequestResult { + return withContext(bgCoroutineContext) { + val inputGestureData = + retrieveInputGestureDataForShortcutBeingDeleted() + ?: return@withContext ShortcutCustomizationRequestResult.ERROR_OTHER + return@withContext when ( + val result = inputManager.removeCustomInputGesture(inputGestureData) + ) { + CUSTOM_INPUT_GESTURE_RESULT_SUCCESS -> ShortcutCustomizationRequestResult.SUCCESS + else -> { + Log.w( + TAG, + "Attempted to delete shortcut being customized " + + "${_shortcutBeingCustomized.value} but ran into an error. InputGestureData" + + " = $inputGestureData, error code: $result", + ) + ShortcutCustomizationRequestResult.ERROR_OTHER + } + } + } + } + + private fun getCustomInputGestures(): List<InputGestureData> { + return if (InputSettings.isCustomizableInputGesturesFeatureFlagEnabled()) { + inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY) + } else emptyList() + } + private fun Builder.addKeyGestureTypeFromShortcutLabel(): Builder { + val keyGestureType = getKeyGestureTypeFromShortcutBeingCustomizedLabel() + + if (keyGestureType == null) { + Log.w( + TAG, + "Could not find KeyGestureType for shortcut ${_shortcutBeingCustomized.value}", + ) + return this + } + + return setKeyGestureType(keyGestureType) + } + + @KeyGestureType + private fun getKeyGestureTypeFromShortcutBeingCustomizedLabel(): Int? { val shortcutBeingCustomized = getShortcutBeingCustomized() as? ShortcutCustomizationRequestInfo.Add if (shortcutBeingCustomized == null) { - Log.w(TAG, "User requested to set shortcut but shortcut being customized is null") - return this + Log.w( + TAG, + "Requested key gesture type from label but shortcut being customized is null", + ) + return null } - val keyGestureType = - inputGestureMaps.shortcutLabelToKeyGestureTypeMap[shortcutBeingCustomized.label] + return inputGestureMaps.shortcutLabelToKeyGestureTypeMap[shortcutBeingCustomized.label] + } - if (keyGestureType == null) { - Log.w(TAG, "Could not find KeyGestureType for shortcut $shortcutBeingCustomized") - return this + @KeyGestureType + private fun getKeyGestureTypeFromShortcutBeingDeletedLabel(): Int? { + val shortcutBeingCustomized = + getShortcutBeingCustomized() as? ShortcutCustomizationRequestInfo.Delete + + if (shortcutBeingCustomized == null) { + Log.w( + TAG, + "Requested key gesture type from label but shortcut being customized is null", + ) + return null } - return setKeyGestureType(keyGestureType) + return inputGestureMaps.shortcutLabelToKeyGestureTypeMap[shortcutBeingCustomized.label] } private fun Builder.addTriggerFromSelectedKeyCombination(): Builder { @@ -227,7 +285,7 @@ constructor( Log.w( TAG, "User requested to set shortcut but selected key combination is " + - "$selectedKeyCombination", + "$selectedKeyCombination", ) return this } @@ -235,8 +293,7 @@ constructor( return setTrigger( createKeyTrigger( /* keycode = */ selectedKeyCombination.keyCode, - /* modifierState = */ - shortcutCategoriesUtils.removeUnsupportedModifiers( + /* modifierState = */ shortcutCategoriesUtils.removeUnsupportedModifiers( selectedKeyCombination.modifiers ), ) @@ -256,10 +313,8 @@ constructor( val keyTrigger = gestureData.trigger as KeyTrigger val keyGestureType = gestureData.action.keyGestureType() fetchGroupLabelByGestureType(keyGestureType)?.let { groupLabel -> - toInternalKeyboardShortcutInfo( - keyGestureType, - keyTrigger - )?.let { internalKeyboardShortcutInfo -> + toInternalKeyboardShortcutInfo(keyGestureType, keyTrigger)?.let { + internalKeyboardShortcutInfo -> val group = InternalKeyboardShortcutGroup( label = groupLabel, diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt index d228a15e51b4..ecc076178d2d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt @@ -48,9 +48,7 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType. import com.android.systemui.res.R import javax.inject.Inject -class InputGestureMaps -@Inject -constructor(private val context: Context) { +class InputGestureMaps @Inject constructor(private val context: Context) { val gestureToShortcutCategoryTypeMap = mapOf( // System Category @@ -180,9 +178,10 @@ constructor(private val context: Context) { ) val shortcutLabelToKeyGestureTypeMap: Map<String, Int> - get() = gestureToInternalKeyboardShortcutInfoLabelResIdMap.entries.associateBy({ - context.getString(it.value) - }) { - it.key - } + get() = + gestureToInternalKeyboardShortcutInfoLabelResIdMap.entries.associateBy({ + context.getString(it.value) + }) { + it.key + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt index 95bc9f66618c..a0897f293624 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt @@ -36,9 +36,9 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand import com.android.systemui.keyboard.shortcut.shared.model.ShortcutIcon import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory -import kotlinx.coroutines.withContext import javax.inject.Inject import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.withContext class ShortcutCategoriesUtils @Inject diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt index f4e2f05379bb..7743c53c6900 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt @@ -41,7 +41,13 @@ constructor(private val customShortcutRepository: CustomShortcutCategoriesReposi customShortcutRepository.onCustomizationRequested(requestInfo) } - suspend fun confirmAndSetShortcutCurrentlyBeingCustomized(): ShortcutCustomizationRequestResult { + suspend fun confirmAndSetShortcutCurrentlyBeingCustomized(): + ShortcutCustomizationRequestResult { return customShortcutRepository.confirmAndSetShortcutCurrentlyBeingCustomized() } + + suspend fun deleteShortcutCurrentlyBeingCustomized(): + ShortcutCustomizationRequestResult { + return customShortcutRepository.deleteShortcutCurrentlyBeingCustomized() + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt index 813a1fcac65d..464805334c2a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt @@ -18,25 +18,31 @@ package com.android.systemui.keyboard.shortcut.shared.model sealed interface ShortcutCategoryType { val isTrusted: Boolean + val includeInCustomization: Boolean data object System : ShortcutCategoryType { override val isTrusted: Boolean = true + override val includeInCustomization: Boolean = true } data object MultiTasking : ShortcutCategoryType { override val isTrusted: Boolean = true + override val includeInCustomization: Boolean = true } data object InputMethodEditor : ShortcutCategoryType { override val isTrusted: Boolean = false + override val includeInCustomization: Boolean = false } data object AppCategories : ShortcutCategoryType { override val isTrusted: Boolean = true + override val includeInCustomization: Boolean = true } data class CurrentApp(val packageName: String) : ShortcutCategoryType { override val isTrusted: Boolean = false + override val includeInCustomization: Boolean = false } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt index 203228b4a32b..2d3e7f6f6448 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCustomizationRequestInfo.kt @@ -18,8 +18,14 @@ package com.android.systemui.keyboard.shortcut.shared.model sealed interface ShortcutCustomizationRequestInfo { data class Add( - val label: String, - val categoryType: ShortcutCategoryType, - val subCategoryLabel: String, + val label: String = "", + val categoryType: ShortcutCategoryType = ShortcutCategoryType.System, + val subCategoryLabel: String = "", + ) : ShortcutCustomizationRequestInfo + + data class Delete( + val label: String = "", + val categoryType: ShortcutCategoryType = ShortcutCategoryType.System, + val subCategoryLabel: String = "", ) : ShortcutCustomizationRequestInfo } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt index 310078a647e6..f28618bb8cf4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt @@ -27,8 +27,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo -import com.android.systemui.keyboard.shortcut.ui.composable.AssignNewShortcutDialog +import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutCustomizationDialog import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState +import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.AddShortcutDialog +import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.DeleteShortcutDialog import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutCustomizationViewModel import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.statusbar.phone.SystemUIDialogFactory @@ -50,12 +52,11 @@ constructor( override suspend fun onActivated(): Nothing { viewModel.shortcutCustomizationUiState.collect { uiState -> - if ( - uiState is ShortcutCustomizationUiState.AddShortcutDialog && - !uiState.isDialogShowing - ) { - dialog = createAddShortcutDialog().also { it.show() } - viewModel.onAddShortcutDialogShown() + val shouldShowAddDialog = uiState is AddShortcutDialog && !uiState.isDialogShowing + val shouldShowDeleteDialog = uiState is DeleteShortcutDialog && !uiState.isDialogShowing + if (shouldShowDeleteDialog || shouldShowAddDialog) { + dialog = createDialog().also { it.show() } + viewModel.onDialogShown() } else if (uiState is ShortcutCustomizationUiState.Inactive) { dialog?.dismiss() dialog = null @@ -68,25 +69,21 @@ constructor( viewModel.onShortcutCustomizationRequested(requestInfo) } - private fun createAddShortcutDialog(): Dialog { + private fun createDialog(): Dialog { return dialogFactory.create(dialogDelegate = ShortcutCustomizationDialogDelegate()) { dialog -> val uiState by - viewModel.shortcutCustomizationUiState.collectAsStateWithLifecycle( - initialValue = ShortcutCustomizationUiState.Inactive - ) + viewModel.shortcutCustomizationUiState.collectAsStateWithLifecycle( + initialValue = ShortcutCustomizationUiState.Inactive + ) val coroutineScope = rememberCoroutineScope() - AssignNewShortcutDialog( + ShortcutCustomizationDialog( uiState = uiState, - modifier = Modifier - .width(364.dp) - .wrapContentHeight() - .padding(vertical = 24.dp), + modifier = Modifier.width(364.dp).wrapContentHeight().padding(vertical = 24.dp), onKeyPress = { viewModel.onKeyPressed(it) }, onCancel = { dialog.dismiss() }, - onConfirmSetShortcut = { - coroutineScope.launch { viewModel.onSetShortcut() } - }, + onConfirmSetShortcut = { coroutineScope.launch { viewModel.onSetShortcut() } }, + onConfirmDeleteShortcut = { coroutineScope.launch { viewModel.deleteShortcutCurrentlyBeingCustomized() } }, ) dialog.setOnDismissListener { viewModel.onDialogDismissed() } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt index c9b778e96dd1..20040c673994 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt @@ -61,40 +61,74 @@ import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiSt import com.android.systemui.res.R @Composable -fun AssignNewShortcutDialog( +fun ShortcutCustomizationDialog( uiState: ShortcutCustomizationUiState, modifier: Modifier = Modifier, onKeyPress: (KeyEvent) -> Boolean, onCancel: () -> Unit, onConfirmSetShortcut: () -> Unit, + onConfirmDeleteShortcut: () -> Unit, ) { - if (uiState is ShortcutCustomizationUiState.AddShortcutDialog) { - Column(modifier = modifier) { - Title( - uiState.shortcutLabel, - modifier = Modifier.padding(horizontal = 24.dp).width(316.dp), - ) - Description( - modifier = Modifier.padding(top = 24.dp, start = 24.dp, end = 24.dp).width(316.dp) - ) - PromptShortcutModifier( - modifier = - Modifier.padding(top = 24.dp, start = 116.5.dp, end = 116.5.dp) - .width(131.dp) - .height(48.dp), - defaultModifierKey = uiState.defaultCustomShortcutModifierKey, - ) - SelectedKeyCombinationContainer( - shouldShowError = uiState.errorMessage.isNotEmpty(), - onKeyPress = onKeyPress, - pressedKeys = uiState.pressedKeys, - ) - ErrorMessageContainer(uiState.errorMessage) - DialogButtons( - onCancel, - isSetShortcutButtonEnabled = uiState.pressedKeys.isNotEmpty(), - onConfirm = onConfirmSetShortcut, - ) + when (uiState) { + is ShortcutCustomizationUiState.AddShortcutDialog -> { + Column(modifier = modifier) { + Title(uiState.shortcutLabel) + Description( + text = + stringResource( + id = R.string.shortcut_customize_mode_add_shortcut_description + ) + ) + PromptShortcutModifier( + modifier = + Modifier.padding(top = 24.dp, start = 116.5.dp, end = 116.5.dp) + .width(131.dp) + .height(48.dp), + defaultModifierKey = uiState.defaultCustomShortcutModifierKey, + ) + SelectedKeyCombinationContainer( + shouldShowError = uiState.errorMessage.isNotEmpty(), + onKeyPress = onKeyPress, + pressedKeys = uiState.pressedKeys, + ) + ErrorMessageContainer(uiState.errorMessage) + DialogButtons( + onCancel, + isConfirmButtonEnabled = uiState.pressedKeys.isNotEmpty(), + onConfirm = onConfirmSetShortcut, + confirmButtonText = + stringResource( + R.string.shortcut_helper_customize_dialog_set_shortcut_button_label + ), + ) + } + } + is ShortcutCustomizationUiState.DeleteShortcutDialog -> { + Column(modifier) { + Title( + title = + stringResource( + id = R.string.shortcut_customize_mode_remove_shortcut_dialog_title + ) + ) + Description( + text = + stringResource( + id = R.string.shortcut_customize_mode_remove_shortcut_description + ) + ) + DialogButtons( + onCancel = onCancel, + onConfirm = onConfirmDeleteShortcut, + confirmButtonText = + stringResource( + R.string.shortcut_helper_customize_dialog_remove_button_label + ), + ) + } + } + else -> { + /* No-Op */ } } } @@ -102,8 +136,9 @@ fun AssignNewShortcutDialog( @Composable fun DialogButtons( onCancel: () -> Unit, - isSetShortcutButtonEnabled: Boolean, + isConfirmButtonEnabled: Boolean = true, onConfirm: () -> Unit, + confirmButtonText: String, ) { Row( modifier = @@ -126,9 +161,8 @@ fun DialogButtons( color = MaterialTheme.colorScheme.primary, width = 116.dp, contentColor = MaterialTheme.colorScheme.onPrimary, - text = - stringResource(R.string.shortcut_helper_customize_dialog_set_shortcut_button_label), - enabled = isSetShortcutButtonEnabled, + text = confirmButtonText, + enabled = isConfirmButtonEnabled, ) } } @@ -262,23 +296,28 @@ private fun ShortcutTextKey(key: ShortcutKey.Text) { } @Composable -private fun Title(title: String, modifier: Modifier = Modifier) { +private fun Title(title: String) { Text( text = title, style = MaterialTheme.typography.headlineSmall, fontSize = 24.sp, - modifier = modifier.wrapContentSize(Alignment.Center), + modifier = + Modifier.padding(horizontal = 24.dp).width(316.dp).wrapContentSize(Alignment.Center), color = MaterialTheme.colorScheme.onSurface, lineHeight = 32.sp, + fontWeight = FontWeight.W400, ) } @Composable -private fun Description(modifier: Modifier = Modifier) { +private fun Description(text: String) { Text( - text = stringResource(id = R.string.shortcut_helper_customize_mode_sub_title), + text = text, style = MaterialTheme.typography.bodyMedium, - modifier = modifier.wrapContentSize(Alignment.Center), + modifier = + Modifier.padding(top = 24.dp, start = 24.dp, end = 24.dp) + .width(316.dp) + .wrapContentSize(Alignment.Center), color = MaterialTheme.colorScheme.onSurfaceVariant, ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt index b6b5d1716c79..e3675de5d197 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt @@ -111,7 +111,6 @@ import androidx.compose.ui.util.fastForEach import androidx.compose.ui.util.fastForEachIndexed import com.android.compose.modifiers.thenIf import com.android.compose.ui.graphics.painter.rememberDrawablePainter -import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo @@ -122,6 +121,7 @@ import com.android.systemui.keyboard.shortcut.ui.model.IconSource import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState import com.android.systemui.res.R +import com.android.systemui.keyboard.shortcut.shared.model.Shortcut as ShortcutModel import kotlinx.coroutines.delay @Composable @@ -461,15 +461,19 @@ private fun EndSidePanel( SubCategoryContainerDualPane( searchQuery = searchQuery, subCategory = subcategory, - isCustomizing = isCustomizing, - onCustomizationRequested = { label, subCategoryLabel -> - onCustomizationRequested( - ShortcutCustomizationRequestInfo.Add( - label = label, - subCategoryLabel = subCategoryLabel, - categoryType = category.type, - ) - ) + isCustomizing = isCustomizing and category.type.includeInCustomization, + onCustomizationRequested = { requestInfo -> + when (requestInfo) { + is ShortcutCustomizationRequestInfo.Add -> + onCustomizationRequested( + requestInfo.copy(categoryType = category.type) + ) + + is ShortcutCustomizationRequestInfo.Delete -> + onCustomizationRequested( + requestInfo.copy(categoryType = category.type) + ) + } }, ) Spacer(modifier = Modifier.height(8.dp)) @@ -500,7 +504,7 @@ private fun SubCategoryContainerDualPane( searchQuery: String, subCategory: ShortcutSubCategory, isCustomizing: Boolean, - onCustomizationRequested: (String, String) -> Unit = { _: String, _: String -> }, + onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit, ) { Surface( modifier = Modifier.fillMaxWidth(), @@ -522,7 +526,19 @@ private fun SubCategoryContainerDualPane( searchQuery = searchQuery, shortcut = shortcut, isCustomizing = isCustomizing, - onCustomizationRequested = { onCustomizationRequested(it, subCategory.label) }, + onCustomizationRequested = { requestInfo -> + when (requestInfo) { + is ShortcutCustomizationRequestInfo.Add -> + onCustomizationRequested( + requestInfo.copy(subCategoryLabel = subCategory.label) + ) + + is ShortcutCustomizationRequestInfo.Delete -> + onCustomizationRequested( + requestInfo.copy(subCategoryLabel = subCategory.label) + ) + } + }, ) } } @@ -544,7 +560,7 @@ private fun Shortcut( searchQuery: String, shortcut: ShortcutModel, isCustomizing: Boolean = false, - onCustomizationRequested: (String) -> Unit = {}, + onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {}, ) { val interactionSource = remember { MutableInteractionSource() } val isFocused by interactionSource.collectIsFocusedAsState() @@ -572,7 +588,16 @@ private fun Shortcut( modifier = Modifier.weight(.666f), shortcut = shortcut, isCustomizing = isCustomizing, - onAddShortcutRequested = { onCustomizationRequested(shortcut.label) }, + onAddShortcutRequested = { + onCustomizationRequested( + ShortcutCustomizationRequestInfo.Add(label = shortcut.label) + ) + }, + onDeleteShortcutRequested = { + onCustomizationRequested( + ShortcutCustomizationRequestInfo.Delete(label = shortcut.label) + ) + }, ) } } @@ -761,7 +786,7 @@ private fun ShortcutDescriptionText( Text( modifier = modifier, text = textWithHighlightedSearchQuery(shortcut.label, searchQuery), - style = MaterialTheme.typography.bodyMedium, + style = MaterialTheme.typography.titleSmall, color = MaterialTheme.colorScheme.onSurface, ) } @@ -879,7 +904,7 @@ private fun CategoryItemTwoPane( Text( fontSize = 18.sp, color = colors.textColor(selected).value, - style = MaterialTheme.typography.headlineSmall, + style = MaterialTheme.typography.titleSmall, text = label, ) } @@ -975,9 +1000,11 @@ private fun KeyboardSettings(horizontalPadding: Dp, verticalPadding: Dp, onClick ) { Row(verticalAlignment = Alignment.CenterVertically) { Text( - stringResource(id = R.string.shortcut_helper_keyboard_settings_buttons_label), + text = + stringResource(id = R.string.shortcut_helper_keyboard_settings_buttons_label), color = MaterialTheme.colorScheme.onSurfaceVariant, fontSize = 16.sp, + style = MaterialTheme.typography.titleSmall, ) Spacer(modifier = Modifier.weight(1f)) Icon( diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt index adadeebd0769..990257d642ff 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutCustomizationUiState.kt @@ -27,5 +27,7 @@ sealed interface ShortcutCustomizationUiState { val pressedKeys: List<ShortcutKey> = emptyList(), ) : ShortcutCustomizationUiState + data class DeleteShortcutDialog(val isDialogShowing: Boolean) : ShortcutCustomizationUiState + data object Inactive : ShortcutCustomizationUiState } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt index 8178c6a1c705..b467bb481443 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt @@ -29,6 +29,8 @@ import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutCustomiz import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState +import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.AddShortcutDialog +import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState.DeleteShortcutDialog import com.android.systemui.res.R import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -55,7 +57,7 @@ constructor( } } .combine(_shortcutCustomizationUiState) { keys, uiState -> - if (uiState is ShortcutCustomizationUiState.AddShortcutDialog) { + if (uiState is AddShortcutDialog) { uiState.copy(pressedKeys = keys) } else { uiState @@ -66,7 +68,7 @@ constructor( when (requestInfo) { is ShortcutCustomizationRequestInfo.Add -> { _shortcutCustomizationUiState.value = - ShortcutCustomizationUiState.AddShortcutDialog( + AddShortcutDialog( shortcutLabel = requestInfo.label, defaultCustomShortcutModifierKey = shortcutCustomizationInteractor.getDefaultCustomShortcutModifierKey(), @@ -75,14 +77,19 @@ constructor( ) shortcutCustomizationInteractor.onCustomizationRequested(requestInfo) } + + is ShortcutCustomizationRequestInfo.Delete -> { + _shortcutCustomizationUiState.value = DeleteShortcutDialog(isDialogShowing = false) + shortcutCustomizationInteractor.onCustomizationRequested(requestInfo) + } } } - fun onAddShortcutDialogShown() { + fun onDialogShown() { _shortcutCustomizationUiState.update { uiState -> - (uiState as? ShortcutCustomizationUiState.AddShortcutDialog)?.copy( - isDialogShowing = true - ) ?: uiState + (uiState as? AddShortcutDialog)?.copy(isDialogShowing = true) + ?: (uiState as? DeleteShortcutDialog)?.copy(isDialogShowing = true) + ?: uiState } } @@ -115,6 +122,7 @@ constructor( ), ) } + ShortcutCustomizationRequestResult.ERROR_OTHER -> getUiStateWithErrorMessage( uiState = uiState, @@ -125,13 +133,23 @@ constructor( } } + suspend fun deleteShortcutCurrentlyBeingCustomized() { + val result = + shortcutCustomizationInteractor.deleteShortcutCurrentlyBeingCustomized() + + _shortcutCustomizationUiState.update { uiState -> + when (result) { + ShortcutCustomizationRequestResult.SUCCESS -> ShortcutCustomizationUiState.Inactive + else -> uiState + } + } + } + private fun getUiStateWithErrorMessage( uiState: ShortcutCustomizationUiState, errorMessage: String, ): ShortcutCustomizationUiState { - return (uiState as? ShortcutCustomizationUiState.AddShortcutDialog)?.copy( - errorMessage = errorMessage - ) ?: uiState + return (uiState as? AddShortcutDialog)?.copy(errorMessage = errorMessage) ?: uiState } private fun updatePressedKeys(keyEvent: KeyEvent) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 7097c1d2fc4e..d40fe468b0a5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -81,7 +81,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardLockWhileAwakeInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardServiceLockNowInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardStateCallbackInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor; import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier; @@ -330,8 +330,7 @@ public class KeyguardService extends Service { return new FoldGracePeriodProvider(); } }; - private final KeyguardLockWhileAwakeInteractor - mKeyguardLockWhileAwakeInteractor; + private final KeyguardServiceLockNowInteractor mKeyguardServiceLockNowInteractor; @Inject public KeyguardService( @@ -357,7 +356,7 @@ public class KeyguardService extends Service { KeyguardDismissInteractor keyguardDismissInteractor, Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy, KeyguardStateCallbackInteractor keyguardStateCallbackInteractor, - KeyguardLockWhileAwakeInteractor keyguardLockWhileAwakeInteractor) { + KeyguardServiceLockNowInteractor keyguardServiceLockNowInteractor) { super(); mKeyguardViewMediator = keyguardViewMediator; mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher; @@ -389,7 +388,7 @@ public class KeyguardService extends Service { mKeyguardEnabledInteractor = keyguardEnabledInteractor; mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor; mKeyguardDismissInteractor = keyguardDismissInteractor; - mKeyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor; + mKeyguardServiceLockNowInteractor = keyguardServiceLockNowInteractor; } @Override @@ -665,7 +664,7 @@ public class KeyguardService extends Service { if (SceneContainerFlag.isEnabled()) { mDeviceEntryInteractorLazy.get().lockNow(); } else if (KeyguardWmStateRefactor.isEnabled()) { - mKeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout(options); + mKeyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(options); } mKeyguardViewMediator.doKeyguardTimeout(options); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 01ec4d026646..9f131607cb99 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -3943,7 +3943,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } private void notifyDefaultDisplayCallbacks(boolean showing) { - if (SceneContainerFlag.isEnabled()) { + if (SceneContainerFlag.isEnabled() || KeyguardWmStateRefactor.isEnabled()) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt index 0b78be128c69..8906156252a6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt @@ -25,6 +25,7 @@ import com.android.systemui.common.shared.model.Icon import com.android.systemui.communal.data.repository.CommunalSceneRepository import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.shared.model.CommunalScenes +import com.android.systemui.communal.shared.model.CommunalTransitionKeys import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.res.R @@ -98,7 +99,10 @@ constructor( if (SceneContainerFlag.isEnabled) { sceneInteractor.changeScene(Scenes.Communal, "lockscreen to communal from shortcut") } else { - communalSceneRepository.changeScene(CommunalScenes.Communal, null) + communalSceneRepository.changeScene( + CommunalScenes.Communal, + transitionKey = CommunalTransitionKeys.SimpleFade, + ) } return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled } 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 1e672e23970b..deef2a6c3a4c 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 @@ -21,11 +21,13 @@ import android.annotation.SuppressLint import android.app.DreamManager import com.android.app.animation.Interpolators import com.android.app.tracing.coroutines.launchTraced as launch +import com.android.systemui.Flags.communalHubOnMobile import com.android.systemui.Flags.communalSceneKtfRefactor import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.communal.shared.model.CommunalScenes +import com.android.systemui.communal.shared.model.CommunalTransitionKeys import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -48,7 +50,6 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.filter -import com.android.app.tracing.coroutines.launchTraced as launch @OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton @@ -176,6 +177,9 @@ constructor( communalSceneInteractor.changeScene( newScene = CommunalScenes.Communal, loggingReason = "FromDreamingTransitionInteractor", + transitionKey = + if (communalHubOnMobile()) CommunalTransitionKeys.SimpleFade + else null, ) } else { startTransitionTo( 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 631e44aca26d..42cbd7d39248 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 @@ -16,39 +16,52 @@ package com.android.systemui.keyguard.domain.interactor +import com.android.app.tracing.coroutines.launchTraced as launch +import com.android.internal.widget.LockPatternUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.scene.shared.flag.SceneContainerFlag +import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.kotlin.sample import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import com.android.app.tracing.coroutines.launchTraced as launch +import kotlinx.coroutines.withContext /** - * Logic around the keyguard being enabled/disabled, per [KeyguardService]. If the keyguard is not - * enabled, the lockscreen cannot be shown and the device will go from AOD/DOZING directly to GONE. + * Logic around the keyguard being enabled, disabled, or suppressed via adb. If the keyguard is + * disabled or suppressed, the lockscreen cannot be shown and the device will go from AOD/DOZING + * directly to GONE. * * Keyguard can be disabled by selecting Security: "None" in settings, or by apps that hold * permission to do so (such as Phone). Some CTS tests also disable keyguard in onCreate or onStart * rather than simply dismissing the keyguard or setting up the device to have Security: None, for * reasons unknown. + * + * Keyguard can be suppressed by calling "adb shell locksettings set-disabled true", which is + * frequently done in tests. If keyguard is suppressed, it won't show even if the keyguard is + * enabled. If keyguard is not suppressed, then we defer to whether keyguard is enabled or disabled. */ @SysUISingleton class KeyguardEnabledInteractor @Inject constructor( - @Application scope: CoroutineScope, + @Application val scope: CoroutineScope, + @Background val backgroundDispatcher: CoroutineDispatcher, val repository: KeyguardRepository, val biometricSettingsRepository: BiometricSettingsRepository, - keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor, + private val selectedUserInteractor: SelectedUserInteractor, + private val lockPatternUtils: LockPatternUtils, + keyguardDismissTransitionInteractor: dagger.Lazy<KeyguardDismissTransitionInteractor>, internalTransitionInteractor: InternalKeyguardTransitionInteractor, ) { @@ -62,6 +75,10 @@ constructor( * If the keyguard is disabled while we're locked, we will transition to GONE unless we're in * lockdown mode. If the keyguard is re-enabled, we'll transition back to LOCKSCREEN if we were * locked when it was disabled. + * + * Even if the keyguard is enabled, it's possible for it to be suppressed temporarily via adb. + * If you need to respect that adb command, you will need to use + * [isKeyguardEnabledAndNotSuppressed] instead of using this flow. */ val isKeyguardEnabled: StateFlow<Boolean> = repository.isKeyguardEnabled @@ -96,9 +113,9 @@ constructor( val currentTransitionInfo = internalTransitionInteractor.currentTransitionInfoInternal() if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) { - keyguardDismissTransitionInteractor.startDismissKeyguardTransition( - "keyguard disabled" - ) + keyguardDismissTransitionInteractor + .get() + .startDismissKeyguardTransition("keyguard disabled") } } } @@ -116,4 +133,37 @@ constructor( fun isShowKeyguardWhenReenabled(): Boolean { return repository.isShowKeyguardWhenReenabled() } + + /** + * Whether the keyguard is enabled, and has not been suppressed via adb. + * + * There is unfortunately no callback for [isKeyguardSuppressed], which means this can't be a + * flow, since it's ambiguous when we would query the latest suppression value. + */ + suspend fun isKeyguardEnabledAndNotSuppressed(): Boolean { + return isKeyguardEnabled.value && !isKeyguardSuppressed() + } + + /** + * Returns whether the lockscreen has been disabled ("suppressed") via "adb shell locksettings + * set-disabled". If suppressed, we'll ignore all signals that would typically result in showing + * the keyguard, regardless of the value of [isKeyguardEnabled]. + * + * It's extremely confusing to have [isKeyguardEnabled] not be the inverse of "is lockscreen + * disabled", so this method intentionally re-terms it as "suppressed". + * + * Note that if the lockscreen is currently showing when it's suppressed, it will remain visible + * until it's unlocked, at which point it will never re-appear until suppression is removed. + */ + suspend fun isKeyguardSuppressed( + userId: Int = selectedUserInteractor.getSelectedUserId() + ): Boolean { + // isLockScreenDisabled returns true whenever keyguard is not enabled, even if the adb + // command was not used to disable/suppress the lockscreen. To make these booleans as clear + // as possible, only return true if keyguard is suppressed when it otherwise would have + // been enabled. + return withContext(backgroundDispatcher) { + isKeyguardEnabled.value && lockPatternUtils.isLockScreenDisabled(userId) + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt index 0ab3e5c0927f..ce84e71a3562 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt @@ -16,27 +16,16 @@ package com.android.systemui.keyguard.domain.interactor -import android.os.Bundle import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge -/** - * Emitted when we receive a [KeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout] - * call. - * - * Includes a timestamp so it's not conflated by the StateFlow. - */ -data class KeyguardTimeoutWhileAwakeEvent(val timestamp: Long, val options: Bundle?) - /** The reason we're locking while awake, used for logging. */ enum class LockWhileAwakeReason(private val logReason: String) { LOCKDOWN("Lockdown initiated."), @@ -71,10 +60,8 @@ class KeyguardLockWhileAwakeInteractor constructor( biometricSettingsRepository: BiometricSettingsRepository, keyguardEnabledInteractor: KeyguardEnabledInteractor, + keyguardServiceLockNowInteractor: KeyguardServiceLockNowInteractor, ) { - /** Emits whenever a timeout event is received by [KeyguardService]. */ - private val timeoutEvents: MutableStateFlow<KeyguardTimeoutWhileAwakeEvent?> = - MutableStateFlow(null) /** Emits whenever the current user is in lockdown mode. */ private val inLockdown: Flow<LockWhileAwakeReason> = @@ -97,25 +84,19 @@ constructor( /** Emits whenever we should lock while the screen is on, for any reason. */ val lockWhileAwakeEvents: Flow<LockWhileAwakeReason> = merge( - inLockdown, - keyguardReenabled, - timeoutEvents.filterNotNull().map { - LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON - }, + // We're in lockdown, and the keyguard is enabled. If the keyguard is disabled, the + // lockdown button is hidden in the UI, but it's still possible to trigger lockdown in + // tests. + inLockdown + .filter { keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed() } + .map { LockWhileAwakeReason.LOCKDOWN }, + // The keyguard was re-enabled, and it was showing when it was originally disabled. + // Tests currently expect that if the keyguard is re-enabled, it will show even if it's + // suppressed, so we don't check for isKeyguardEnabledAndNotSuppressed() on this flow. + keyguardReenabled.map { LockWhileAwakeReason.KEYGUARD_REENABLED }, + // KeyguardService says we need to lock now, and the lockscreen is enabled. + keyguardServiceLockNowInteractor.lockNowEvents + .filter { keyguardEnabledInteractor.isKeyguardEnabledAndNotSuppressed() } + .map { LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON }, ) - - /** - * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that - * the device locked while the screen was on. - * - * [options] appears to be no longer used, but we'll keep it in this interactor in case that - * turns out not to be true. - */ - fun onKeyguardServiceDoKeyguardTimeout(options: Bundle?) { - timeoutEvents.value = - KeyguardTimeoutWhileAwakeEvent( - timestamp = System.currentTimeMillis(), - options = options, - ) - } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt new file mode 100644 index 000000000000..9ed53ea74316 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.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.annotation.SuppressLint +import android.os.Bundle +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.launch + +/** + * Emitted when we receive a [KeyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout] + * call. + */ +data class KeyguardLockNowEvent(val options: Bundle?) + +/** + * Logic around requests by [KeyguardService] to lock the device right now, even though the device + * is awake and not going to sleep. + * + * This can happen if WM#lockNow() is called, or if the screen is forced to stay awake but the lock + * timeout elapses. + * + * This is not the only way for the device to lock while the screen is on. The other cases, which do + * not directly involve [KeyguardService], are handled in [KeyguardLockWhileAwakeInteractor]. + */ +@SysUISingleton +class KeyguardServiceLockNowInteractor +@Inject +constructor(@Background val backgroundScope: CoroutineScope) { + + /** + * Emits whenever [KeyguardService] receives a call that indicates we should lock the device + * right now, even though the device is awake and not going to sleep. + * + * WARNING: This is only one of multiple reasons the device might need to lock while not going + * to sleep. Unless you're dealing with keyguard internals that specifically need to know that + * we're locking due to a call to doKeyguardTimeout, use + * [KeyguardLockWhileAwakeInteractor.lockWhileAwakeEvents]. + * + * This is fundamentally an event flow, hence the SharedFlow. + */ + @SuppressLint("SharedFlowCreation") + val lockNowEvents: MutableSharedFlow<KeyguardLockNowEvent> = MutableSharedFlow() + + /** + * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that + * the device locked while the screen was on. + * + * [options] appears to be no longer used, but we'll keep it in this interactor in case that + * turns out not to be true. + */ + fun onKeyguardServiceDoKeyguardTimeout(options: Bundle?) { + backgroundScope.launch { lockNowEvents.emit(KeyguardLockNowEvent(options = options)) } + } +} 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 fbc7e2aecbdf..8641dfa5a183 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 @@ -25,6 +25,7 @@ import android.content.Intent import android.content.IntentFilter import android.provider.Settings import android.provider.Settings.Secure +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.widget.LockPatternUtils import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -48,20 +49,21 @@ import javax.inject.Inject import kotlin.math.max import kotlin.math.min import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.map -import com.android.app.tracing.coroutines.launchTraced as launch +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onStart /** * Logic related to the ability to wake directly to GONE from asleep (AOD/DOZING), without going * through LOCKSCREEN or a BOUNCER state. * * This is possible in the following scenarios: - * - The lockscreen is disabled, either from an app request (SUW does this), or by the security + * - The keyguard is not enabled, either from an app request (SUW does this), or by the security * "None" setting. + * - The keyguard was suppressed via adb. * - A biometric authentication event occurred while we were asleep (fingerprint auth, etc). This * specifically is referred to throughout the codebase as "wake and unlock". * - The screen timed out, but the "lock after screen timeout" duration has not elapsed. @@ -86,43 +88,44 @@ constructor( private val lockPatternUtils: LockPatternUtils, private val systemSettings: SystemSettings, private val selectedUserInteractor: SelectedUserInteractor, + keyguardEnabledInteractor: KeyguardEnabledInteractor, + keyguardServiceLockNowInteractor: KeyguardServiceLockNowInteractor, ) { /** - * Whether the lockscreen was disabled as of the last wake/sleep event, according to - * LockPatternUtils. - * - * This will always be true if [repository.isKeyguardServiceEnabled]=false, but it can also be - * true when the keyguard service is enabled if the lockscreen has been disabled via adb using - * the `adb shell locksettings set-disabled true` command, which is often done in tests. - * - * Unlike keyguardServiceEnabled, changes to this value should *not* immediately show or hide - * the keyguard. If the lockscreen is disabled in this way, it will just not show on the next - * sleep/wake. + * Whether the keyguard was suppressed as of the most recent wakefulness event or lockNow + * command. Keyguard suppression can only be queried (there is no callback available), and + * legacy code only queried the value in onStartedGoingToSleep and doKeyguardTimeout. Tests now + * depend on that behavior, so for now, we'll replicate it here. */ - private val isLockscreenDisabled: Flow<Boolean> = - powerInteractor.isAwake.map { isLockscreenDisabled() } + private val shouldSuppressKeyguard = + merge(powerInteractor.isAwake, keyguardServiceLockNowInteractor.lockNowEvents) + .map { keyguardEnabledInteractor.isKeyguardSuppressed() } + // Default to false, so that flows that combine this one emit prior to the first + // wakefulness emission. + .onStart { emit(false) } /** * Whether we can wake from AOD/DOZING directly to GONE, bypassing LOCKSCREEN/BOUNCER states. * * This is possible in the following cases: * - Keyguard is disabled, either from an app request or from security being set to "None". + * - Keyguard is suppressed, via adb locksettings. * - We're wake and unlocking (fingerprint auth occurred while asleep). * - We're allowed to ignore auth and return to GONE, due to timeouts not elapsing. */ val canWakeDirectlyToGone = combine( repository.isKeyguardEnabled, - isLockscreenDisabled, + shouldSuppressKeyguard, repository.biometricUnlockState, repository.canIgnoreAuthAndReturnToGone, ) { keyguardEnabled, - isLockscreenDisabled, + shouldSuppressKeyguard, biometricUnlockState, canIgnoreAuthAndReturnToGone -> - (!keyguardEnabled || isLockscreenDisabled) || + (!keyguardEnabled || shouldSuppressKeyguard) || BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) || canIgnoreAuthAndReturnToGone } @@ -186,9 +189,9 @@ constructor( .sample( transitionInteractor.isCurrentlyIn( Scenes.Gone, - stateWithoutSceneContainer = KeyguardState.GONE + stateWithoutSceneContainer = KeyguardState.GONE, ), - ::Pair + ::Pair, ) .collect { (wakefulness, finishedInGone) -> // Save isAwake for use in onDreamingStarted/onDreamingStopped. @@ -260,7 +263,7 @@ constructor( delayedActionFilter, SYSTEMUI_PERMISSION, null /* scheduler */, - Context.RECEIVER_EXPORTED_UNAUDITED + Context.RECEIVER_EXPORTED_UNAUDITED, ) } @@ -282,7 +285,7 @@ constructor( context, 0, intent, - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE, ) val time = systemClock.elapsedRealtime() + getCanIgnoreAuthAndReturnToGoneDuration() @@ -311,16 +314,6 @@ constructor( } /** - * Returns whether the lockscreen is disabled, either because the keyguard service is disabled - * or because an adb command has disabled the lockscreen. - */ - private fun isLockscreenDisabled( - userId: Int = selectedUserInteractor.getSelectedUserId() - ): Boolean { - return lockPatternUtils.isLockScreenDisabled(userId) - } - - /** * Returns the duration within which we can return to GONE without auth after a screen timeout * (or power button press, if lock instantly is disabled). * @@ -336,7 +329,7 @@ constructor( .getIntForUser( Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, KEYGUARD_CAN_IGNORE_AUTH_DURATION, - userId + userId, ) .toLong() @@ -352,7 +345,7 @@ constructor( .getIntForUser( Settings.System.SCREEN_OFF_TIMEOUT, KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT, - userId + userId, ) .toLong() 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 364b1a9731cc..9df293bf2c15 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 @@ -112,8 +112,10 @@ constructor( } .distinctUntilChanged() - private val isDeviceEntered by lazy { deviceEntryInteractor.get().isDeviceEntered } - private val isDeviceNotEntered by lazy { isDeviceEntered.map { !it } } + private val isDeviceEnteredDirectly by lazy { + deviceEntryInteractor.get().isDeviceEnteredDirectly + } + private val isDeviceNotEnteredDirectly by lazy { isDeviceEnteredDirectly.map { !it } } /** * Surface visibility, which is either determined by the default visibility when not @@ -126,7 +128,7 @@ constructor( sceneInteractor.get().transitionState.flatMapLatestConflated { state -> when { state.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Gone) -> - isDeviceEntered + isDeviceEnteredDirectly state.isTransitioning(from = Scenes.Bouncer, to = Scenes.Gone) -> (state as Transition).progress.map { progress -> progress > @@ -210,7 +212,7 @@ constructor( when (it.currentScene) { in keyguardScenes -> flowOf(true) in nonKeyguardScenes -> flowOf(false) - in keyguardAgnosticScenes -> isDeviceNotEntered + in keyguardAgnosticScenes -> isDeviceNotEnteredDirectly else -> throw IllegalStateException("Unknown scene: ${it.currentScene}") } @@ -220,7 +222,7 @@ constructor( it.isTransitioningSets(from = keyguardScenes) -> flowOf(true) it.isTransitioningSets(from = nonKeyguardScenes) -> flowOf(false) it.isTransitioningSets(from = keyguardAgnosticScenes) -> - isDeviceNotEntered + isDeviceNotEnteredDirectly else -> throw IllegalStateException("Unknown scene: ${it.fromContent}") } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt index 6c03b2489380..ac302dd26365 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt @@ -17,7 +17,7 @@ package com.android.systemui.keyguard.ui.binder -import android.content.Context +import android.content.res.Resources import android.view.View import android.view.View.INVISIBLE import android.view.View.VISIBLE @@ -34,8 +34,8 @@ import com.android.systemui.keyguard.ui.view.layout.sections.setVisibility import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.clocks.ClockController +import com.android.systemui.plugins.clocks.ClockPreviewConfig import com.android.systemui.shared.clocks.ClockRegistry -import kotlin.reflect.KSuspendFunction1 /** Binder for the small clock view, large clock view. */ object KeyguardPreviewClockViewBinder { @@ -66,11 +66,11 @@ object KeyguardPreviewClockViewBinder { @JvmStatic fun bind( - context: Context, rootView: ConstraintLayout, viewModel: KeyguardPreviewClockViewModel, clockRegistry: ClockRegistry, - updateClockAppearance: KSuspendFunction1<ClockController, Unit>, + updateClockAppearance: suspend (ClockController, Resources) -> Unit, + clockPreviewConfig: ClockPreviewConfig, ) { rootView.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -82,7 +82,10 @@ object KeyguardPreviewClockViewBinder { .forEach { rootView.removeView(it) } } lastClock = currentClock - updateClockAppearance(currentClock) + updateClockAppearance( + currentClock, + clockPreviewConfig.previewContext.resources, + ) if (viewModel.shouldHighlightSelectedAffordance) { (currentClock.largeClock.layout.views + @@ -98,7 +101,12 @@ object KeyguardPreviewClockViewBinder { (it.parent as? ViewGroup)?.removeView(it) rootView.addView(it) } - applyPreviewConstraints(context, rootView, currentClock, viewModel) + applyPreviewConstraints( + clockPreviewConfig, + rootView, + currentClock, + viewModel, + ) } } .invokeOnCompletion { @@ -121,14 +129,14 @@ object KeyguardPreviewClockViewBinder { } private fun applyPreviewConstraints( - context: Context, + clockPreviewConfig: ClockPreviewConfig, rootView: ConstraintLayout, previewClock: ClockController, viewModel: KeyguardPreviewClockViewModel, ) { val cs = ConstraintSet().apply { clone(rootView) } - previewClock.largeClock.layout.applyPreviewConstraints(context, cs) - previewClock.smallClock.layout.applyPreviewConstraints(context, cs) + previewClock.largeClock.layout.applyPreviewConstraints(clockPreviewConfig, cs) + previewClock.smallClock.layout.applyPreviewConstraints(clockPreviewConfig, cs) // When selectedClockSize is the initial value, make both clocks invisible to avoid // flickering diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt index baa681282a0b..e89be5d6ae4c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt @@ -17,7 +17,6 @@ package com.android.systemui.keyguard.ui.binder -import android.content.Context import android.view.View import androidx.core.view.isInvisible import androidx.lifecycle.Lifecycle @@ -26,16 +25,16 @@ import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.shared.model.ClockSizeSetting import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.plugins.clocks.ClockPreviewConfig /** Binder for the small clock view, large clock view and smartspace. */ object KeyguardPreviewSmartspaceViewBinder { @JvmStatic fun bind( - previewContext: Context, smartspace: View, - splitShadePreview: Boolean, viewModel: KeyguardPreviewSmartspaceViewModel, + clockPreviewConfig: ClockPreviewConfig, ) { smartspace.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -44,15 +43,9 @@ object KeyguardPreviewSmartspaceViewBinder { val topPadding = when (it) { ClockSizeSetting.DYNAMIC -> - viewModel.getLargeClockSmartspaceTopPadding( - splitShadePreview, - previewContext, - ) + viewModel.getLargeClockSmartspaceTopPadding(clockPreviewConfig) ClockSizeSetting.SMALL -> - viewModel.getSmallClockSmartspaceTopPadding( - splitShadePreview, - previewContext, - ) + viewModel.getSmallClockSmartspaceTopPadding(clockPreviewConfig) } smartspace.setTopPadding(topPadding) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index ab9cffc05667..9924a3bcdd6a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -22,6 +22,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.content.res.Resources import android.graphics.Rect import android.hardware.display.DisplayManager import android.os.Bundle @@ -47,6 +48,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.core.view.isInvisible +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.policy.SystemBarUtils import com.android.keyguard.ClockEventController import com.android.keyguard.KeyguardClockSwitch @@ -57,6 +59,7 @@ import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel import com.android.systemui.coroutines.newTracingContext +import com.android.systemui.customization.R as customR import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -80,9 +83,11 @@ import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessage import com.android.systemui.monet.ColorScheme import com.android.systemui.monet.Style import com.android.systemui.plugins.clocks.ClockController +import com.android.systemui.plugins.clocks.ClockPreviewConfig import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.res.R +import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shared.clocks.ClockRegistry import com.android.systemui.shared.clocks.DefaultClockController @@ -105,7 +110,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.flowOf -import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import org.json.JSONException @@ -352,8 +356,11 @@ constructor( val topPadding: Int = smartspaceViewModel.getLargeClockSmartspaceTopPadding( - previewInSplitShade(), - previewContext, + ClockPreviewConfig( + previewContext, + getPreviewShadeLayoutWide(display!!), + SceneContainerFlag.isEnabled, + ) ) val startPadding: Int = smartspaceViewModel.getSmartspaceStartPadding(previewContext) val endPadding: Int = smartspaceViewModel.getSmartspaceEndPadding(previewContext) @@ -436,11 +443,15 @@ constructor( setUpClock(previewContext, rootView) if (MigrateClocksToBlueprint.isEnabled) { KeyguardPreviewClockViewBinder.bind( - previewContext, keyguardRootView, clockViewModel, clockRegistry, ::updateClockAppearance, + ClockPreviewConfig( + previewContext, + getPreviewShadeLayoutWide(display!!), + SceneContainerFlag.isEnabled, + ), ) } else { KeyguardPreviewClockViewBinder.bind( @@ -455,10 +466,14 @@ constructor( smartSpaceView?.let { KeyguardPreviewSmartspaceViewBinder.bind( - previewContext, it, - previewInSplitShade(), smartspaceViewModel, + clockPreviewConfig = + ClockPreviewConfig( + previewContext, + getPreviewShadeLayoutWide(display!!), + SceneContainerFlag.isEnabled, + ), ) } setupCommunalTutorialIndicator(keyguardRootView) @@ -552,20 +567,14 @@ constructor( val layoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, - resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.small_clock_height - ), + resources.getDimensionPixelSize(customR.dimen.small_clock_height), ) layoutParams.topMargin = SystemBarUtils.getStatusBarHeight(previewContext) + - resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.small_clock_padding_top - ) + resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) smallClockHostView.layoutParams = layoutParams smallClockHostView.setPaddingRelative( - /* start = */ resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.clock_padding_start - ), + /* start = */ resources.getDimensionPixelSize(customR.dimen.clock_padding_start), /* top = */ 0, /* end = */ 0, /* bottom = */ 0, @@ -637,7 +646,7 @@ constructor( onClockChanged() } - private suspend fun updateClockAppearance(clock: ClockController) { + private suspend fun updateClockAppearance(clock: ClockController, resources: Resources) { if (!MigrateClocksToBlueprint.isEnabled) { clockController.clock = clock } @@ -667,6 +676,11 @@ constructor( if (MigrateClocksToBlueprint.isEnabled) { clockController.clock = clock } + // When set clock to clockController,it will reset fontsize based on context.resources + // We need to override it with overlaid resources + clock.largeClock.events.onFontSettingChanged( + resources.getDimensionPixelSize(customR.dimen.large_clock_text_size).toFloat() + ) } private fun onClockChanged() { @@ -676,7 +690,7 @@ constructor( coroutineScope.launch { val clock = clockRegistry.createCurrentClock() clockController.clock = clock - updateClockAppearance(clock) + updateClockAppearance(clock, context.resources) updateLargeClock(clock) updateSmallClock(clock) } @@ -742,12 +756,14 @@ constructor( smallClockHostView.addView(clock.smallClock.view) } - /* - * When multi_crop_preview_ui_flag is on, we can preview portrait in split shadow direction - * or vice versa. So we need to decide preview direction by width and height - */ - private fun previewInSplitShade(): Boolean { - return width > height + private fun getPreviewShadeLayoutWide(display: Display): Boolean { + return if (display.displayId == 0) { + shadeInteractor.isShadeLayoutWide.value + } else { + // For the unfolded preview in a folded screen; it's landscape by default + // For the folded preview in an unfolded screen; it's portrait by default + display.name == "Inner Display" + } } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt index 8622ffc04601..160380bb09bc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.Context import android.view.View +import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.BOTTOM @@ -47,11 +48,15 @@ constructor( visibility = View.GONE } } + override fun addViews(constraintLayout: ConstraintLayout) { if (!MigrateClocksToBlueprint.isEnabled) { return } - + if (emptyView.parent != null) { + // As emptyView is lazy, it might be already attached. + (emptyView.parent as? ViewGroup)?.removeView(emptyView) + } constraintLayout.addView(emptyView) burnInLayer = AodBurnInLayer(context).apply { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt index 6c98d5b01e4e..70bf8bca55b9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt @@ -235,9 +235,7 @@ constructor( val smallClockBottom = keyguardClockViewModel.getSmallClockTopMargin() + - context.resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.small_clock_height - ) + context.resources.getDimensionPixelSize(customR.dimen.small_clock_height) val dateWeatherSmartspaceHeight = getDimen(context, DATE_WEATHER_VIEW_HEIGHT).toFloat() val marginBetweenSmartspaceAndNotification = context.resources.getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt index 3a7a640be85f..6e30e482bda0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel +import android.content.Context import android.content.res.Resources import androidx.annotation.VisibleForTesting import androidx.constraintlayout.helper.widget.Layer @@ -27,7 +28,8 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.shared.model.ClockSize import com.android.systemui.keyguard.shared.model.ClockSizeSetting -import com.android.systemui.res.R +import com.android.systemui.plugins.clocks.ClockPreviewConfig +import com.android.systemui.plugins.clocks.DefaultClockFaceLayout.Companion.getSmallClockTopPadding import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -46,6 +48,7 @@ import kotlinx.coroutines.flow.stateIn class KeyguardClockViewModel @Inject constructor( + val context: Context, keyguardClockInteractor: KeyguardClockInteractor, @Application private val applicationScope: CoroutineScope, aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel, @@ -158,16 +161,15 @@ constructor( ) /** Calculates the top margin for the small clock. */ - fun getSmallClockTopMargin(): Int { - val statusBarHeight = systemBarUtils.getStatusBarHeaderHeightKeyguard() - return if (shadeInteractor.isShadeLayoutWide.value) { - resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin) - - if (SceneContainerFlag.isEnabled) statusBarHeight else 0 - } else { - resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) + - if (!SceneContainerFlag.isEnabled) statusBarHeight else 0 - } - } + fun getSmallClockTopMargin(): Int = + getSmallClockTopPadding( + ClockPreviewConfig( + context, + shadeInteractor.isShadeLayoutWide.value, + SceneContainerFlag.isEnabled, + ), + systemBarUtils.getStatusBarHeaderHeightKeyguard(), + ) val smallClockTopMargin = combine( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt index 65c0f57b76f5..1c4498212502 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt @@ -17,11 +17,12 @@ package com.android.systemui.keyguard.ui.viewmodel import android.content.Context -import com.android.internal.policy.SystemBarUtils import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.shared.model.ClockSizeSetting -import com.android.systemui.res.R +import com.android.systemui.plugins.clocks.ClockPreviewConfig +import com.android.systemui.plugins.clocks.DefaultClockFaceLayout.Companion.getSmallClockTopPadding +import com.android.systemui.statusbar.ui.SystemBarUtilsProxy import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -35,6 +36,7 @@ constructor( interactor: KeyguardClockInteractor, val smartspaceViewModel: KeyguardSmartspaceViewModel, val clockViewModel: KeyguardClockViewModel, + private val systemBarUtils: SystemBarUtilsProxy, ) { val selectedClockSize: StateFlow<ClockSizeSetting> = interactor.selectedClockSize @@ -59,29 +61,18 @@ constructor( return KeyguardSmartspaceViewModel.getSmartspaceEndMargin(context) } - fun getSmallClockSmartspaceTopPadding(splitShadePreview: Boolean, context: Context): Int { - return getSmallClockTopPadding(splitShadePreview, context) + - context.resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.small_clock_height - ) - } - - fun getLargeClockSmartspaceTopPadding(splitShadePreview: Boolean, context: Context): Int { - return getSmallClockTopPadding(splitShadePreview, context) - } - /* * SmallClockTopPadding decides the top position of smartspace */ - private fun getSmallClockTopPadding(splitShadePreview: Boolean, context: Context): Int { - return with(context.resources) { - if (splitShadePreview) { - getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin) - } else { - getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) + - SystemBarUtils.getStatusBarHeight(context) + - getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset) - } - } + fun getSmallClockSmartspaceTopPadding(config: ClockPreviewConfig): Int { + return getSmallClockTopPadding(config, systemBarUtils.getStatusBarHeaderHeightKeyguard()) + + config.previewContext.resources.getDimensionPixelSize(customR.dimen.small_clock_height) + } + + fun getLargeClockSmartspaceTopPadding(clockPreviewConfig: ClockPreviewConfig): Int { + return getSmallClockTopPadding( + clockPreviewConfig, + systemBarUtils.getStatusBarHeaderHeightKeyguard(), + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt index 6f29004d4f3f..618b04700afb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt @@ -23,6 +23,7 @@ import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor +import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -44,6 +45,7 @@ constructor( private val deviceEntryInteractor: DeviceEntryInteractor, private val communalInteractor: CommunalInteractor, private val shadeInteractor: ShadeInteractor, + private val occlusionInteractor: SceneContainerOcclusionInteractor, ) : UserActionsViewModel() { override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) { @@ -57,7 +59,8 @@ constructor( deviceEntryInteractor.isUnlocked, communalInteractor.isCommunalAvailable, shadeInteractor.shadeMode, - ) { isDeviceUnlocked, isCommunalAvailable, shadeMode -> + occlusionInteractor.isOccludingActivityShown, + ) { isDeviceUnlocked, isCommunalAvailable, shadeMode, isOccluded -> buildList { if (isCommunalAvailable) { add(Swipe.Start to Scenes.Communal) @@ -67,7 +70,8 @@ constructor( addAll( when (shadeMode) { - ShadeMode.Single -> singleShadeActions() + ShadeMode.Single -> + singleShadeActions(isDownFromTopEdgeEnabled = !isOccluded) ShadeMode.Split -> splitShadeActions() ShadeMode.Dual -> dualShadeActions() } diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt index 881228d597b0..93ecae32ac17 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt @@ -21,11 +21,11 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.snapshots.StateFactoryMarker import com.android.app.tracing.coroutines.launchTraced as launch import com.android.app.tracing.coroutines.traceCoroutine +import com.android.systemui.log.table.TableLogBuffer import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow -import com.android.app.tracing.coroutines.launchTraced as launch /** * Keeps snapshot/Compose [State]s up-to-date. @@ -47,6 +47,12 @@ class Hydrator( * concatenation or templating. */ private val traceName: String, + /** + * An optional [TableLogBuffer] to log emissions to the states. [traceName] will be used as the + * prefix for the columns logged by this [Hydrator], allowing to aggregate multiple hydrators in + * the same table. + */ + private val tableLogBuffer: TableLogBuffer? = null, ) : ExclusiveActivatable() { private val children = mutableListOf<NamedActivatable>() @@ -62,15 +68,8 @@ class Hydrator( * automatically set on the returned [State]. */ @StateFactoryMarker - fun <T> hydratedStateOf( - traceName: String, - source: StateFlow<T>, - ): State<T> { - return hydratedStateOf( - traceName = traceName, - initialValue = source.value, - source = source, - ) + fun <T> hydratedStateOf(traceName: String, source: StateFlow<T>): State<T> { + return hydratedStateOf(traceName = traceName, initialValue = source.value, source = source) } /** @@ -81,26 +80,44 @@ class Hydrator( * performance findings with actual code. One recommendation: prefer whole string literals * instead of some complex concatenation or templating scheme. Use `null` to disable * performance tracing for this state. + * + * If a [TableLogBuffer] was provided, every emission to the flow will be logged using the + * [traceName] as the column name. For this to work correctly, all the states in the same + * hydrator should have different [traceName]. Use `null` to disable logging for this state. + * * @param initialValue The first value to place on the [State] * @param source The upstream [Flow] to collect from; values emitted to it will be automatically * set on the returned [State]. */ @StateFactoryMarker - fun <T> hydratedStateOf( - traceName: String?, - initialValue: T, - source: Flow<T>, - ): State<T> { + fun <T> hydratedStateOf(traceName: String?, initialValue: T, source: Flow<T>): State<T> { check(!isActive) { "Cannot call hydratedStateOf after Hydrator is already active." } val mutableState = mutableStateOf(initialValue) + traceName?.let { name -> + tableLogBuffer?.logChange( + prefix = this.traceName, + columnName = name, + value = initialValue?.toString(), + isInitial = true, + ) + } children.add( NamedActivatable( traceName = traceName, activatable = object : ExclusiveActivatable() { override suspend fun onActivated(): Nothing { - source.collect { mutableState.value = it } + source.collect { + traceName?.let { name -> + tableLogBuffer?.logChange( + prefix = this@Hydrator.traceName, + columnName = name, + value = it?.toString(), + ) + } + mutableState.value = it + } awaitCancellation() } }, @@ -122,8 +139,5 @@ class Hydrator( } } - private data class NamedActivatable( - val traceName: String?, - val activatable: Activatable, - ) + private data class NamedActivatable(val traceName: String?, val activatable: Activatable) } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt index 35efd751b8fe..7d3094827e17 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt @@ -20,6 +20,7 @@ import android.app.ActivityManager.RunningTaskInfo import android.hardware.display.DisplayManager import android.media.projection.MediaProjectionInfo import android.media.projection.MediaProjectionManager +import android.media.projection.StopReason import android.os.Handler import android.view.ContentRecordingSession import android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY @@ -72,7 +73,7 @@ constructor( } } - override suspend fun stopProjecting() { + override suspend fun stopProjecting(@StopReason stopReason: Int) { withContext(backgroundDispatcher) { logger.log( TAG, @@ -80,7 +81,7 @@ constructor( {}, { "Requesting MediaProjectionManager#stopActiveProjection" }, ) - mediaProjectionManager.stopActiveProjection() + mediaProjectionManager.stopActiveProjection(stopReason) } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt index 50182d7a3b33..a01d8c2c98de 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt @@ -17,6 +17,7 @@ package com.android.systemui.mediaprojection.data.repository import android.app.ActivityManager.RunningTaskInfo +import android.media.projection.StopReason import com.android.systemui.mediaprojection.data.model.MediaProjectionState import kotlinx.coroutines.flow.Flow @@ -27,7 +28,7 @@ interface MediaProjectionRepository { suspend fun switchProjectedTask(task: RunningTaskInfo) /** Stops the currently active projection. */ - suspend fun stopProjecting() + suspend fun stopProjecting(@StopReason stopReason: Int) /** Represents the current [MediaProjectionState]. */ val mediaProjectionState: Flow<MediaProjectionState> diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 80ac2fcd4aa4..0a4e8c660761 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -18,6 +18,7 @@ package com.android.systemui.navigationbar.gestural; import static android.content.pm.ActivityInfo.CONFIG_FONT_SCALE; import static android.view.InputDevice.SOURCE_MOUSE; import static android.view.InputDevice.SOURCE_TOUCHPAD; +import static android.view.MotionEvent.TOOL_TYPE_FINGER; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; import static com.android.systemui.Flags.edgebackGestureHandlerGetRunningTasksBackground; @@ -216,6 +217,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final int mDisplayId; private final UiThreadContext mUiThreadContext; + private final Handler mBgHandler; private final Executor mBackgroundExecutor; private final Rect mPipExcludedBounds = new Rect(); @@ -378,11 +380,14 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack @Override public void onInputDeviceAdded(int deviceId) { if (isTrackpadDevice(deviceId)) { - boolean wasEmpty = mTrackpadsConnected.isEmpty(); - mTrackpadsConnected.add(deviceId); - if (wasEmpty) { - update(); - } + // This updates the gesture handler state and should be running on the main thread. + mUiThreadContext.getHandler().post(() -> { + boolean wasEmpty = mTrackpadsConnected.isEmpty(); + mTrackpadsConnected.add(deviceId); + if (wasEmpty) { + update(); + } + }); } } @@ -391,10 +396,13 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack @Override public void onInputDeviceRemoved(int deviceId) { - mTrackpadsConnected.remove(deviceId); - if (mTrackpadsConnected.isEmpty()) { - update(); - } + // This updates the gesture handler state and should be running on the main thread. + mUiThreadContext.getHandler().post(() -> { + mTrackpadsConnected.remove(deviceId); + if (mTrackpadsConnected.isEmpty()) { + update(); + } + }); } private void update() { @@ -408,12 +416,12 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } private boolean isTrackpadDevice(int deviceId) { + // This is a blocking binder call that should run on a bg thread. InputDevice inputDevice = mInputManager.getInputDevice(deviceId); if (inputDevice == null) { return false; } - return inputDevice.getSources() == (InputDevice.SOURCE_MOUSE - | InputDevice.SOURCE_TOUCHPAD); + return inputDevice.getSources() == (SOURCE_MOUSE | SOURCE_TOUCHPAD); } }; @@ -457,6 +465,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mDisplayId = context.getDisplayId(); mUiThreadContext = uiThreadContext; mBackgroundExecutor = backgroundExecutor; + mBgHandler = bgHandler; mUserTracker = userTracker; mOverviewProxyService = overviewProxyService; mSysUiState = sysUiState; @@ -611,9 +620,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mIsAttached = true; mOverviewProxyService.addCallback(mQuickSwitchListener); mSysUiState.addCallback(mSysUiStateCallback); - mInputManager.registerInputDeviceListener( - mInputDeviceListener, - mUiThreadContext.getHandler()); + mInputManager.registerInputDeviceListener(mInputDeviceListener, mBgHandler); int[] inputDevices = mInputManager.getInputDeviceIds(); for (int inputDeviceId : inputDevices) { mInputDeviceListener.onInputDeviceAdded(inputDeviceId); @@ -1089,8 +1096,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack && isValidTrackpadBackGesture(true /* isTrackpadEvent */); } else { mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets - && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()) - && !isButtonPressFromTrackpad(ev); + && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()) + && !isButtonPressFromTrackpad(ev); } if (mAllowGesture) { mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge); @@ -1202,10 +1209,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } private boolean isButtonPressFromTrackpad(MotionEvent ev) { - // We don't allow back for button press from the trackpad, and yet we do with a mouse. - int sources = InputManager.getInstance().getInputDevice(ev.getDeviceId()).getSources(); - int sourceTrackpad = (SOURCE_MOUSE | SOURCE_TOUCHPAD); - return (sources & sourceTrackpad) == sourceTrackpad && ev.getButtonState() != 0; + return ev.getSource() == (SOURCE_MOUSE | SOURCE_TOUCHPAD) + && ev.getToolType(ev.getActionIndex()) == TOOL_TYPE_FINGER; } private void dispatchToBackAnimation(MotionEvent event) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java index 40613c0edc68..96c0cac53908 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java @@ -149,7 +149,6 @@ import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.data.repository.LightBarControllerStore; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.CentralSurfaces; @@ -259,7 +258,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements private boolean mTransientShownFromGestureOnSystemBar; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; private LightBarController mLightBarController; - private final LightBarControllerStore mLightBarControllerStore; + private final LightBarController mMainLightBarController; + private final LightBarController.Factory mLightBarControllerFactory; private AutoHideController mAutoHideController; private final AutoHideController mMainAutoHideController; private final AutoHideController.Factory mAutoHideControllerFactory; @@ -580,7 +580,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements @Background Executor bgExecutor, UiEventLogger uiEventLogger, NavBarHelper navBarHelper, - LightBarControllerStore lightBarControllerStore, + LightBarController mainLightBarController, + LightBarController.Factory lightBarControllerFactory, AutoHideController mainAutoHideController, AutoHideController.Factory autoHideControllerFactory, Optional<TelecomManager> telecomManagerOptional, @@ -627,7 +628,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mUiEventLogger = uiEventLogger; mNavBarHelper = navBarHelper; mNotificationShadeDepthController = notificationShadeDepthController; - mLightBarControllerStore = lightBarControllerStore; + mMainLightBarController = mainLightBarController; + mLightBarControllerFactory = lightBarControllerFactory; mMainAutoHideController = mainAutoHideController; mAutoHideControllerFactory = autoHideControllerFactory; mTelecomManagerOptional = telecomManagerOptional; @@ -840,7 +842,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements // Unfortunately, we still need it because status bar needs LightBarController // before notifications creation. We cannot directly use getLightBarController() // from NavigationBarFragment directly. - LightBarController lightBarController = mLightBarControllerStore.forDisplay(mDisplayId); + LightBarController lightBarController = mIsOnDefaultDisplay + ? mMainLightBarController : mLightBarControllerFactory.create(mContext); setLightBarController(lightBarController); // TODO(b/118592525): to support multi-display, we start to add something which is diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java index d715f42ed499..dc188c24e02b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java @@ -16,8 +16,11 @@ package com.android.systemui.qs; +import static com.android.systemui.Flags.gsfQuickSettings; + import android.content.ClipData; import android.content.ClipboardManager; +import android.graphics.Typeface; import android.text.TextUtils; import android.view.View; import android.widget.TextView; @@ -64,6 +67,9 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme mRetailModeInteractor = retailModeInteractor; mBuildText = mView.findViewById(R.id.build); + if (gsfQuickSettings()) { + mBuildText.setTypeface(Typeface.create("gsf-body-medium", Typeface.NORMAL)); + } mPageIndicator = mView.findViewById(R.id.footer_page_indicator); mEditButton = mView.findViewById(android.R.id.edit); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt index 21c45c550e08..5167d173647e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -233,6 +233,7 @@ constructor( // Only allow scrolling when we are fully expanded. That way, we don't intercept // swipes in lockscreen (when somehow QS is receiving touches). { (scrollState.canScrollForward && viewModel.isQsFullyExpanded) || isCustomizing }, + viewModel::emitMotionEventForFalsingSwipeNested, ) frame.addView( composeView, @@ -718,7 +719,6 @@ constructor( max = QuickSettingsShade.Dimensions.GridMaxHeight ), - containerViewModel.editModeViewModel::startEditing, ) } } @@ -951,6 +951,7 @@ private class FrameLayoutTouchPassthrough( private val clippingEnabledProvider: () -> Boolean, private val clippingTopProvider: () -> Int, private val canScrollForwardQs: () -> Boolean, + private val emitMotionEventForFalsing: () -> Unit, ) : FrameLayout(context) { override fun isTransformedTouchPointInView( x: Float, @@ -967,6 +968,32 @@ private class FrameLayoutTouchPassthrough( val touchSlop = ViewConfiguration.get(context).scaledTouchSlop var downY = 0f + var preventingIntercept = false + + override fun onTouchEvent(event: MotionEvent): Boolean { + val action = event.actionMasked + when (action) { + MotionEvent.ACTION_DOWN -> { + preventingIntercept = false + if (canScrollVertically(1)) { + // If we can scroll down, make sure we're not intercepted by the parent + preventingIntercept = true + parent?.requestDisallowInterceptTouchEvent(true) + } else if (!canScrollVertically(-1)) { + // Don't pass on the touch to the view, because scrolling will unconditionally + // disallow interception even if we can't scroll. + // if a user can't scroll at all, we should never listen to the touch. + return false + } + } + MotionEvent.ACTION_UP -> { + if (preventingIntercept) { + emitMotionEventForFalsing() + } + } + } + return super.onTouchEvent(event) + } override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { // If there's a touch on this view and we can scroll down, we don't want to be intercepted @@ -974,8 +1001,10 @@ private class FrameLayoutTouchPassthrough( when (action) { MotionEvent.ACTION_DOWN -> { + preventingIntercept = false // If we can scroll down, make sure none of our parents intercepts us. if (canScrollForwardQs()) { + preventingIntercept = true parent?.requestDisallowInterceptTouchEvent(true) } downY = ev.y diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeLog.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeLog.kt new file mode 100644 index 000000000000..5f151eba5c84 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeLog.kt @@ -0,0 +1,24 @@ +/* + * 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.qs.composefragment.dagger + +import javax.inject.Qualifier + +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class QSFragmentComposeLog diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt index 2ec729223a8d..4c9df116bd97 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt @@ -18,7 +18,8 @@ package com.android.systemui.qs.composefragment.dagger import android.content.Context import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.log.table.TableLogBufferFactory import com.android.systemui.qs.flags.QSComposeFragment import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.util.Utils @@ -38,5 +39,14 @@ interface QSFragmentComposeModule { fun providesUsingMedia(@ShadeDisplayAware context: Context): Boolean { return QSComposeFragment.isEnabled && Utils.useQsMediaPlayer(context) } + + @Provides + @SysUISingleton + @QSFragmentComposeLog + fun providesQSFragmentComposeViewModelTableLog( + factory: TableLogBufferFactory + ): TableLogBuffer { + return factory.create("QSFragmentComposeViewModel", 200) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt index e3de6d5152e4..02498d69b83d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt @@ -31,6 +31,8 @@ import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.BouncerPanelExpansionCalculator import com.android.systemui.Dumpable import com.android.systemui.animation.ShadeInterpolation +import com.android.systemui.classifier.Classifier +import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor @@ -39,6 +41,7 @@ import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator +import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QQS import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS @@ -48,6 +51,7 @@ import com.android.systemui.media.dagger.MediaModule.QS_PANEL import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.FooterActionsController +import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeLog import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.qs.panels.domain.interactor.TileSquishinessInteractor @@ -100,7 +104,9 @@ constructor( @ShadeDisplayAware configurationInteractor: ConfigurationInteractor, private val largeScreenHeaderHelper: LargeScreenHeaderHelper, private val squishinessInteractor: TileSquishinessInteractor, + private val falsingInteractor: FalsingInteractor, private val inFirstPageViewModel: InFirstPageViewModel, + @QSFragmentComposeLog private val tableLogBuffer: TableLogBuffer, mediaInRowInLandscapeViewModelFactory: MediaInRowInLandscapeViewModel.Factory, @Named(QUICK_QS_PANEL) val qqsMediaHost: MediaHost, @Named(QS_PANEL) val qsMediaHost: MediaHost, @@ -112,7 +118,7 @@ constructor( private val qqsMediaInRowViewModel = mediaInRowInLandscapeViewModelFactory.create(LOCATION_QQS) private val qsMediaInRowViewModel = mediaInRowInLandscapeViewModelFactory.create(LOCATION_QS) - private val hydrator = Hydrator("QSFragmentComposeViewModel.hydrator") + private val hydrator = Hydrator("QSFragmentComposeViewModel.hydrator", tableLogBuffer) val footerActionsViewModel = footerActionsViewModelFactory.create(lifecycleScope).also { @@ -434,6 +440,10 @@ constructor( } } + fun emitMotionEventForFalsingSwipeNested() { + falsingInteractor.isFalseTouch(Classifier.QS_SWIPE_NESTED) + } + override suspend fun onActivated(): Nothing { initMediaHosts() // init regardless of using media (same as current QS). coroutineScope { diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index f8d408054d25..0e64820c984e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -15,6 +15,8 @@ */ package com.android.systemui.qs.customize; +import static com.android.systemui.Flags.gsfQuickSettings; + import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; @@ -81,6 +83,10 @@ public class QSCustomizer extends LinearLayout { mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0, com.android.internal.R.string.reset) .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); mToolbar.setTitle(R.string.qs_edit); + if (gsfQuickSettings()) { + mToolbar.setTitleTextAppearance(context, R.style.TextAppearance_QSEditTitle); + } + mRecyclerView = findViewById(android.R.id.list); mTransparentView = findViewById(R.id.customizer_transparent_view); DefaultItemAnimator animator = new DefaultItemAnimator(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 829c419fc07f..db778a208b1e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -14,12 +14,15 @@ package com.android.systemui.qs.customize; +import static com.android.systemui.Flags.gsfQuickSettings; + import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Handler; import android.view.LayoutInflater; @@ -309,6 +312,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta if (viewType == TYPE_HEADER) { View v = inflater.inflate(R.layout.qs_customize_header, parent, false); v.setMinimumHeight(calculateHeaderMinHeight(context)); + if (gsfQuickSettings()) { + ((TextView) v.findViewById(android.R.id.title)).setTypeface( + Typeface.create("gsf-label-large", Typeface.NORMAL)); + } return new Holder(v); } if (viewType == TYPE_DIVIDER) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.kt new file mode 100644 index 000000000000..c2764f9f338b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.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.qs.panels.ui.compose + +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.android.systemui.lifecycle.rememberViewModel +import com.android.systemui.qs.panels.ui.viewmodel.EditModeButtonViewModel +import com.android.systemui.qs.ui.compose.borderOnFocus +import com.android.systemui.res.R + +@Composable +fun EditModeButton( + viewModelFactory: EditModeButtonViewModel.Factory, + modifier: Modifier = Modifier, +) { + val viewModel = rememberViewModel(traceName = "EditModeButton") { viewModelFactory.create() } + CompositionLocalProvider( + value = LocalContentColor provides MaterialTheme.colorScheme.onSurface + ) { + IconButton( + onClick = viewModel::onButtonClick, + shape = RoundedCornerShape(CornerSize(28.dp)), + modifier = + modifier.borderOnFocus( + color = MaterialTheme.colorScheme.secondary, + cornerSize = CornerSize(24.dp), + ), + ) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = stringResource(id = R.string.qs_edit), + ) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt index c729c7c15176..14abfa2313d8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt @@ -68,29 +68,16 @@ class EditTileListState( return _tiles.indexOfFirst { it is TileGridCell && it.tile.tileSpec == tileSpec } } - /** - * Whether the tile with this [TileSpec] is currently an icon in the [EditTileListState] - * - * @return true if the tile is an icon, false if it's large, null if the tile isn't in the list - */ - fun isIcon(tileSpec: TileSpec): Boolean? { - val index = indexOf(tileSpec) - return if (index != -1) { - val cell = _tiles[index] - cell as TileGridCell - return cell.isIcon - } else { - null - } - } - - /** Toggle the size of the tile corresponding to the [TileSpec] */ - fun toggleSize(tileSpec: TileSpec) { + /** Resize the tile corresponding to the [TileSpec] to [toIcon] */ + fun resizeTile(tileSpec: TileSpec, toIcon: Boolean) { val fromIndex = indexOf(tileSpec) if (fromIndex != -1) { - val cell = _tiles.removeAt(fromIndex) - cell as TileGridCell - _tiles.add(fromIndex, cell.copy(width = if (cell.isIcon) largeTilesSpan else 1)) + val cell = _tiles[fromIndex] as TileGridCell + + if (cell.isIcon == toIcon) return + + _tiles.removeAt(fromIndex) + _tiles.add(fromIndex, cell.copy(width = if (toIcon) 1 else largeTilesSpan)) regenerateGrid(fromIndex) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt index 0d375817b705..a22eb3a8d517 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt @@ -27,12 +27,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec /** A layout of tiles, indicating how they should be composed when showing in QS or in edit mode. */ interface GridLayout { - @Composable - fun SceneScope.TileGrid( - tiles: List<TileViewModel>, - modifier: Modifier, - editModeStart: () -> Unit, - ) + @Composable fun SceneScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier) @Composable fun EditTileGrid( diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt index 789fdebc36eb..b6dbf4db57a4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.panels.ui.compose +import android.view.MotionEvent import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -29,22 +30,15 @@ import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CornerSize -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Edit -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource +import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.padding @@ -55,10 +49,10 @@ import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType import com.android.systemui.qs.panels.ui.compose.Dimensions.FooterHeight import com.android.systemui.qs.panels.ui.compose.Dimensions.InterPageSpacing +import com.android.systemui.qs.panels.ui.viewmodel.EditModeButtonViewModel import com.android.systemui.qs.panels.ui.viewmodel.PaginatedGridViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel import com.android.systemui.qs.ui.compose.borderOnFocus -import com.android.systemui.res.R import javax.inject.Inject class PaginatedGridLayout @@ -68,11 +62,7 @@ constructor( @PaginatedBaseLayoutType private val delegateGridLayout: PaginatableGridLayout, ) : GridLayout by delegateGridLayout { @Composable - override fun SceneScope.TileGrid( - tiles: List<TileViewModel>, - modifier: Modifier, - editModeStart: () -> Unit, - ) { + override fun SceneScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier) { val viewModel = rememberViewModel(traceName = "PaginatedGridLayout-TileGrid") { viewModelFactory.create() @@ -115,7 +105,13 @@ constructor( state = pagerState, modifier = Modifier.sysuiResTag("qs_pager") - .padding(horizontal = { -contentPaddingValue.roundToPx() }), + .padding(horizontal = { -contentPaddingValue.roundToPx() }) + .pointerInteropFilter { event -> + if (event.actionMasked == MotionEvent.ACTION_UP) { + viewModel.registerSideSwipeGesture() + } + false + }, contentPadding = contentPadding, pageSpacing = if (pages.size > 1) InterPageSpacing else 0.dp, beyondViewportPageCount = 1, @@ -123,14 +119,12 @@ constructor( ) { val page = pages[it] - with(delegateGridLayout) { - TileGrid(tiles = page, modifier = Modifier, editModeStart = {}) - } + with(delegateGridLayout) { TileGrid(tiles = page, modifier = Modifier) } } FooterBar( buildNumberViewModelFactory = viewModel.buildNumberViewModelFactory, pagerState = pagerState, - editModeStart = editModeStart, + editButtonViewModelFactory = viewModel.editModeButtonViewModelFactory, ) } } @@ -145,7 +139,7 @@ private object Dimensions { private fun FooterBar( buildNumberViewModelFactory: BuildNumberViewModel.Factory, pagerState: PagerState, - editModeStart: () -> Unit, + editButtonViewModelFactory: EditModeButtonViewModel.Factory, ) { // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is // expected to be inside a scrollable container, this should not be an issue. @@ -181,24 +175,7 @@ private fun FooterBar( ) Row(Modifier.weight(1f)) { Spacer(modifier = Modifier.weight(1f)) - CompositionLocalProvider( - value = LocalContentColor provides MaterialTheme.colorScheme.onSurface - ) { - IconButton( - onClick = editModeStart, - shape = RoundedCornerShape(CornerSize(28.dp)), - modifier = - Modifier.borderOnFocus( - color = MaterialTheme.colorScheme.secondary, - cornerSize = CornerSize(FooterHeight / 2), - ), - ) { - Icon( - imageVector = Icons.Default.Edit, - contentDescription = stringResource(id = R.string.qs_edit), - ) - } - } + EditModeButton(viewModelFactory = editButtonViewModelFactory) } } } 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 ca28ab3e6ce3..2928ad117922 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 @@ -75,6 +75,8 @@ fun SceneScope.QuickQuickSettings( coroutineScope = scope, bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), tileHapticsViewModelFactoryProvider = viewModel.tileHapticsViewModelFactoryProvider, + // There should be no QuickQuickSettings when the details view is enabled. + detailsViewModel = null, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt new file mode 100644 index 000000000000..1bfbbe1964dc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt @@ -0,0 +1,121 @@ +/* + * 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.qs.panels.ui.compose + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Settings +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.em +import com.android.systemui.qs.flags.QsDetailedView +import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel + +@Composable +fun TileDetails(detailsViewModel: DetailsViewModel) { + + if (!QsDetailedView.isEnabled) { + throw IllegalStateException("QsDetailedView should be enabled") + } + + val tileDetailedViewModel = detailsViewModel.activeTileDetails ?: return + + DisposableEffect(Unit) { onDispose { detailsViewModel.closeDetailedView() } } + + Column( + modifier = Modifier + .fillMaxWidth() + // The height of the details view is TBD. + .fillMaxHeight() + ) { + CompositionLocalProvider( + value = LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + + IconButton( + onClick = { detailsViewModel.closeDetailedView() }, + modifier = Modifier + .align(Alignment.CenterVertically) + .height(TileDetailsDefaults.IconHeight) + .padding(start = TileDetailsDefaults.IconPadding), + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + // Description is TBD + contentDescription = "Back to QS panel", + ) + } + Text( + text = tileDetailedViewModel.getTitle(), + modifier = Modifier + .align(Alignment.CenterVertically), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleLarge + ) + IconButton( + onClick = { tileDetailedViewModel.clickOnSettingsButton() }, + modifier = Modifier + .align(Alignment.CenterVertically) + .height(TileDetailsDefaults.IconHeight) + .padding(end = TileDetailsDefaults.IconPadding), + ) { + Icon( + imageVector = Icons.Default.Settings, + // Description is TBD + contentDescription = "Go to Settings", + ) + } + } + Text( + text = tileDetailedViewModel.getSubTitle(), + modifier = Modifier + .fillMaxWidth(), + textAlign = TextAlign.Center, + style = MaterialTheme.typography.titleSmall + + ) + } + tileDetailedViewModel.GetContentView() + } +} + +private object TileDetailsDefaults { + val IconHeight = 48.dp + val IconPadding = 4.dp +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt index 1a5297b10e37..6c1906bb906f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt @@ -24,13 +24,9 @@ import com.android.compose.animation.scene.SceneScope import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel @Composable -fun SceneScope.TileGrid( - viewModel: TileGridViewModel, - modifier: Modifier = Modifier, - editModeStart: () -> Unit, -) { +fun SceneScope.TileGrid(viewModel: TileGridViewModel, modifier: Modifier = Modifier) { val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle() val tiles by viewModel.tileViewModels.collectAsStateWithLifecycle(emptyList()) - with(gridLayout) { TileGrid(tiles, modifier, editModeStart) } + with(gridLayout) { TileGrid(tiles, modifier) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt index 31ea60e2f0bc..c6141a1a7cc2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt @@ -20,7 +20,6 @@ package com.android.systemui.qs.panels.ui.compose.infinitegrid import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.fadeIn @@ -75,7 +74,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -124,8 +122,12 @@ import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState import com.android.systemui.qs.panels.ui.compose.selection.ResizableTileContainer -import com.android.systemui.qs.panels.ui.compose.selection.TileWidths +import com.android.systemui.qs.panels.ui.compose.selection.ResizingState +import com.android.systemui.qs.panels.ui.compose.selection.ResizingState.ResizeOperation +import com.android.systemui.qs.panels.ui.compose.selection.ResizingState.ResizeOperation.FinalResizeOperation +import com.android.systemui.qs.panels.ui.compose.selection.ResizingState.ResizeOperation.TemporaryResizeOperation import com.android.systemui.qs.panels.ui.compose.selection.clearSelectionTile +import com.android.systemui.qs.panels.ui.compose.selection.rememberResizingState import com.android.systemui.qs.panels.ui.compose.selection.rememberSelectionState import com.android.systemui.qs.panels.ui.compose.selection.selectableTile import com.android.systemui.qs.panels.ui.model.GridCell @@ -136,10 +138,10 @@ import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.shared.model.groupAndSort import com.android.systemui.res.R +import kotlin.math.max import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collectLatest object TileType @@ -181,15 +183,7 @@ fun DefaultEditTileGrid( onStopEditing: () -> Unit, onReset: (() -> Unit)?, ) { - val currentListState by rememberUpdatedState(listState) - val selectionState = - rememberSelectionState( - onResize = { currentListState.toggleSize(it) }, - onResizeEnd = { spec -> - // Commit the size currently in the list - currentListState.isIcon(spec)?.let { onResize(spec, it) } - }, - ) + val selectionState = rememberSelectionState() val reset: (() -> Unit)? = if (onReset != null) { { @@ -349,10 +343,21 @@ private fun CurrentTilesGrid( } .testTag(CURRENT_TILES_GRID_TEST_TAG), ) { - EditTiles(cells, columns, listState, selectionState, coroutineScope, largeTilesSpan) { spec - -> - // Toggle the current size of the tile - currentListState.isIcon(spec)?.let { onResize(spec, !it) } + EditTiles(cells, columns, listState, selectionState, coroutineScope, largeTilesSpan) { + resizingOperation -> + when (resizingOperation) { + is TemporaryResizeOperation -> { + currentListState.resizeTile(resizingOperation.spec, resizingOperation.toIcon) + } + is FinalResizeOperation -> { + // Commit the new size of the tile + onResize(resizingOperation.spec, resizingOperation.toIcon) + + // Mark the selection as automatic in case the tile ends up moving to a + // different row with its new size. + selectionState.select(resizingOperation.spec, manual = false) + } + } } } } @@ -373,7 +378,7 @@ private fun AvailableTileGrid( // Available tiles Column( - verticalArrangement = spacedBy(CommonTileDefaults.TileArrangementPadding), + verticalArrangement = spacedBy(TileArrangementPadding), horizontalAlignment = Alignment.Start, modifier = Modifier.fillMaxWidth().wrapContentHeight().testTag(AVAILABLE_TILES_GRID_TEST_TAG), @@ -387,7 +392,7 @@ private fun AvailableTileGrid( ) tiles.chunked(columns).forEach { row -> Row( - horizontalArrangement = spacedBy(CommonTileDefaults.TileArrangementPadding), + horizontalArrangement = spacedBy(TileArrangementPadding), modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max), ) { row.forEachIndexed { index, tileGridCell -> @@ -436,7 +441,7 @@ fun LazyGridScope.EditTiles( selectionState: MutableSelectionState, coroutineScope: CoroutineScope, largeTilesSpan: Int, - onToggleSize: (spec: TileSpec) -> Unit, + onResize: (operation: ResizeOperation) -> Unit, ) { items( count = cells.size, @@ -464,7 +469,7 @@ fun LazyGridScope.EditTiles( index = index, dragAndDropState = dragAndDropState, selectionState = selectionState, - onToggleSize = onToggleSize, + onResize = onResize, coroutineScope = coroutineScope, bounceableInfo = cells.bounceableInfo(index, columns), largeTilesSpan = largeTilesSpan, @@ -482,7 +487,7 @@ private fun TileGridCell( index: Int, dragAndDropState: DragAndDropState, selectionState: MutableSelectionState, - onToggleSize: (spec: TileSpec) -> Unit, + onResize: (operation: ResizeOperation) -> Unit, coroutineScope: CoroutineScope, largeTilesSpan: Int, bounceableInfo: BounceableInfo, @@ -511,28 +516,47 @@ private fun TileGridCell( selected = selectionState.selection?.tileSpec == cell.tile.tileSpec } - // Current base, min and max width of this tile - var tileWidths: TileWidths? by remember { mutableStateOf(null) } - val padding = with(LocalDensity.current) { TileArrangementPadding.roundToPx() } + val state = rememberResizingState(cell.tile.tileSpec, cell.isIcon) + + val progress: () -> Float = { + if (selected) { + // If selected, return the manual progress from the drag + state.progress() + } else { + // Else, return the target progress for the tile format + if (cell.isIcon) 0f else 1f + } + } + + if (!selected) { + // Update the draggable anchor state when the tile's size is not manually toggled + LaunchedEffect(cell.isIcon) { state.updateCurrentValue(cell.isIcon) } + } else { + // If the tile is selected, listen to new target values from the draggable anchor to toggle + // the tile's size + LaunchedEffect(state.temporaryResizeOperation) { onResize(state.temporaryResizeOperation) } + LaunchedEffect(state.finalResizeOperation) { onResize(state.finalResizeOperation) } + } + + val totalPadding = + with(LocalDensity.current) { (largeTilesSpan - 1) * TileArrangementPadding.roundToPx() } ResizableTileContainer( selected = selected, - selectionState = selectionState, + state = state, selectionAlpha = { selectionAlpha }, selectionColor = selectionColor, - tileWidths = { tileWidths }, modifier = modifier .height(TileHeight) .fillMaxWidth() .onSizeChanged { // Grab the size before the bounceable to get the idle width - val totalPadding = (largeTilesSpan - 1) * padding val min = if (cell.isIcon) it.width else (it.width - totalPadding) / largeTilesSpan val max = if (cell.isIcon) (it.width * largeTilesSpan) + totalPadding else it.width - tileWidths = TileWidths(it.width, min, max) + state.updateAnchors(min.toFloat(), max.toFloat()) } .bounceable( bounceable = currentBounceableInfo.bounceable, @@ -552,7 +576,7 @@ private fun TileGridCell( listOf( // TODO(b/367748260): Add final accessibility actions CustomAccessibilityAction("Toggle size") { - onToggleSize(cell.tile.tileSpec) + onResize(FinalResizeOperation(cell.tile.tileSpec, !cell.isIcon)) true } ) @@ -567,24 +591,7 @@ private fun TileGridCell( ) .tileBackground(colors.background) ) { - val targetValue = if (cell.isIcon) 0f else 1f - val animatedProgress = remember { Animatable(targetValue) } - - val resizingState = selectionState.resizingState?.takeIf { selected } - LaunchedEffect(targetValue, resizingState) { - if (resizingState == null) { - animatedProgress.animateTo(targetValue) - } else { - snapshotFlow { resizingState.progression } - .collectLatest { animatedProgress.snapTo(it) } - } - } - - EditTile( - tile = cell.tile, - tileWidths = { tileWidths }, - progress = { animatedProgress.value }, - ) + EditTile(tile = cell.tile, state = state, progress = progress) } } } @@ -651,7 +658,7 @@ private fun SpacerGridCell(modifier: Modifier = Modifier) { @Composable fun EditTile( tile: EditTileViewModel, - tileWidths: () -> TileWidths?, + state: ResizingState, progress: () -> Float, colors: TileColors = EditModeTileDefaults.editTileColors(), ) { @@ -661,12 +668,16 @@ fun EditTile( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.layout { measurable, constraints -> + val (min, max) = state.bounds + val currentProgress = progress() // Always display the tile using the large size and trust the parent composable // to clip the content as needed. This stop the labels from being truncated. - val width = tileWidths()?.max ?: constraints.maxWidth + val width = + max?.roundToInt()?.takeIf { it > constraints.maxWidth } + ?: constraints.maxWidth val placeable = measurable.measure(constraints.copy(minWidth = width, maxWidth = width)) - val currentProgress = progress() + val startPadding = if (currentProgress == 0f) { // Find the center of the max width when the tile is icon only @@ -675,7 +686,7 @@ fun EditTile( // Find the center of the minimum width to hold the same position as the // tile is resized. val basePadding = - tileWidths()?.min?.let { iconHorizontalCenter(it) } ?: 0f + min?.let { iconHorizontalCenter(it.roundToInt()) } ?: 0f // Large tiles, represented with a progress of 1f, have a 0.dp padding basePadding * (1f - currentProgress) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt index 29ff1715dea2..8fd99a52eceb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt @@ -36,6 +36,7 @@ import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout import com.android.systemui.qs.panels.ui.compose.bounceableInfo import com.android.systemui.qs.panels.ui.compose.rememberEditListState import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel +import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridViewModel @@ -49,17 +50,14 @@ import javax.inject.Inject class InfiniteGridLayout @Inject constructor( + private val detailsViewModel: DetailsViewModel, private val iconTilesViewModel: IconTilesViewModel, private val viewModelFactory: InfiniteGridViewModel.Factory, private val tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider, ) : PaginatableGridLayout { @Composable - override fun SceneScope.TileGrid( - tiles: List<TileViewModel>, - modifier: Modifier, - editModeStart: () -> Unit, - ) { + override fun SceneScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier) { DisposableEffect(tiles) { val token = Any() tiles.forEach { it.startListening(token) } @@ -104,6 +102,7 @@ constructor( tileHapticsViewModelFactoryProvider = tileHapticsViewModelFactoryProvider, coroutineScope = scope, bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), + detailsViewModel = detailsViewModel, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt index 0a80a19871fd..abdf923ebe73 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt @@ -77,10 +77,12 @@ import com.android.systemui.haptics.msdl.qs.TileHapticsViewModel import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.flags.QsDetailedView import com.android.systemui.qs.panels.ui.compose.BounceableInfo import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel +import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileUiState import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel import com.android.systemui.qs.panels.ui.viewmodel.toUiState @@ -121,6 +123,7 @@ fun Tile( bounceableInfo: BounceableInfo, tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider, modifier: Modifier = Modifier, + detailsViewModel: DetailsViewModel?, ) { val state by tile.state.collectAsStateWithLifecycle(tile.currentState) val currentBounceableInfo by rememberUpdatedState(bounceableInfo) @@ -163,12 +166,20 @@ fun Tile( .takeIf { uiState.handlesLongClick } TileContainer( onClick = { - tile.onClick(expandable) - hapticsViewModel?.setTileInteractionState( - TileHapticsViewModel.TileInteractionState.CLICKED - ) - if (uiState.accessibilityUiState.toggleableState != null) { - coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } + var hasDetails = false + if (QsDetailedView.isEnabled) { + hasDetails = detailsViewModel?.onTileClicked(tile.spec) == true + } + if (!hasDetails) { + // For those tile's who doesn't have a detailed view, process with their + // `onClick` behavior. + tile.onClick(expandable) + hapticsViewModel?.setTileInteractionState( + TileHapticsViewModel.TileInteractionState.CLICKED + ) + if (uiState.accessibilityUiState.toggleableState != null) { + coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } + } } }, onLongClick = longClick, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt index 1d36aee4eb85..c6c6dcaa896c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt @@ -27,11 +27,8 @@ import com.android.systemui.qs.pipeline.shared.TileSpec /** Creates the state of the current selected tile that is remembered across compositions. */ @Composable -fun rememberSelectionState( - onResize: (TileSpec) -> Unit, - onResizeEnd: (TileSpec) -> Unit, -): MutableSelectionState { - return remember { MutableSelectionState(onResize, onResizeEnd) } +fun rememberSelectionState(): MutableSelectionState { + return remember { MutableSelectionState() } } /** @@ -41,47 +38,18 @@ fun rememberSelectionState( data class Selection(val tileSpec: TileSpec, val manual: Boolean) /** Holds the state of the current selection. */ -class MutableSelectionState( - val onResize: (TileSpec) -> Unit, - private val onResizeEnd: (TileSpec) -> Unit, -) { +class MutableSelectionState { private var _selection = mutableStateOf<Selection?>(null) - private var _resizingState = mutableStateOf<ResizingState?>(null) /** The [Selection] if a tile is selected, null if not. */ val selection by _selection - /** The [ResizingState] of the selected tile is currently being resized, null if not. */ - val resizingState by _resizingState - fun select(tileSpec: TileSpec, manual: Boolean) { _selection.value = Selection(tileSpec, manual) } fun unSelect() { _selection.value = null - onResizingDragEnd() - } - - fun onResizingDrag(offset: Float) { - _resizingState.value?.onDrag(offset) - } - - fun onResizingDragStart(tileWidths: TileWidths) { - _selection.value?.let { - _resizingState.value = ResizingState(tileWidths) { onResize(it.tileSpec) } - } - } - - fun onResizingDragEnd() { - _resizingState.value = null - _selection.value?.let { - onResizeEnd(it.tileSpec) - - // Mark the selection as automatic in case the tile ends up moving to a different - // row with its new size. - _selection.value = it.copy(manual = false) - } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt index 41c3de55af70..1113053e36d2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/ResizingState.kt @@ -16,56 +16,82 @@ package com.android.systemui.qs.panels.ui.compose.selection +import androidx.compose.foundation.gestures.AnchoredDraggableState +import androidx.compose.foundation.gestures.DraggableAnchors +import androidx.compose.foundation.gestures.animateTo +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.setValue -import com.android.systemui.qs.panels.ui.compose.selection.ResizingDefaults.RESIZING_THRESHOLD +import androidx.compose.runtime.remember +import com.android.systemui.qs.panels.ui.compose.selection.ResizingState.ResizeOperation.FinalResizeOperation +import com.android.systemui.qs.panels.ui.compose.selection.ResizingState.ResizeOperation.TemporaryResizeOperation +import com.android.systemui.qs.pipeline.shared.TileSpec -class ResizingState(val widths: TileWidths, private val onResize: () -> Unit) { - /** Total drag offset of this resize operation. */ - private var totalOffset by mutableFloatStateOf(0f) +@Composable +fun rememberResizingState(tileSpec: TileSpec, startsAsIcon: Boolean): ResizingState { + return remember(tileSpec) { ResizingState(tileSpec, startsAsIcon) } +} - /** Width in pixels of the resizing tile. */ - var width by mutableIntStateOf(widths.base) +enum class QSDragAnchor { + Icon, + Large, +} - /** Progression between icon (0) and large (1) sizes. */ - val progression - get() = calculateProgression() +class ResizingState(tileSpec: TileSpec, startsAsIcon: Boolean) { + val anchoredDraggableState = + AnchoredDraggableState(if (startsAsIcon) QSDragAnchor.Icon else QSDragAnchor.Large) - // Whether the tile is currently over the threshold and should be a large tile - private var passedThreshold: Boolean = passedThreshold(progression) + val bounds by derivedStateOf { + anchoredDraggableState.anchors.minPosition().takeIf { !it.isNaN() } to + anchoredDraggableState.anchors.maxPosition().takeIf { !it.isNaN() } + } - fun onDrag(offset: Float) { - totalOffset += offset - width = (widths.base + totalOffset).toInt().coerceIn(widths.min, widths.max) + val temporaryResizeOperation by derivedStateOf { + TemporaryResizeOperation( + tileSpec, + toIcon = anchoredDraggableState.currentValue == QSDragAnchor.Icon, + ) + } - passedThreshold(progression).let { - // Resize if we went over the threshold - if (passedThreshold != it) { - passedThreshold = it - onResize() - } - } + val finalResizeOperation by derivedStateOf { + FinalResizeOperation( + tileSpec, + toIcon = anchoredDraggableState.settledValue == QSDragAnchor.Icon, + ) } - private fun passedThreshold(progression: Float): Boolean { - return progression >= RESIZING_THRESHOLD + fun updateAnchors(min: Float, max: Float) { + anchoredDraggableState.updateAnchors( + DraggableAnchors { + QSDragAnchor.Icon at min + QSDragAnchor.Large at max + } + ) } - /** The progression of the resizing tile between an icon tile (0f) and a large tile (1f) */ - private fun calculateProgression(): Float { - return ((width - widths.min) / (widths.max - widths.min).toFloat()).coerceIn(0f, 1f) + suspend fun updateCurrentValue(isIcon: Boolean) { + anchoredDraggableState.animateTo(if (isIcon) QSDragAnchor.Icon else QSDragAnchor.Large) } -} -/** Holds the width of a tile as well as its min and max widths */ -data class TileWidths(val base: Int, val min: Int, val max: Int) { - init { - check(max > min) { "The max width needs to be larger than the min width." } + suspend fun toggleCurrentValue() { + val isIcon = anchoredDraggableState.currentValue == QSDragAnchor.Icon + updateCurrentValue(!isIcon) } -} -private object ResizingDefaults { - const val RESIZING_THRESHOLD = .25f + fun progress(): Float = anchoredDraggableState.progress(QSDragAnchor.Icon, QSDragAnchor.Large) + + /** + * Represents a resizing operation for a tile. + * + * @property spec The tile's [TileSpec] + * @property toIcon The new size for the tile. + */ + sealed class ResizeOperation private constructor(val spec: TileSpec, val toIcon: Boolean) { + /** A temporary resizing operation, used while a resizing movement is in motion. */ + class TemporaryResizeOperation(spec: TileSpec, toIcon: Boolean) : + ResizeOperation(spec, toIcon) + + /** A final resizing operation, used while a resizing movement is done. */ + class FinalResizeOperation(spec: TileSpec, toIcon: Boolean) : ResizeOperation(spec, toIcon) + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt index 8a345ce97c84..c1545e1263db 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt @@ -19,10 +19,11 @@ package com.android.systemui.qs.panels.ui.compose.selection import androidx.compose.animation.core.Spring import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.animateIntAsState import androidx.compose.animation.core.spring import androidx.compose.foundation.Canvas -import androidx.compose.foundation.gestures.detectHorizontalDragGestures +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.anchoredDraggable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.size @@ -30,7 +31,9 @@ import androidx.compose.foundation.systemGestureExclusion import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithContent @@ -40,16 +43,16 @@ import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.layout import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.toSize import androidx.compose.ui.zIndex -import com.android.compose.modifiers.thenIf import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingDotSize import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.SelectedBorderWidth +import kotlin.math.roundToInt +import kotlinx.coroutines.launch /** * Places a dot to handle resizing drag events. Use this on tiles to resize. @@ -58,31 +61,24 @@ import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.Sel * selected. * * @param selected whether resizing drag events should be handled - * @param selectionState the [MutableSelectionState] on the grid + * @param state the [ResizingState] for the tile * @param selectionAlpha the animated value for the dot and border alpha * @param selectionColor the [Color] of the dot and border - * @param tileWidths the [TileWidths] of the selected tile */ @Composable fun ResizableTileContainer( selected: Boolean, - selectionState: MutableSelectionState, + state: ResizingState, selectionAlpha: () -> Float, selectionColor: Color, - tileWidths: () -> TileWidths?, modifier: Modifier = Modifier, content: @Composable BoxScope.() -> Unit = {}, ) { - Box( - modifier - .resizable(selected, selectionState, tileWidths) - .selectionBorder(selectionColor, selectionAlpha) - ) { + Box(modifier.resizable(selected, state).selectionBorder(selectionColor, selectionAlpha)) { content() ResizingHandle( enabled = selected, - selectionState = selectionState, - tileWidths = tileWidths, + state = state, modifier = // Higher zIndex to make sure the handle is drawn above the content Modifier.zIndex(2f), @@ -91,15 +87,11 @@ fun ResizableTileContainer( } @Composable -private fun ResizingHandle( - enabled: Boolean, - selectionState: MutableSelectionState, - tileWidths: () -> TileWidths?, - modifier: Modifier = Modifier, -) { +private fun ResizingHandle(enabled: Boolean, state: ResizingState, modifier: Modifier = Modifier) { // Manually creating the touch target around the resizing dot to ensure that the next tile // does not receive the touch input accidentally. val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current + val scope = rememberCoroutineScope() Box( modifier .layout { measurable, constraints -> @@ -112,20 +104,14 @@ private fun ResizingHandle( ) } } - .thenIf(enabled) { - Modifier.systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) } - .pointerInput(Unit) { - detectHorizontalDragGestures( - onHorizontalDrag = { _, offset -> - selectionState.onResizingDrag(offset) - }, - onDragStart = { - tileWidths()?.let { selectionState.onResizingDragStart(it) } - }, - onDragEnd = selectionState::onResizingDragEnd, - onDragCancel = selectionState::onResizingDragEnd, - ) - } + .systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) } + .anchoredDraggable( + enabled = enabled, + state = state.anchoredDraggableState, + orientation = Orientation.Horizontal, + ) + .clickable(enabled = enabled, interactionSource = null, indication = null) { + scope.launch { state.toggleCurrentValue() } } ) { ResizingDot(enabled = enabled, modifier = Modifier.align(Alignment.Center)) @@ -165,23 +151,15 @@ private fun Modifier.selectionBorder( } @Composable -private fun Modifier.resizable( - selected: Boolean, - selectionState: MutableSelectionState, - tileWidths: () -> TileWidths?, -): Modifier { +private fun Modifier.resizable(selected: Boolean, state: ResizingState): Modifier { if (!selected) return zIndex(1f) - // Animated diff between the current width and the resized width of the tile. We can't use - // animateContentSize here as the tile is sometimes unbounded. - val remainingOffset by - animateIntAsState( - selectionState.resizingState?.let { tileWidths()?.base?.minus(it.width) ?: 0 } ?: 0, - label = "QSEditTileWidthOffset", - ) return zIndex(2f).layout { measurable, constraints -> + val isIdle by derivedStateOf { state.progress().let { it == 0f || it == 1f } } // Grab the width from the resizing state if a resize is in progress - val width = selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset) + val width = + state.anchoredDraggableState.requireOffset().roundToInt().takeIf { !isIdle } + ?: constraints.maxWidth val placeable = measurable.measure(constraints.copy(minWidth = width, maxWidth = width)) layout(constraints.maxWidth, placeable.height) { placeable.place(0, 0) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt new file mode 100644 index 000000000000..d8361f544bdc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt @@ -0,0 +1,65 @@ +/* + * 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.qs.panels.ui.viewmodel + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.plugins.qs.TileDetailsViewModel +import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor +import com.android.systemui.qs.pipeline.shared.TileSpec +import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow + +@SysUISingleton +class DetailsViewModel +@Inject constructor(val currentTilesInteractor: CurrentTilesInteractor) { + + /** + * The current active [TileDetailsViewModel]. If it's `null`, it means the qs overlay is not + * showing any details view. It changes when [onTileClicked] and [closeDetailedView]. + */ + private val _activeTileDetails = mutableStateOf<TileDetailsViewModel?>(null) + val activeTileDetails by _activeTileDetails + + /** + * Update the active [TileDetailsViewModel] to `null`. + * @see activeTileDetails + */ + fun closeDetailedView() { + _activeTileDetails.value = null + } + + /** + * Update the active [TileDetailsViewModel] to the `spec`'s corresponding view model. + * Return if the [TileDetailsViewModel] is successfully found. + * @see activeTileDetails + */ + fun onTileClicked(spec: TileSpec?): Boolean { + if (spec == null) { + _activeTileDetails.value = null + return false + } + + _activeTileDetails.value = currentTilesInteractor + .currentQSTiles + .firstOrNull { it.tileSpec == spec.spec } + ?.detailsViewModel + + return _activeTileDetails.value != null + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt new file mode 100644 index 000000000000..b033473a91e5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt @@ -0,0 +1,41 @@ +/* + * 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.qs.panels.ui.viewmodel + +import com.android.systemui.classifier.domain.interactor.FalsingInteractor +import com.android.systemui.plugins.FalsingManager +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +class EditModeButtonViewModel +@AssistedInject +constructor( + private val editModeViewModel: EditModeViewModel, + private val falsingInteractor: FalsingInteractor, +) { + + fun onButtonClick() { + if (!falsingInteractor.isFalseTap(FalsingManager.LOW_PENALTY)) { + editModeViewModel.startEditing() + } + } + + @AssistedFactory + interface Factory { + fun create(): EditModeButtonViewModel + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt index bff330b98fda..4a18872ad6f6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt @@ -17,6 +17,8 @@ package com.android.systemui.qs.panels.ui.viewmodel import androidx.compose.runtime.getValue +import com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE +import com.android.systemui.classifier.domain.interactor.FalsingInteractor import com.android.systemui.development.ui.viewmodel.BuildNumberViewModel import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator @@ -36,6 +38,8 @@ constructor( paginatedGridInteractor: PaginatedGridInteractor, inFirstPageViewModel: InFirstPageViewModel, val buildNumberViewModelFactory: BuildNumberViewModel.Factory, + val editModeButtonViewModelFactory: EditModeButtonViewModel.Factory, + private val falsingInteractor: FalsingInteractor, ) : IconTilesViewModel by iconTilesViewModel, ExclusiveActivatable() { private val hydrator = Hydrator("PaginatedGridViewModel") @@ -53,6 +57,10 @@ constructor( val columns: Int get() = columnsWithMediaViewModel.columns + fun registerSideSwipeGesture() { + falsingInteractor.isFalseTouch(QS_SWIPE_SIDE) + } + override suspend fun onActivated(): Nothing { coroutineScope { launch { hydrator.activate() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 1fb76f1eaa7f..b7ebce247ec9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -27,6 +27,7 @@ import android.content.res.Resources.ID_NULL import android.graphics.Color import android.graphics.PorterDuff import android.graphics.Rect +import android.graphics.Typeface import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable @@ -308,6 +309,14 @@ constructor( } setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE)) setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE)) + + if (Flags.gsfQuickSettings()) { + label.apply { + typeface = Typeface.create("gsf-title-small-emphasized", Typeface.NORMAL) + } + secondaryLabel.apply { typeface = Typeface.create("gsf-label-medium", Typeface.NORMAL) } + } + addView(labelContainer) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt index 42ef0cd3abb7..7225800e25e3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt @@ -28,11 +28,14 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.plugins.qs.TileDetailsViewModel import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger +import com.android.systemui.qs.flags.QsDetailedView import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel import com.android.systemui.qs.tiles.dialog.InternetDialogManager import com.android.systemui.qs.tiles.dialog.WifiStateWorker import com.android.systemui.res.R @@ -90,6 +93,9 @@ constructor( } override fun handleClick(expandable: Expandable?) { + if (QsDetailedView.isEnabled) { + return + } mainHandler.post { internetDialogManager.create( aboveStatusBar = true, @@ -100,6 +106,10 @@ constructor( } } + override fun getDetailsViewModel(): TileDetailsViewModel { + return InternetDetailsViewModel { longClick(null) } + } + override fun secondaryClick(expandable: Expandable?) { // TODO(b/358352265): Figure out the correct action for the secondary click // Toggle wifi diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt new file mode 100644 index 000000000000..f239a179d79a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt @@ -0,0 +1,59 @@ +/* + * 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.qs.tiles.dialog + +import android.view.LayoutInflater +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import com.android.systemui.plugins.qs.TileDetailsViewModel +import com.android.systemui.res.R + +class InternetDetailsViewModel( + onLongClick: () -> Unit, +) : TileDetailsViewModel() { + private val _onLongClick = onLongClick + + @Composable + override fun GetContentView() { + AndroidView( + modifier = Modifier.fillMaxWidth().fillMaxHeight(), + factory = { context -> + // Inflate with the existing dialog xml layout + LayoutInflater.from(context) + .inflate(R.layout.internet_connectivity_dialog, null) + // TODO: b/377388104 - Implement the internet details view + }, + ) + } + + override fun clickOnSettingsButton() { + _onLongClick() + } + + override fun getTitle(): String { + // TODO: b/377388104 Update the placeholder text + return "Internet" + } + + override fun getSubTitle(): String { + // TODO: b/377388104 Update the placeholder text + return "Tab a network to connect" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt index 8f870d468997..4806c3f83224 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.qs.tiles.impl.custom.domain.interactor import android.os.UserHandle import android.service.quicksettings.Tile -import com.android.systemui.common.coroutine.ConflatedCallbackFlow +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor @@ -28,6 +28,7 @@ import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePacka import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel import com.android.systemui.qs.tiles.impl.di.QSTileScope import com.android.systemui.user.data.repository.UserRepository +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -44,7 +45,6 @@ import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn -import com.android.app.tracing.coroutines.launchTraced as launch @QSTileScope @OptIn(ExperimentalCoroutinesApi::class) @@ -64,7 +64,7 @@ constructor( private val bindingFlow = mutableUserFlow .flatMapLatest { user -> - ConflatedCallbackFlow.conflatedCallbackFlow { + conflatedCallbackFlow { serviceInteractor.setUser(user) // Wait for the CustomTileInteractor to become initialized first, because @@ -79,7 +79,7 @@ constructor( defaultsRepository.requestNewDefaults( user, tileSpec.componentName, - true + true, ) } .launchIn(this) @@ -99,7 +99,7 @@ constructor( override fun tileData( user: UserHandle, - triggers: Flow<DataUpdateTrigger> + triggers: Flow<DataUpdateTrigger>, ): Flow<CustomTileDataModel> { tileScope.launch { mutableUserFlow.emit(user) } return bindingFlow.combine(triggers) { _, _ -> }.flatMapLatest { dataFlow(user) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt index ab3862b75ee8..f9a1ad5d8424 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt @@ -25,6 +25,7 @@ import com.android.systemui.Dumpable import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.QSHost import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon @@ -35,14 +36,17 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.io.PrintWriter import java.util.concurrent.CopyOnWriteArraySet +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectIndexed import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.takeWhile +import kotlinx.coroutines.launch // TODO(b/http://b/299909989): Use QSTileViewModel directly after the rollout class QSTileViewModelAdapter @@ -51,6 +55,7 @@ constructor( @Application private val applicationScope: CoroutineScope, private val qsHost: QSHost, @Assisted private val qsTileViewModel: QSTileViewModel, + @UiBackground private val uiBgDispatcher: CoroutineDispatcher, ) : QSTile, Dumpable { private val context @@ -162,19 +167,25 @@ constructor( override fun setListening(client: Any?, listening: Boolean) { client ?: return if (listening) { - val clientWasNotAlreadyListening = listeningClients.add(client) - if (clientWasNotAlreadyListening && listeningClients.size == 1) { - stateJob = - qsTileViewModel.state - .filterNotNull() - .map { mapState(context, it, qsTileViewModel.config) } - .onEach { legacyState -> - val changed = legacyState.copyTo(cachedState) - if (changed) { - callbacks.forEach { it.onStateChanged(legacyState) } + applicationScope.launch(uiBgDispatcher) { + val shouldStartMappingJob = + listeningClients.add(client) // new client + && listeningClients.size == 1 // first client + + if (shouldStartMappingJob) { + stateJob = + qsTileViewModel.state + .filterNotNull() + .map { mapState(context, it, qsTileViewModel.config) } + .onEach { legacyState -> + val changed = legacyState.copyTo(cachedState) + if (changed) { + callbacks.forEach { it.onStateChanged(legacyState) } + } } - } - .launchIn(applicationScope) + .flowOn(uiBgDispatcher) + .launchIn(applicationScope) + } } } else { listeningClients.remove(client) diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt index da175c9120ba..62b120332289 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.ui.viewmodel import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel import com.android.systemui.lifecycle.ExclusiveActivatable +import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel @@ -36,6 +37,7 @@ constructor( @Assisted supportsBrightnessMirroring: Boolean, val tileGridViewModel: TileGridViewModel, val editModeViewModel: EditModeViewModel, + val detailsViewModel: DetailsViewModel, ) : ExclusiveActivatable() { val brightnessSliderViewModel = diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt index 02b2bb1585bd..d94f00fff400 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt @@ -171,9 +171,16 @@ constructor( } ALL_ISSUE_TYPES.keys.forEach { popupMenu.menu.add(it).apply { + // Set this for every item in the list to ensure equal spacing. Set it to + // transparent for non-selected items so icon is only visible for selected element. setIcon(R.drawable.arrow_pointing_down) if (it != state.issueTypeRes) { iconTintList = ColorStateList.valueOf(Color.TRANSPARENT) + } else { + contentDescription = + context.getString(com.android.internal.R.string.selected) + + " " + + context.getString(it) } intent = Intent().putExtra(EXTRA_ISSUE_TYPE_RES, it) diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/DisabledContentInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/DisabledContentInteractor.kt new file mode 100644 index 000000000000..d7c3b6b43c71 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/DisabledContentInteractor.kt @@ -0,0 +1,93 @@ +/* + * 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.scene.domain.interactor + +import com.android.compose.animation.scene.ContentKey +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult +import com.android.systemui.scene.shared.model.Overlays +import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.statusbar.disableflags.domain.interactor.DisableFlagsInteractor +import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +class DisabledContentInteractor +@Inject +constructor(private val disableFlagsInteractor: DisableFlagsInteractor) { + + /** Returns `true` if the given [key] is disabled; `false` if it's enabled */ + fun isDisabled( + key: ContentKey, + disabledFlags: DisableFlagsModel = disableFlagsInteractor.disableFlags.value, + ): Boolean { + return with(disabledFlags) { + when (key) { + Scenes.Shade, + Overlays.NotificationsShade -> !isShadeEnabled() + Scenes.QuickSettings, + Overlays.QuickSettingsShade -> !isQuickSettingsEnabled() + else -> false + } + } + } + + /** Runs the given [block] each time that [key] becomes disabled. */ + suspend fun repeatWhenDisabled(key: ContentKey, block: suspend (disabled: ContentKey) -> Unit) { + disableFlagsInteractor.disableFlags + .map { isDisabled(key) } + .distinctUntilChanged() + .collectLatest { isDisabled -> + if (isDisabled) { + block(key) + } + } + } + + /** + * Returns a filtered version of [unfiltered], without action-result entries that would navigate + * to disabled scenes. + */ + fun filteredUserActions( + unfiltered: Flow<Map<UserAction, UserActionResult>> + ): Flow<Map<UserAction, UserActionResult>> { + return combine(disableFlagsInteractor.disableFlags, unfiltered) { + disabledFlags, + unfilteredMap -> + unfilteredMap.filterValues { actionResult -> + val destination = + when (actionResult) { + is UserActionResult.ChangeScene -> actionResult.toScene + is UserActionResult.ShowOverlay -> actionResult.overlay + is UserActionResult.ReplaceByOverlay -> actionResult.overlay + else -> null + } + if (destination != null) { + // results that lead to a disabled destination get filtered out. + !isDisabled(key = destination, disabledFlags = disabledFlags) + } else { + // Action results that don't lead to a destination are never filtered out. + true + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index f20e5a54f6ed..d83d74e4e538 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -21,6 +21,8 @@ import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey +import com.android.compose.animation.scene.UserAction +import com.android.compose.animation.scene.UserActionResult import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor @@ -64,6 +66,7 @@ constructor( private val sceneFamilyResolvers: Lazy<Map<SceneKey, @JvmSuppressWildcards SceneResolver>>, private val deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>, private val keyguardEnabledInteractor: Lazy<KeyguardEnabledInteractor>, + private val disabledContentInteractor: DisabledContentInteractor, ) { interface OnSceneAboutToChangeListener { @@ -465,6 +468,10 @@ constructor( return false } + if (disabledContentInteractor.isDisabled(to)) { + return false + } + val inMidTransitionFromGone = (transitionState.value as? ObservableTransitionState.Transition)?.fromContent == Scenes.Gone @@ -503,6 +510,10 @@ constructor( " Logging reason for overlay change was: $loggingReason" } + if (to != null && disabledContentInteractor.isDisabled(to)) { + return false + } + val isFromValid = (from == null) || (from in currentOverlays.value) val isToValid = (to == null) || (to !in currentOverlays.value && to in repository.allContentKeys) @@ -517,4 +528,14 @@ constructor( /** Returns `true` if [scene] can be resolved from [family]. */ fun isSceneInFamily(scene: SceneKey, family: SceneKey): Boolean = sceneFamilyResolvers.get()[family]?.includesScene(scene) == true + + /** + * Returns a filtered version of [unfiltered], without action-result entries that would navigate + * to disabled scenes. + */ + fun filteredUserActions( + unfiltered: Flow<Map<UserAction, UserActionResult>> + ): Flow<Map<UserAction, UserActionResult>> { + return disabledContentInteractor.filteredUserActions(unfiltered) + } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt index ba789a01d285..3a07ce959bb3 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene.domain.interactor +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton @@ -29,9 +30,9 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.init.NotificationsController import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor -import com.android.systemui.statusbar.policy.HeadsUpManager import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.CoroutineScope @@ -44,7 +45,6 @@ import kotlinx.coroutines.flow.flatMapConcat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import com.android.app.tracing.coroutines.launchTraced as launch /** Business logic about the visibility of various parts of the window root view. */ @OptIn(ExperimentalCoroutinesApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index aece5c65ce12..8d8c24eae9e2 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -19,7 +19,6 @@ package com.android.systemui.scene.domain.startable import android.app.StatusBarManager -import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.internal.logging.UiEventLogger @@ -56,6 +55,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.scene.data.model.asIterable import com.android.systemui.scene.data.model.sceneStackOf +import com.android.systemui.scene.domain.interactor.DisabledContentInteractor import com.android.systemui.scene.domain.interactor.SceneBackInteractor import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor @@ -103,6 +103,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** * Hooks up business logic that manipulates the state of the [SceneInteractor] for the system UI @@ -144,6 +145,7 @@ constructor( private val alternateBouncerInteractor: AlternateBouncerInteractor, private val vibratorHelper: VibratorHelper, private val msdlPlayer: MSDLPlayer, + private val disabledContentInteractor: DisabledContentInteractor, ) : CoreStartable { private val centralSurfaces: CentralSurfaces? get() = centralSurfacesOptLazy.get().getOrNull() @@ -281,6 +283,7 @@ constructor( handlePowerState() handleDreamState() handleShadeTouchability() + handleDisableFlags() } private fun handleBouncerImeVisibility() { @@ -565,6 +568,38 @@ constructor( } } + private fun handleDisableFlags() { + applicationScope.launch { + launch { + sceneInteractor.currentScene.collectLatest { currentScene -> + disabledContentInteractor.repeatWhenDisabled(currentScene) { + switchToScene( + targetSceneKey = SceneFamilies.Home, + loggingReason = + "Current scene ${currentScene.debugName} became" + " disabled", + ) + } + } + } + + launch { + sceneInteractor.currentOverlays.collectLatest { overlays -> + overlays.forEach { overlay -> + launch { + disabledContentInteractor.repeatWhenDisabled(overlay) { + sceneInteractor.hideOverlay( + overlay = overlay, + loggingReason = + "Overlay ${overlay.debugName} became" + " disabled", + ) + } + } + } + } + } + } + } + private fun handleDeviceEntryHapticsWhileDeviceLocked() { applicationScope.launch { deviceEntryInteractor.isDeviceEntered.collectLatest { isDeviceEntered -> diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt index 32d5cb460cd8..c1e8032aa9e5 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt @@ -280,6 +280,16 @@ constructor( } } + /** + * Returns a filtered version of [unfiltered], without action-result entries that would navigate + * to disabled scenes. + */ + fun filteredUserActions( + unfiltered: Flow<Map<UserAction, UserActionResult>> + ): Flow<Map<UserAction, UserActionResult>> { + return sceneInteractor.filteredUserActions(unfiltered) + } + /** Defines interface for classes that can handle externally-reported [MotionEvent]s. */ interface MotionEventHandler { /** Notifies that a [MotionEvent] has occurred. */ diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index 49ceba834dd4..31780a56f7f0 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -37,9 +37,11 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.theme.PlatformTheme import com.android.internal.annotations.VisibleForTesting import com.android.systemui.Flags +import com.android.systemui.Flags.communalHubOnMobile import com.android.systemui.ambient.touch.TouchMonitor import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent import com.android.systemui.communal.dagger.Communal @@ -70,7 +72,6 @@ import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import com.android.app.tracing.coroutines.launchTraced as launch /** * Controller that's responsible for the glanceable hub container view and its touch handling. @@ -513,14 +514,19 @@ constructor( val touchOnUmo = keyguardMediaController.isWithinMediaViewBounds(ev.x.toInt(), ev.y.toInt()) val touchOnSmartspace = lockscreenSmartspaceController.isWithinSmartspaceBounds(ev.x.toInt(), ev.y.toInt()) - if (!hubShowing && (touchOnNotifications || touchOnUmo || touchOnSmartspace)) { + val glanceableHubV2 = communalHubOnMobile() + if ( + !hubShowing && + (touchOnNotifications || touchOnUmo || touchOnSmartspace || glanceableHubV2) + ) { logger.d({ "Lockscreen touch ignored: touchOnNotifications: $bool1, touchOnUmo: $bool2, " + - "touchOnSmartspace: $bool3" + "touchOnSmartspace: $bool3, glanceableHubV2: $bool4" }) { bool1 = touchOnNotifications bool2 = touchOnUmo bool3 = touchOnSmartspace + bool4 = glanceableHubV2 } return false } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 42a756c27de7..0c0add2dd237 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -186,13 +186,15 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.ConversationNotificationManager; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; 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.footer.shared.FooterViewRefactor; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -222,12 +224,10 @@ import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; import com.android.systemui.statusbar.phone.TapAgainViewController; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.SplitShadeStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.util.Compile; diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAware.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAware.kt index 111d335d0d0f..e8cdf836a58f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAware.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAware.kt @@ -29,5 +29,13 @@ import javax.inject.Qualifier * `ConfigurationController`) will be dynamically updated to reflect the current display's * configuration. This ensures consistent rendering even when the shade window is moved to an * external display. + * + * Note that in SystemUI, Currently, the shade window includes the lockscreen, quick settings, the + * notification stack, AOD, Bouncer, Glancable hub, and potentially other components that have been + * introduced after this comment is written. + * + * TODO: b/378016985 - The usage of this annotation in the relevant packages will be enforced by a + * presubmit linter that will highlight instances of the global instances used in shade window + * classes. */ @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class ShadeDisplayAware diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt index 4f73a3456cad..91ca2ca52287 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt @@ -19,7 +19,8 @@ package com.android.systemui.shade import android.content.Context import android.content.res.Resources import android.view.LayoutInflater -import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY +import android.view.WindowManager +import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE import com.android.systemui.CoreStartable import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.common.ui.ConfigurationStateImpl @@ -30,16 +31,22 @@ import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl import com.android.systemui.dagger.SysUISingleton import com.android.systemui.res.R +import com.android.systemui.scene.ui.view.WindowRootView +import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository import com.android.systemui.shade.data.repository.ShadeDisplaysRepository import com.android.systemui.shade.data.repository.ShadeDisplaysRepositoryImpl +import com.android.systemui.shade.display.ShadeDisplayPolicyModule +import com.android.systemui.shade.domain.interactor.ShadeDisplaysInteractor import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround import com.android.systemui.statusbar.phone.ConfigurationControllerImpl import com.android.systemui.statusbar.phone.ConfigurationForwarder import com.android.systemui.statusbar.policy.ConfigurationController +import dagger.BindsOptionalOf import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap +import javax.inject.Provider /** * Module responsible for managing display-specific components and resources for the notification @@ -53,7 +60,7 @@ import dagger.multibindings.IntoMap * By using this dedicated module, we ensure the notification shade window always utilizes the * correct display context and resources, regardless of the display it's on. */ -@Module +@Module(includes = [OptionalShadeDisplayAwareBindings::class, ShadeDisplayPolicyModule::class]) object ShadeDisplayAwareModule { /** Creates a new context for the shade window. */ @@ -63,7 +70,7 @@ object ShadeDisplayAwareModule { fun provideShadeDisplayAwareContext(context: Context): Context { return if (ShadeWindowGoesAround.isEnabled) { context - .createWindowContext(context.display, TYPE_APPLICATION_OVERLAY, /* options= */ null) + .createWindowContext(context.display, TYPE_NOTIFICATION_SHADE, /* options= */ null) .apply { setTheme(R.style.Theme_SystemUI) } } else { context @@ -73,6 +80,20 @@ object ShadeDisplayAwareModule { @Provides @ShadeDisplayAware @SysUISingleton + fun provideShadeWindowManager( + defaultWindowManager: WindowManager, + @ShadeDisplayAware context: Context, + ): WindowManager { + return if (ShadeWindowGoesAround.isEnabled) { + context.getSystemService(WindowManager::class.java) as WindowManager + } else { + defaultWindowManager + } + } + + @Provides + @ShadeDisplayAware + @SysUISingleton fun provideShadeDisplayAwareResources(@ShadeDisplayAware context: Context): Resources { return context.resources } @@ -162,6 +183,15 @@ object ShadeDisplayAwareModule { return impl } + @SysUISingleton + @Provides + fun provideMutableShadePositionRepository( + impl: ShadeDisplaysRepositoryImpl + ): MutableShadeDisplaysRepository { + ShadeWindowGoesAround.isUnexpectedlyInLegacyMode() + return impl + } + @Provides @IntoMap @ClassKey(ShadePrimaryDisplayCommand::class) @@ -172,4 +202,20 @@ object ShadeDisplayAwareModule { CoreStartable.NOP } } + + @Provides + @IntoMap + @ClassKey(ShadeDisplaysInteractor::class) + fun provideShadeDisplaysInteractor(impl: Provider<ShadeDisplaysInteractor>): CoreStartable { + return if (ShadeWindowGoesAround.isEnabled) { + impl.get() + } else { + CoreStartable.NOP + } + } +} + +@Module +internal interface OptionalShadeDisplayAwareBindings { + @BindsOptionalOf fun bindOptionalOfWindowRootView(): WindowRootView } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt index a5d9e9660ca5..a54f6b9c6743 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt @@ -20,11 +20,14 @@ import android.view.Display import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.display.data.repository.DisplayRepository -import com.android.systemui.shade.data.repository.ShadeDisplaysRepository +import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository +import com.android.systemui.shade.display.ShadeDisplayPolicy +import com.android.systemui.shade.display.SpecificDisplayIdPolicy import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import java.io.PrintWriter import javax.inject.Inject +import kotlin.text.toIntOrNull @SysUISingleton class ShadePrimaryDisplayCommand @@ -32,7 +35,9 @@ class ShadePrimaryDisplayCommand constructor( private val commandRegistry: CommandRegistry, private val displaysRepository: DisplayRepository, - private val positionRepository: ShadeDisplaysRepository, + private val positionRepository: MutableShadeDisplaysRepository, + private val policies: Set<@JvmSuppressWildcards ShadeDisplayPolicy>, + private val defaultPolicy: ShadeDisplayPolicy, ) : Command, CoreStartable { override fun start() { @@ -40,8 +45,11 @@ constructor( } override fun help(pw: PrintWriter) { - pw.println("shade_display_override <displayId> ") - pw.println("Set the display which is holding the shade.") + pw.println("shade_display_override (<displayId>|<policyName>) ") + pw.println("Set the display which is holding the shade, or the policy that defines it.") + pw.println() + pw.println("shade_display_override policies") + pw.println("Lists available policies") pw.println() pw.println("shade_display_override reset ") pw.println("Reset the display which is holding the shade.") @@ -68,21 +76,27 @@ constructor( "reset" -> reset() "list", "status" -> printStatus() + "policies" -> printPolicies() "any_external" -> anyExternal() - else -> { - val cmdAsInteger = command?.toIntOrNull() - if (cmdAsInteger != null) { - changeDisplay(displayId = cmdAsInteger) - } else { - help(pw) - } + null -> help(pw) + else -> parsePolicy(command) + } + } + + private fun parsePolicy(policyIdentifier: String) { + val displayId = policyIdentifier.toIntOrNull() + when { + displayId != null -> changeDisplay(displayId = displayId) + policies.any { it.name == policyIdentifier } -> { + positionRepository.policy.value = policies.first { it.name == policyIdentifier } } + else -> help(pw) } } private fun reset() { - positionRepository.resetDisplayId() - pw.println("Reset shade primary display id to ${Display.DEFAULT_DISPLAY}") + positionRepository.policy.value = defaultPolicy + pw.println("Reset shade display policy to default policy: ${defaultPolicy.name}") } private fun printStatus() { @@ -95,6 +109,15 @@ constructor( } } + private fun printPolicies() { + val currentPolicyName = positionRepository.policy.value.name + pw.println("Available policies: ") + policies.forEach { + pw.print(" - ${it.name}") + pw.println(if (currentPolicyName == it.name) " (Current policy)" else "") + } + } + private fun anyExternal() { val anyExternalDisplay = displaysRepository.displays.value.firstOrNull { @@ -116,7 +139,7 @@ constructor( } private fun setDisplay(id: Int) { - positionRepository.setDisplayId(id) + positionRepository.policy.value = SpecificDisplayIdPolicy(id) pw.println("New shade primary display id is $id") } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt index 7346a28dc7a7..9ca23f0f1e17 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt @@ -19,8 +19,8 @@ import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.shade.domain.interactor.ShadeBackActionInteractor import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor import com.android.systemui.statusbar.GestureRecorder +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.phone.CentralSurfaces -import com.android.systemui.statusbar.policy.HeadsUpManager /** * Allows CentralSurfacesImpl to interact with the shade. Only CentralSurfacesImpl should reference @@ -37,7 +37,7 @@ interface ShadeSurface : centralSurfaces: CentralSurfaces, recorder: GestureRecorder, hideExpandedRunnable: Runnable, - headsUpManager: HeadsUpManager + headsUpManager: HeadsUpManager, ) /** Cancels any pending collapses. */ diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt index ec4018c7d238..3bbda162cf31 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurfaceImpl.kt @@ -17,8 +17,8 @@ package com.android.systemui.shade import com.android.systemui.statusbar.GestureRecorder +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.phone.CentralSurfaces -import com.android.systemui.statusbar.policy.HeadsUpManager import javax.inject.Inject class ShadeSurfaceImpl @Inject constructor() : ShadeSurface, ShadeViewControllerEmptyImpl() { @@ -26,7 +26,7 @@ class ShadeSurfaceImpl @Inject constructor() : ShadeSurface, ShadeViewController centralSurfaces: CentralSurfaces, recorder: GestureRecorder, hideExpandedRunnable: Runnable, - headsUpManager: HeadsUpManager + headsUpManager: HeadsUpManager, ) {} override fun cancelPendingCollapse() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt index 71c565816362..732d4d1500e7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadeDisplayRepository.kt @@ -23,14 +23,14 @@ import kotlinx.coroutines.flow.StateFlow class FakeShadeDisplayRepository : ShadeDisplaysRepository { private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY) - override fun setDisplayId(displayId: Int) { + fun setDisplayId(displayId: Int) { _displayId.value = displayId } override val displayId: StateFlow<Int> get() = _displayId - override fun resetDisplayId() { + fun resetDisplayId() { _displayId.value = Display.DEFAULT_DISPLAY } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt index 4a95e339cade..756241e9b071 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt @@ -18,37 +18,40 @@ package com.android.systemui.shade.data.repository import android.view.Display import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.shade.display.ShadeDisplayPolicy import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.stateIn +/** Source of truth for the display currently holding the shade. */ interface ShadeDisplaysRepository { /** ID of the display which currently hosts the shade */ val displayId: StateFlow<Int> +} - /** - * Updates the value of the shade display id stored, emitting to the new display id to every - * component dependent on the shade display id - */ - fun setDisplayId(displayId: Int) - - /** Resets value of shade primary display to the default display */ - fun resetDisplayId() +/** Allows to change the policy that determines in which display the Shade window is visible. */ +interface MutableShadeDisplaysRepository : ShadeDisplaysRepository { + /** Updates the policy to select where the shade is visible. */ + val policy: MutableStateFlow<ShadeDisplayPolicy> } -/** Source of truth for the display currently holding the shade. */ +/** Keeps the policy and propagates the display id for the shade from it. */ @SysUISingleton -class ShadeDisplaysRepositoryImpl @Inject constructor() : ShadeDisplaysRepository { - private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY) - - override val displayId: StateFlow<Int> - get() = _displayId - - override fun setDisplayId(displayId: Int) { - _displayId.value = displayId - } - - override fun resetDisplayId() { - _displayId.value = Display.DEFAULT_DISPLAY - } +@OptIn(ExperimentalCoroutinesApi::class) +class ShadeDisplaysRepositoryImpl +@Inject +constructor(defaultPolicy: ShadeDisplayPolicy, @Background bgScope: CoroutineScope) : + MutableShadeDisplaysRepository { + override val policy = MutableStateFlow<ShadeDisplayPolicy>(defaultPolicy) + + override val displayId: StateFlow<Int> = + policy + .flatMapLatest { it.displayId } + .stateIn(bgScope, SharingStarted.WhileSubscribed(), Display.DEFAULT_DISPLAY) } diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicy.kt new file mode 100644 index 000000000000..3f6c949fcba6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/display/AnyExternalShadeDisplayPolicy.kt @@ -0,0 +1,57 @@ +/* + * 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.shade.display + +import android.view.Display +import android.view.Display.DEFAULT_DISPLAY +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.display.data.repository.DisplayRepository +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +/** + * Returns an external display if one exists, otherwise the default display. + * + * If there are multiple external displays, the one with minimum display ID is returned. + */ +@SysUISingleton +class AnyExternalShadeDisplayPolicy +@Inject +constructor(displayRepository: DisplayRepository, @Background bgScope: CoroutineScope) : + ShadeDisplayPolicy { + override val name: String + get() = "any_external_display" + + override val displayId: StateFlow<Int> = + displayRepository.displays + .map { displays -> + displays + .filter { it.displayId != DEFAULT_DISPLAY && it.type in ALLOWED_DISPLAY_TYPES } + .minOfOrNull { it.displayId } ?: DEFAULT_DISPLAY + } + .stateIn(bgScope, SharingStarted.WhileSubscribed(), DEFAULT_DISPLAY) + + private companion object { + val ALLOWED_DISPLAY_TYPES = + setOf(Display.TYPE_EXTERNAL, Display.TYPE_OVERLAY, Display.TYPE_WIFI) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt new file mode 100644 index 000000000000..1b22ee40009b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt @@ -0,0 +1,45 @@ +/* + * 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.shade.display + +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoSet +import kotlinx.coroutines.flow.StateFlow + +/** Describes the display the shade should be shown in. */ +interface ShadeDisplayPolicy { + val name: String + + /** The display id the shade should be at, according to this policy. */ + val displayId: StateFlow<Int> +} + +@Module +interface ShadeDisplayPolicyModule { + @IntoSet + @Binds + fun provideDefaultPolicyToSet(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy + + @IntoSet + @Binds + fun provideAnyExternalShadeDisplayPolicyToSet( + impl: AnyExternalShadeDisplayPolicy + ): ShadeDisplayPolicy + + @Binds fun provideDefaultPolicy(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt new file mode 100644 index 000000000000..13e766409bab --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt @@ -0,0 +1,33 @@ +/* + * 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.shade.display + +import android.view.Display +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +/** Policy to specify a display id explicitly. */ +open class SpecificDisplayIdPolicy(displayId: Int) : ShadeDisplayPolicy { + override val name: String + get() = "display_${displayId}_policy" + + override val displayId: StateFlow<Int> = MutableStateFlow(displayId) +} + +class DefaultShadeDisplayPolicy @Inject constructor() : + SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY) diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt index 1055dcb55d5f..fb2cbec84236 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt @@ -18,21 +18,21 @@ package com.android.systemui.shade.domain.interactor import android.content.Context import android.util.Log -import android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE +import android.view.WindowManager +import androidx.annotation.UiThread import com.android.app.tracing.coroutines.launchTraced import com.android.app.tracing.traceSection import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository -import com.android.systemui.display.shared.model.DisplayWindowProperties import com.android.systemui.scene.ui.view.WindowRootView import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shade.ShadeWindowLayoutParams import com.android.systemui.shade.data.repository.ShadeDisplaysRepository import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround -import com.android.systemui.statusbar.phone.ConfigurationForwarder +import com.android.systemui.util.kotlin.getOrNull +import java.util.Optional import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope @@ -43,15 +43,27 @@ import kotlinx.coroutines.withContext class ShadeDisplaysInteractor @Inject constructor( - private val shadeRootView: WindowRootView, + optionalShadeRootView: Optional<WindowRootView>, private val shadePositionRepository: ShadeDisplaysRepository, @ShadeDisplayAware private val shadeContext: Context, - private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, + @ShadeDisplayAware private val wm: WindowManager, @Background private val bgScope: CoroutineScope, - @ShadeDisplayAware private val configurationForwarder: ConfigurationForwarder, - @Main private val mainContext: CoroutineContext, + @Main private val mainThreadContext: CoroutineContext, ) : CoreStartable { + private val shadeLayoutParams: WindowManager.LayoutParams = + ShadeWindowLayoutParams.create(shadeContext) + + private val shadeRootView = + optionalShadeRootView.getOrNull() + ?: error( + """ + ShadeRootView must be provided for this ShadeDisplayInteractor to work. + If it is not, it means this is being instantiated in a SystemUI variant that shouldn't. + """ + .trimIndent() + ) + override fun start() { ShadeWindowGoesAround.isUnexpectedlyInLegacyMode() bgScope.launchTraced(TAG) { @@ -60,51 +72,52 @@ constructor( } /** Tries to move the shade. If anything wrong happens, fails gracefully without crashing. */ - private suspend fun moveShadeWindowTo(destinationDisplayId: Int) { - val currentId = shadeRootView.display.displayId - if (currentId == destinationDisplayId) { + private suspend fun moveShadeWindowTo(destinationId: Int) { + Log.d(TAG, "Trying to move shade window to display with id $destinationId") + val currentDisplay = shadeRootView.display + if (currentDisplay == null) { + Log.w(TAG, "Current shade display is null") + return + } + val currentId = currentDisplay.displayId + if (currentId == destinationId) { Log.w(TAG, "Trying to move the shade to a display it was already in") return } try { - moveShadeWindow(fromId = currentId, toId = destinationDisplayId) + withContext(mainThreadContext) { moveShadeWindow(toId = destinationId) } } catch (e: IllegalStateException) { Log.e( TAG, - "Unable to move the shade window from display $currentId to $destinationDisplayId", + "Unable to move the shade window from display $currentId to $destinationId", e, ) } } - private suspend fun moveShadeWindow(fromId: Int, toId: Int) { - val sourceProperties = getDisplayWindowProperties(fromId) - val destinationProperties = getDisplayWindowProperties(toId) - traceSection({ "MovingShadeWindow from $fromId to $toId" }) { - withContext(mainContext) { - traceSection("removeView") { - sourceProperties.windowManager.removeView(shadeRootView) - } - traceSection("addView") { - destinationProperties.windowManager.addView( - shadeRootView, - ShadeWindowLayoutParams.create(shadeContext), - ) - } - } - } - traceSection("SecondaryShadeInteractor#onConfigurationChanged") { - configurationForwarder.onConfigurationChanged( - destinationProperties.context.resources.configuration - ) + @UiThread + private fun moveShadeWindow(toId: Int) { + traceSection({ "moveShadeWindow to $toId" }) { + removeShadeWindow() + updateContextDisplay(toId) + addShadeWindow() } } - private fun getDisplayWindowProperties(displayId: Int): DisplayWindowProperties { - return displayWindowPropertiesRepository.get(displayId, TYPE_NOTIFICATION_SHADE) + @UiThread + private fun removeShadeWindow(): Unit = + traceSection("removeShadeWindow") { wm.removeView(shadeRootView) } + + @UiThread + private fun addShadeWindow(): Unit = + traceSection("addShadeWindow") { wm.addView(shadeRootView, shadeLayoutParams) } + + @UiThread + private fun updateContextDisplay(newDisplayId: Int) { + traceSection("updateContextDisplay") { shadeContext.updateDisplay(newDisplayId) } } private companion object { - const val TAG = "SecondaryShadeInteractor" + const val TAG = "ShadeDisplaysInteractor" } } 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 45516aa69cd7..0d847d84c820 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 @@ -23,6 +23,7 @@ import android.icu.text.DateFormat import android.icu.text.DisplayContext import android.os.UserHandle import android.provider.Settings +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.plugins.ActivityStarter @@ -48,7 +49,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import com.android.app.tracing.coroutines.launchTraced as launch /** Models UI state for the shade header. */ class ShadeHeaderViewModel @@ -87,10 +87,6 @@ constructor( /** Whether or not the privacy chip is enabled in the device privacy config. */ val isPrivacyChipEnabled: StateFlow<Boolean> = privacyChipInteractor.isChipEnabled - private val _isDisabled = MutableStateFlow(false) - /** Whether or not the Shade Header should be disabled based on disableFlags. */ - val isDisabled: StateFlow<Boolean> = _isDisabled.asStateFlow() - private val longerPattern = context.getString(R.string.abbrev_wday_month_day_no_year_alarm) private val shorterPattern = context.getString(R.string.abbrev_month_day_no_year) private val longerDateFormat = MutableStateFlow(getFormatFromPattern(longerPattern)) @@ -132,8 +128,6 @@ constructor( .collect { _mobileSubIds.value = it } } - launch { shadeInteractor.isQsEnabled.map { !it }.collect { _isDisabled.value = it } } - awaitCancellation() } } 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 ce4c081358ba..0253122183eb 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 @@ -30,15 +30,21 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode +import com.android.systemui.statusbar.disableflags.domain.interactor.DisableFlagsInteractor import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor 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.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach /** * Models UI state used to render the content of the shade scene. @@ -54,6 +60,7 @@ constructor( val brightnessMirrorViewModelFactory: BrightnessMirrorViewModel.Factory, val mediaCarouselInteractor: MediaCarouselInteractor, shadeInteractor: ShadeInteractor, + private val disableFlagsInteractor: DisableFlagsInteractor, private val footerActionsViewModelFactory: FooterActionsViewModel.Factory, private val footerActionsController: FooterActionsController, private val unfoldTransitionInteractor: UnfoldTransitionInteractor, @@ -70,12 +77,21 @@ constructor( val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasActiveMediaOrRecommendation + private val _isQsEnabled = + MutableStateFlow(!disableFlagsInteractor.disableFlags.value.isQuickSettingsEnabled()) + val isQsEnabled: StateFlow<Boolean> = _isQsEnabled.asStateFlow() + private val footerActionsControllerInitialized = AtomicBoolean(false) - override suspend fun onActivated(): Nothing { - deviceEntryInteractor.isDeviceEntered.collect { isDeviceEntered -> - _isEmptySpaceClickable.value = !isDeviceEntered - } + override suspend fun onActivated(): Nothing = coroutineScope { + deviceEntryInteractor.isDeviceEntered + .onEach { isDeviceEntered -> _isEmptySpaceClickable.value = !isDeviceEntered } + .launchIn(this) + disableFlagsInteractor.disableFlags + .map { it.isQuickSettingsEnabled() } + .onEach { _isQsEnabled.value = it } + .launchIn(this) + awaitCancellation() } /** diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt index b0777c9e20b9..dd1b58c47b63 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt @@ -27,24 +27,28 @@ import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge /** Returns collection of [UserAction] to [UserActionResult] pairs for opening the single shade. */ fun singleShadeActions( - requireTwoPointersForTopEdgeForQs: Boolean = false + isDownFromTopEdgeEnabled: Boolean = true, + requireTwoPointersForTopEdgeForQs: Boolean = false, ): Array<Pair<UserAction, UserActionResult>> { val shadeUserActionResult = UserActionResult(Scenes.Shade, isIrreversible = true) val qsSceneUserActionResult = UserActionResult(Scenes.QuickSettings, isIrreversible = true) - return arrayOf( - // Swiping down, not from the edge, always goes to shade. - Swipe.Down to shadeUserActionResult, - Swipe.Down(pointerCount = 2) to shadeUserActionResult, - - // Swiping down from the top edge. - swipeDownFromTop(pointerCount = 1) to - if (requireTwoPointersForTopEdgeForQs) { - shadeUserActionResult - } else { - qsSceneUserActionResult - }, - swipeDownFromTop(pointerCount = 2) to qsSceneUserActionResult, - ) + return buildList { + // Swiping down, not from the edge, always goes to shade. + add(Swipe.Down to shadeUserActionResult) + add(Swipe.Down(pointerCount = 2) to shadeUserActionResult) + if (isDownFromTopEdgeEnabled) { + add( + swipeDownFromTop(pointerCount = 1) to + if (requireTwoPointersForTopEdgeForQs) { + shadeUserActionResult + } else { + qsSceneUserActionResult + } + ) + add(swipeDownFromTop(pointerCount = 2) to qsSceneUserActionResult) + } + } + .toTypedArray() } /** Returns collection of [UserAction] to [UserActionResult] pairs for opening the split shade. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index e19fcd05e9a4..85b8bf9aec80 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -39,24 +39,24 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.HeadsUpManager import java.io.PrintWriter import javax.inject.Inject import kotlin.math.max /** * A utility class that handles notification panel expansion when a user swipes downward on a - * notification from the pulsing state. - * If face-bypass is enabled, the user can swipe down anywhere on the screen (not just from a - * notification) to trigger the notification panel expansion. + * notification from the pulsing state. If face-bypass is enabled, the user can swipe down anywhere + * on the screen (not just from a notification) to trigger the notification panel expansion. */ @SysUISingleton -class PulseExpansionHandler @Inject +class PulseExpansionHandler +@Inject constructor( context: Context, private val wakeUpCoordinator: NotificationWakeUpCoordinator, @@ -67,7 +67,7 @@ constructor( private val falsingManager: FalsingManager, private val shadeInteractor: ShadeInteractor, private val lockscreenShadeTransitionController: LockscreenShadeTransitionController, - dumpManager: DumpManager + dumpManager: DumpManager, ) : Gefingerpoken, Dumpable { companion object { private val SPRING_BACK_ANIMATION_LENGTH_MS = 375 @@ -91,14 +91,13 @@ constructor( pulseExpandAbortListener?.run() } } - headsUpManager.unpinAll( - /*userUnPinned= */ - true, - ) + headsUpManager.unpinAll(/* userUnPinned= */ true) } } + var leavingLockscreen: Boolean = false private set + private var touchSlop = 0f private var minDragDistance = 0 private lateinit var stackScrollerController: NotificationStackScrollLayoutController @@ -111,24 +110,27 @@ constructor( private val isFalseTouch: Boolean get() = falsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) + var pulseExpandAbortListener: Runnable? = null var bouncerShowing: Boolean = false init { initResources(context) - configurationController.addCallback(object : ConfigurationController.ConfigurationListener { - override fun onConfigChanged(newConfig: Configuration?) { - initResources(context) + configurationController.addCallback( + object : ConfigurationController.ConfigurationListener { + override fun onConfigChanged(newConfig: Configuration?) { + initResources(context) + } } - }) + ) mPowerManager = context.getSystemService(PowerManager::class.java) dumpManager.registerDumpable(this) } private fun initResources(context: Context) { - minDragDistance = context.resources.getDimensionPixelSize( - R.dimen.keyguard_drag_down_min_distance) + minDragDistance = + context.resources.getDimensionPixelSize(R.dimen.keyguard_drag_down_min_distance) touchSlop = ViewConfiguration.get(context).scaledTouchSlop.toFloat() } @@ -137,7 +139,8 @@ constructor( } private fun canHandleMotionEvent(): Boolean { - return wakeUpCoordinator.canShowPulsingHuns && !shadeInteractor.isQsExpanded.value && + return wakeUpCoordinator.canShowPulsingHuns && + !shadeInteractor.isQsExpanded.value && !bouncerShowing } @@ -189,18 +192,20 @@ constructor( } override fun onTouchEvent(event: MotionEvent): Boolean { - val finishExpanding = (event.action == MotionEvent.ACTION_CANCEL || - event.action == MotionEvent.ACTION_UP) && isExpanding + val finishExpanding = + (event.action == MotionEvent.ACTION_CANCEL || event.action == MotionEvent.ACTION_UP) && + isExpanding - val isDraggingNotificationOrCanBypass = mStartingChild?.showingPulsing() == true || - bypassController.canBypass() + val isDraggingNotificationOrCanBypass = + mStartingChild?.showingPulsing() == true || bypassController.canBypass() if ((!canHandleMotionEvent() || !isDraggingNotificationOrCanBypass) && !finishExpanding) { // We allow cancellations/finishing to still go through here to clean up the state return false } - if (velocityTracker == null || !isExpanding || - event.actionMasked == MotionEvent.ACTION_DOWN) { + if ( + velocityTracker == null || !isExpanding || event.actionMasked == MotionEvent.ACTION_DOWN + ) { return startExpansion(event) } velocityTracker!!.addMovement(event) @@ -210,12 +215,11 @@ constructor( when (event.actionMasked) { MotionEvent.ACTION_MOVE -> updateExpansionHeight(moveDistance) MotionEvent.ACTION_UP -> { - velocityTracker!!.computeCurrentVelocity( - /* units= */ - 1000, - ) - val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000 && - statusBarStateController.state != StatusBarState.SHADE + velocityTracker!!.computeCurrentVelocity(/* units= */ 1000) + val canExpand = + moveDistance > 0 && + velocityTracker!!.getYVelocity() > -1000 && + statusBarStateController.state != StatusBarState.SHADE if (!falsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) { finishExpansion() } else { @@ -243,22 +247,16 @@ constructor( mPowerManager!!.wakeUp( SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, - "com.android.systemui:PULSEDRAG" + "com.android.systemui:PULSEDRAG", ) } - lockscreenShadeTransitionController.goToLockedShade( - startingChild, - needsQSAnimation = false - ) + lockscreenShadeTransitionController.goToLockedShade(startingChild, needsQSAnimation = false) lockscreenShadeTransitionController.finishPulseAnimation(cancelled = false) leavingLockscreen = true isExpanding = false if (mStartingChild is ExpandableNotificationRow) { val row = mStartingChild as ExpandableNotificationRow? - row!!.onExpandedByGesture( - /*userExpanded= */ - true, - ) + row!!.onExpandedByGesture(/* userExpanded= */ true) } } @@ -266,19 +264,15 @@ constructor( var expansionHeight = max(height, 0.0f) if (mStartingChild != null) { val child = mStartingChild!! - val newHeight = Math.min( - (child.collapsedHeight + expansionHeight).toInt(), - child.maxContentHeight - ) + val newHeight = + Math.min((child.collapsedHeight + expansionHeight).toInt(), child.maxContentHeight) child.actualHeight = newHeight } else { wakeUpCoordinator.setNotificationsVisibleForExpansion( - height - > lockscreenShadeTransitionController.distanceUntilShowingPulsingNotifications, - /*animate= */ - true, - /*increaseSpeed= */ - true + height > + lockscreenShadeTransitionController.distanceUntilShowingPulsingNotifications, + /*animate= */ true, + /*increaseSpeed= */ true, ) } lockscreenShadeTransitionController.setPulseHeight(expansionHeight, animate = false) @@ -296,7 +290,7 @@ constructor( @VisibleForTesting fun reset( child: ExpandableView, - animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS.toLong() + animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS.toLong(), ) { if (child.actualHeight == child.collapsedHeight) { setUserLocked(child, false) @@ -309,11 +303,13 @@ constructor( // don't use reflection, because the `actualHeight` field may be obfuscated child.actualHeight = animation.animatedValue as Int } - anim.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - setUserLocked(child, false) + anim.addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + setUserLocked(child, false) + } } - }) + ) anim.start() } @@ -331,12 +327,9 @@ constructor( } lockscreenShadeTransitionController.finishPulseAnimation(cancelled = true) wakeUpCoordinator.setNotificationsVisibleForExpansion( - /*visible= */ - false, - /*animate= */ - true, - /*increaseSpeed= */ - false + /*visible= */ false, + /*animate= */ true, + /*increaseSpeed= */ false, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt index af238f697a18..49c44798d2ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor import android.content.pm.PackageManager +import android.media.projection.StopReason import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton @@ -105,7 +106,7 @@ constructor( /** Stops the currently active projection. */ fun stopProjecting() { - scope.launch { mediaProjectionRepository.stopProjecting() } + scope.launch { mediaProjectionRepository.stopProjecting(StopReason.STOP_PRIVACY_CHIP) } } companion object { 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 434120051039..47b695e50a0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt @@ -27,7 +27,10 @@ import com.android.systemui.log.LogBufferFactory import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.data.StatusBarDataLayerModule import com.android.systemui.statusbar.data.repository.LightBarControllerStore +import com.android.systemui.statusbar.phone.AutoHideController +import com.android.systemui.statusbar.phone.AutoHideControllerImpl import com.android.systemui.statusbar.phone.LightBarController +import com.android.systemui.statusbar.phone.LightBarControllerImpl import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl import com.android.systemui.statusbar.phone.StatusBarSignalPolicy @@ -79,6 +82,13 @@ interface StatusBarModule { implFactory: StatusBarWindowControllerImpl.Factory ): StatusBarWindowController.Factory + @Binds @SysUISingleton fun autoHideController(impl: AutoHideControllerImpl): AutoHideController + + @Binds + fun lightBarControllerFactory( + legacyFactory: LightBarControllerImpl.LegacyFactory + ): LightBarController.Factory + companion object { @Provides diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt index 9a779300de97..3825c098ca5d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -34,12 +34,12 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationContentView import com.android.systemui.statusbar.notification.row.NotificationRowContentBinderLogger import com.android.systemui.statusbar.notification.stack.StackStateAnimator -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.children import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt index 67c53d46b4d0..383227d2b3aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt @@ -22,10 +22,10 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.TransitionAnimator import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.HeadsUpUtil import kotlin.math.ceil import kotlin.math.max @@ -36,12 +36,12 @@ class NotificationLaunchAnimatorControllerProvider( private val notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor, private val notificationListContainer: NotificationListContainer, private val headsUpManager: HeadsUpManager, - private val jankMonitor: InteractionJankMonitor + private val jankMonitor: InteractionJankMonitor, ) { @JvmOverloads fun getAnimatorController( notification: ExpandableNotificationRow, - onFinishAnimationCallback: Runnable? = null + onFinishAnimationCallback: Runnable? = null, ): NotificationTransitionAnimatorController { return NotificationTransitionAnimatorController( notificationLaunchAnimationInteractor, @@ -49,7 +49,7 @@ class NotificationLaunchAnimatorControllerProvider( headsUpManager, notification, jankMonitor, - onFinishAnimationCallback + onFinishAnimationCallback, ) } } @@ -65,7 +65,7 @@ class NotificationTransitionAnimatorController( private val headsUpManager: HeadsUpManager, private val notification: ExpandableNotificationRow, private val jankMonitor: InteractionJankMonitor, - private val onFinishAnimationCallback: Runnable? + private val onFinishAnimationCallback: Runnable?, ) : ActivityTransitionAnimator.Controller { companion object { @@ -109,7 +109,7 @@ class NotificationTransitionAnimatorController( left = location[0], right = location[0] + notification.width, topCornerRadius = topCornerRadius, - bottomCornerRadius = notification.bottomCornerRadius + bottomCornerRadius = notification.bottomCornerRadius, ) params.startTranslationZ = notification.translationZ @@ -177,7 +177,7 @@ class NotificationTransitionAnimatorController( row.entry.key, true /* releaseImmediately */, animate, - reason + reason, ) } @@ -224,7 +224,7 @@ class NotificationTransitionAnimatorController( override fun onTransitionAnimationProgress( state: TransitionAnimator.State, progress: Float, - linearProgress: Float + linearProgress: Float, ) { val params = state as LaunchAnimationParameters params.progress = progress diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 08ffbf2b29d4..7a59f79c77a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -36,14 +36,14 @@ import com.android.systemui.shade.ShadeViewController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.KeyguardBypassController.OnBypassStateChangedListener import com.android.systemui.statusbar.phone.ScreenOffAnimationController -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.doOnEnd import com.android.systemui.util.doOnStart import java.io.PrintWriter diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index c487ff5d35bd..6b84b6d07702 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -65,6 +65,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.icon.IconPack; import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -675,8 +676,8 @@ public final class NotificationEntry extends ListEntry { return row != null && row.isPinnedAndExpanded(); } - public void setRowPinned(boolean pinned) { - if (row != null) row.setPinned(pinned); + public void setRowPinnedStatus(PinnedStatus pinnedStatus) { + if (row != null) row.setPinnedStatus(pinnedStatus); } public boolean isRowHeadsUp() { @@ -1075,7 +1076,7 @@ public final class NotificationEntry extends ListEntry { if (PromotedNotificationContentModel.featureFlagEnabled()) { return mPromotedNotificationContentModel; } else { - Log.wtf(TAG, "getting promoted content without feature flag enabled"); + Log.wtf(TAG, "getting promoted content without feature flag enabled", new Throwable()); return null; } } @@ -1089,7 +1090,7 @@ public final class NotificationEntry extends ListEntry { if (PromotedNotificationContentModel.featureFlagEnabled()) { this.mPromotedNotificationContentModel = promotedNotificationContentModel; } else { - Log.wtf(TAG, "setting promoted content without feature flag enabled"); + Log.wtf(TAG, "setting promoted content without feature flag enabled", new Throwable()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java index 47a04291dd49..733b986b5422 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java @@ -20,13 +20,21 @@ import static android.app.NotificationManager.IMPORTANCE_MIN; import android.app.Notification; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi; import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt; +import com.google.common.primitives.Booleans; + import javax.inject.Inject; /** @@ -44,12 +52,21 @@ public class ColorizedFgsCoordinator implements Coordinator { @Override public void attach(NotifPipeline pipeline) { + if (PromotedNotificationUi.isEnabled()) { + pipeline.addPromoter(mPromotedOngoingPromoter); + } } public NotifSectioner getSectioner() { return mNotifSectioner; } + private final NotifPromoter mPromotedOngoingPromoter = new NotifPromoter("PromotedOngoing") { + @Override + public boolean shouldPromoteToTopLevel(NotificationEntry child) { + return isPromotedOngoing(child); + } + }; /** * Puts colorized foreground service and call notifications into its own section. @@ -64,11 +81,30 @@ public class ColorizedFgsCoordinator implements Coordinator { } return false; } + + private NotifComparator mPreferPromoted = new NotifComparator("PreferPromoted") { + @Override + public int compare(@NonNull ListEntry o1, @NonNull ListEntry o2) { + return -1 * Booleans.compare( + isPromotedOngoing(o1.getRepresentativeEntry()), + isPromotedOngoing(o2.getRepresentativeEntry())); + } + }; + + @Nullable + @Override + public NotifComparator getComparator() { + if (PromotedNotificationUi.isEnabled()) { + return mPreferPromoted; + } else { + return null; + } + } }; /** Determines if the given notification is a colorized or call notification */ public static boolean isRichOngoing(NotificationEntry entry) { - return isColorizedForegroundService(entry) || isCall(entry); + return isPromotedOngoing(entry) || isColorizedForegroundService(entry) || isCall(entry); } private static boolean isColorizedForegroundService(NotificationEntry entry) { @@ -78,6 +114,11 @@ public class ColorizedFgsCoordinator implements Coordinator { && entry.getImportance() > IMPORTANCE_MIN; } + private static boolean isPromotedOngoing(NotificationEntry entry) { + // NOTE: isPromotedOngoing already checks the android.app.ui_rich_ongoing flag. + return entry != null && entry.getSbn().getNotification().isPromotedOngoing(); + } + private static boolean isCall(NotificationEntry entry) { Notification notification = entry.getSbn().getNotification(); return entry.getImportance() > IMPORTANCE_MIN diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt index c7b47eeec218..0269b16d4490 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt @@ -41,13 +41,13 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider import com.android.systemui.statusbar.notification.collection.render.NodeController import com.android.systemui.statusbar.notification.dagger.IncomingHeader +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider import com.android.systemui.statusbar.notification.logKey import com.android.systemui.statusbar.notification.shared.GroupHunAnimationFix import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.time.SystemClock import java.util.function.Consumer diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt index 366704e27b9b..de6f2576ff19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.annotation.SuppressLint import androidx.annotation.VisibleForTesting +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Dumpable import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dump.DumpManager @@ -35,8 +36,8 @@ import com.android.systemui.statusbar.notification.collection.coordinator.dagger import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.headsUpEvents +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.headsUpEvents import com.android.systemui.util.asIndenting import com.android.systemui.util.indentIfPossible import java.io.PrintWriter @@ -52,7 +53,6 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach -import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.yield /** @@ -123,7 +123,7 @@ constructor( unseenNotifications.removeAll(notificationsSeenWhileLocked) logger.logAllMarkedSeenOnUnlock( seenCount = notificationsSeenWhileLocked.size, - remainingUnseenCount = unseenNotifications.size + remainingUnseenCount = unseenNotifications.size, ) notificationsSeenWhileLocked.clear() } @@ -140,7 +140,7 @@ constructor( * been "seen" while the device is on the keyguard. */ private suspend fun trackSeenNotificationsWhileLocked( - notificationsSeenWhileLocked: MutableSet<NotificationEntry>, + notificationsSeenWhileLocked: MutableSet<NotificationEntry> ) = coroutineScope { // Remove removed notifications from the set launch { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java index 8660cd117493..e75c11de57c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java @@ -45,7 +45,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor; import com.android.systemui.statusbar.notification.shared.NotificationMinimalism; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.kotlin.BooleanFlowOperators; import com.android.systemui.util.kotlin.JavaAdapter; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java index 5743ab0eae27..07fa6aeb7900 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java @@ -34,7 +34,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.Visual import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import javax.inject.Inject; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 42aadd132cd8..8a1371f1c415 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -77,6 +77,10 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl; import com.android.systemui.statusbar.notification.logging.dagger.NotificationsLogModule; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLogger; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProvider; +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory; import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -88,7 +92,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.ZenModesCleanupStartable; import dagger.Binds; @@ -101,6 +105,8 @@ import kotlin.coroutines.CoroutineContext; import kotlinx.coroutines.CoroutineScope; +import java.util.Optional; + import javax.inject.Provider; /** @@ -308,4 +314,22 @@ public interface NotificationsModule { @IntoMap @ClassKey(ZenModesCleanupStartable.class) CoreStartable bindsZenModesCleanup(ZenModesCleanupStartable zenModesCleanup); + + /** + * Provides {@link + * com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor} if + * one of the relevant feature flags is enabled. + */ + @Provides + @SysUISingleton + static Optional<PromotedNotificationContentExtractor> + providePromotedNotificationContentExtractor( + PromotedNotificationsProvider provider, Context context, + PromotedNotificationLogger logger) { + if (PromotedNotificationContentModel.featureFlagEnabled()) { + return Optional.of(new PromotedNotificationContentExtractor(provider, context, logger)); + } else { + return Optional.empty(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt index cf4fb25f638e..375983543c30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.notification.data import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository -import com.android.systemui.statusbar.policy.BaseHeadsUpManager +import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl import dagger.Binds import dagger.Module @Module(includes = [NotificationSettingsRepositoryModule::class]) interface NotificationDataLayerModule { - @Binds fun bindHeadsUpNotificationRepository(impl: BaseHeadsUpManager): HeadsUpRepository + @Binds fun bindHeadsUpNotificationRepository(impl: HeadsUpManagerImpl): HeadsUpRepository } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRowRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRowRepository.kt index 7b40812d55c3..266310479a1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRowRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRowRepository.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.data.repository +import com.android.systemui.statusbar.notification.headsup.PinnedStatus import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey import kotlinx.coroutines.flow.StateFlow @@ -31,6 +32,6 @@ interface HeadsUpRowRepository : HeadsUpRowKey { /** A key to identify this row in the view hierarchy. */ val elementKey: Any - /** Whether this notification is "pinned", meaning that it should stay on top of the screen. */ - val isPinned: StateFlow<Boolean> + /** This notification's pinning status. */ + val pinnedStatus: StateFlow<PinnedStatus> } 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 e25127e3e0d6..81335658679b 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 @@ -52,7 +52,9 @@ constructor( val topHeadsUpRowIfPinned: Flow<HeadsUpRowKey?> = headsUpRepository.topHeadsUpRow .flatMapLatest { repository -> - repository?.isPinned?.map { pinned -> repository.takeIf { pinned } } ?: flowOf(null) + repository?.pinnedStatus?.map { pinnedStatus -> + repository.takeIf { pinnedStatus.isPinned } + } ?: flowOf(null) } .distinctUntilChanged() @@ -64,7 +66,7 @@ constructor( if (repositories.isNotEmpty()) { val toCombine: List<Flow<Pair<HeadsUpRowRepository, Boolean>>> = repositories.map { repo -> - repo.isPinned.map { isPinned -> repo to isPinned } + repo.pinnedStatus.map { pinnedStatus -> repo to pinnedStatus.isPinned } } combine(toCombine) { pairs -> pairs.toSet() } } else { @@ -102,7 +104,9 @@ constructor( } else { headsUpRepository.activeHeadsUpRows.flatMapLatest { rows -> if (rows.isNotEmpty()) { - combine(rows.map { it.isPinned }) { pins -> pins.any { it } } + combine(rows.map { it.pinnedStatus }) { pinnedStatus -> + pinnedStatus.any { it.isPinned } + } } else { // if the set is empty, there are no flows to combine flowOf(false) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt index 8edbc5e8e4bb..8bd7a1ab7a77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt @@ -152,6 +152,13 @@ private class ActiveNotificationsStoreBuilder( } else { null } + val promotedContent = + if (PromotedNotificationContentModel.featureFlagEnabled()) { + promotedNotificationContentModel + } else { + null + } + return existingModels.createOrReuse( key = key, groupKey = sbn.groupKey, @@ -174,7 +181,7 @@ private class ActiveNotificationsStoreBuilder( isGroupSummary = sbn.notification.isGroupSummary, bucket = bucket, callType = sbn.toCallType(), - promotedContent = promotedNotificationContentModel, + promotedContent = promotedContent, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/AvalancheController.kt index d406ed4e49c7..5c7c020a3bd9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/AvalancheController.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import android.os.Handler import android.util.Log @@ -24,29 +24,30 @@ import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl.HeadsUpEntry import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun -import com.android.systemui.statusbar.policy.BaseHeadsUpManager.HeadsUpEntry import com.android.systemui.util.Compile import java.io.PrintWriter import javax.inject.Inject /* * Control when heads up notifications show during an avalanche where notifications arrive in fast - * succession, by delaying visual listener side effects and removal handling from BaseHeadsUpManager + * succession, by delaying visual listener side effects and removal handling from + * [HeadsUpManagerImpl]. */ @SysUISingleton class AvalancheController @Inject constructor( - dumpManager: DumpManager, - private val uiEventLogger: UiEventLogger, - private val headsUpManagerLogger: HeadsUpManagerLogger, - @Background private val bgHandler: Handler + dumpManager: DumpManager, + private val uiEventLogger: UiEventLogger, + private val headsUpManagerLogger: HeadsUpManagerLogger, + @Background private val bgHandler: Handler, ) : Dumpable { private val tag = "AvalancheController" private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG) - var baseEntryMapStr : () -> String = { "baseEntryMapStr not initialized" } + var baseEntryMapStr: () -> String = { "baseEntryMapStr not initialized" } var enableAtRuntime = true set(value) { @@ -119,23 +120,29 @@ constructor( if (runnable == null) { headsUpManagerLogger.logAvalancheUpdate( - caller, isEnabled, key, - "Runnable NULL, stop. ${getStateStr()}" + caller, + isEnabled, + key, + "Runnable NULL, stop. ${getStateStr()}", ) return } if (!isEnabled) { headsUpManagerLogger.logAvalancheUpdate( - caller, isEnabled, key, - "NOT ENABLED, run runnable. ${getStateStr()}" + caller, + isEnabled, + key, + "NOT ENABLED, run runnable. ${getStateStr()}", ) runnable.run() return } if (entry == null) { headsUpManagerLogger.logAvalancheUpdate( - caller, isEnabled, key, - "Entry NULL, stop. ${getStateStr()}" + caller, + isEnabled, + key, + "Entry NULL, stop. ${getStateStr()}", ) return } @@ -168,7 +175,7 @@ constructor( headsUpEntryShowing!!.updateEntry( /* updatePostTime= */ false, /* updateEarliestRemovalTime= */ false, - /* reason= */ "avalanche duration update" + /* reason= */ "avalanche duration update", ) } } @@ -192,24 +199,30 @@ constructor( if (runnable == null) { headsUpManagerLogger.logAvalancheDelete( - caller, isEnabled, key, - "Runnable NULL, stop. ${getStateStr()}" + caller, + isEnabled, + key, + "Runnable NULL, stop. ${getStateStr()}", ) return } if (!isEnabled) { runnable.run() headsUpManagerLogger.logAvalancheDelete( - caller, isEnabled = false, key, - "NOT ENABLED, run runnable. ${getStateStr()}" + caller, + isEnabled = false, + key, + "NOT ENABLED, run runnable. ${getStateStr()}", ) return } if (entry == null) { runnable.run() headsUpManagerLogger.logAvalancheDelete( - caller, isEnabled = true, key, - "Entry NULL, run runnable. ${getStateStr()}" + caller, + isEnabled = true, + key, + "Entry NULL, run runnable. ${getStateStr()}", ) return } @@ -219,11 +232,9 @@ constructor( if (entry in nextList) nextList.remove(entry) uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_REMOVED) outcome = "remove from next. ${getStateStr()}" - } else if (entry in debugDropSet) { debugDropSet.remove(entry) outcome = "remove from dropset. ${getStateStr()}" - } else if (isShowing(entry)) { previousHunKey = getKey(headsUpEntryShowing) // Show the next HUN before removing this one, so that we don't tell listeners @@ -233,7 +244,6 @@ constructor( showNext() runnable.run() outcome = "remove showing. ${getStateStr()}" - } else { runnable.run() outcome = "run runnable for untracked shown HUN. ${getStateStr()}" @@ -421,13 +431,13 @@ constructor( private fun getStateStr(): String { return "\navalanche state:" + - "\n\tshowing: [${getKey(headsUpEntryShowing)}]" + - "\n\tprevious: [$previousHunKey]" + - "\n\tnext list: $nextListStr" + - "\n\tnext map: $nextMapStr" + - "\n\tdropped: $dropSetStr" + - "\nBHUM.mHeadsUpEntryMap: " + - baseEntryMapStr() + "\n\tshowing: [${getKey(headsUpEntryShowing)}]" + + "\n\tprevious: [$previousHunKey]" + + "\n\tnext list: $nextListStr" + + "\n\tnext map: $nextMapStr" + + "\n\tdropped: $dropSetStr" + + "\nBHUM.mHeadsUpEntryMap: " + + baseEntryMapStr() } private val dropSetStr: String diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt index b37194b8b7a0..424a3c5e6af9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManager.kt @@ -1,4 +1,20 @@ -package com.android.systemui.statusbar.policy +/* + * 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.notification.headsup import android.graphics.Region import com.android.systemui.Dumpable diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerExt.kt index 5e367509e774..6525b6f1186b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerExt.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerExt.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.statusbar.notification.collection.NotificationEntry diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java index 298ef7ee4bfa..99df9f45840a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import android.annotation.NonNull; import android.annotation.Nullable; @@ -55,6 +55,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun; import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply; import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.kotlin.JavaAdapter; @@ -84,7 +86,7 @@ import kotlinx.coroutines.flow.StateFlowKt; * they simply peek from the top of the screen. */ @SysUISingleton -public class BaseHeadsUpManager +public class HeadsUpManagerImpl implements HeadsUpManager, HeadsUpRepository, OnHeadsUpChangedListener { private static final String TAG = "BaseHeadsUpManager"; private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms"; @@ -180,7 +182,7 @@ public class BaseHeadsUpManager } @Inject - public BaseHeadsUpManager( + public HeadsUpManagerImpl( @NonNull final Context context, HeadsUpManagerLogger logger, StatusBarStateController statusBarStateController, @@ -403,9 +405,11 @@ public class BaseHeadsUpManager } if (shouldHeadsUpAgain) { headsUpEntry.updateEntry(true /* updatePostTime */, "updateNotification"); + PinnedStatus pinnedStatus = shouldHeadsUpBecomePinned(headsUpEntry.mEntry) + ? PinnedStatus.PinnedBySystem + : PinnedStatus.NotPinned; if (headsUpEntry != null) { - setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUpEntry.mEntry), - "updateNotificationInternal"); + setEntryPinned(headsUpEntry, pinnedStatus, "updateNotificationInternal"); } } } @@ -417,7 +421,7 @@ public class BaseHeadsUpManager @Override public boolean shouldSwallowClick(@NonNull String key) { - BaseHeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key); + HeadsUpManagerImpl.HeadsUpEntry entry = getHeadsUpEntry(key); return entry != null && mSystemClock.elapsedRealtime() < entry.mPostTime; } @@ -560,15 +564,16 @@ public class BaseHeadsUpManager } protected void setEntryPinned( - @NonNull BaseHeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned, + @NonNull HeadsUpManagerImpl.HeadsUpEntry headsUpEntry, PinnedStatus pinnedStatus, String reason) { - mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned, reason); + mLogger.logSetEntryPinned(headsUpEntry.mEntry, pinnedStatus, reason); NotificationEntry entry = headsUpEntry.mEntry; + boolean isPinned = pinnedStatus.isPinned(); if (!isPinned) { headsUpEntry.mWasUnpinned = true; } - if (headsUpEntry.isRowPinned() != isPinned) { - headsUpEntry.setRowPinned(isPinned); + if (headsUpEntry.getPinnedStatus().getValue() != pinnedStatus) { + headsUpEntry.setRowPinnedStatus(pinnedStatus); updatePinnedMode(); if (isPinned && entry.getSbn() != null) { mUiEventLogger.logWithInstanceId( @@ -594,8 +599,10 @@ public class BaseHeadsUpManager NotificationEntry entry = headsUpEntry.mEntry; entry.setHeadsUp(true); - final boolean shouldPin = shouldHeadsUpBecomePinned(entry); - setEntryPinned(headsUpEntry, shouldPin, "onEntryAdded"); + final PinnedStatus pinnedStatus = shouldHeadsUpBecomePinned(entry) + ? PinnedStatus.PinnedBySystem + : PinnedStatus.NotPinned; + setEntryPinned(headsUpEntry, pinnedStatus, "onEntryAdded"); EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 1 /* visible */); for (OnHeadsUpChangedListener listener : mListeners) { listener.onHeadsUpStateChanged(entry, true); @@ -653,7 +660,7 @@ public class BaseHeadsUpManager protected void onEntryRemoved(HeadsUpEntry headsUpEntry, String reason) { NotificationEntry entry = headsUpEntry.mEntry; entry.setHeadsUp(false); - setEntryPinned(headsUpEntry, false /* isPinned */, "onEntryRemoved"); + setEntryPinned(headsUpEntry, PinnedStatus.NotPinned, "onEntryRemoved"); EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 0 /* visible */); mLogger.logNotificationActuallyRemoved(entry); for (OnHeadsUpChangedListener listener : mListeners) { @@ -962,7 +969,7 @@ public class BaseHeadsUpManager Runnable runnable = () -> { mLogger.logUnpinEntry(key); - setEntryPinned(headsUpEntry, false /* isPinned */, "unpinAll"); + setEntryPinned(headsUpEntry, PinnedStatus.NotPinned, "unpinAll"); // maybe it got un sticky headsUpEntry.updateEntry(false /* updatePostTime */, "unpinAll"); @@ -1233,7 +1240,8 @@ public class BaseHeadsUpManager @Nullable private Runnable mCancelRemoveRunnable; private boolean mGutsShownPinned; - private final MutableStateFlow<Boolean> mIsPinned = StateFlowKt.MutableStateFlow(false); + private final MutableStateFlow<PinnedStatus> mPinnedStatus = + StateFlowKt.MutableStateFlow(PinnedStatus.NotPinned); /** * If the time this entry has been on was extended @@ -1269,8 +1277,8 @@ public class BaseHeadsUpManager @Override @NonNull - public StateFlow<Boolean> isPinned() { - return mIsPinned; + public StateFlow<PinnedStatus> getPinnedStatus() { + return mPinnedStatus; } /** Attach a NotificationEntry. */ @@ -1300,9 +1308,9 @@ public class BaseHeadsUpManager return mEntry != null && mEntry.isRowPinned(); } - protected void setRowPinned(boolean pinned) { - if (mEntry != null) mEntry.setRowPinned(pinned); - mIsPinned.setValue(pinned); + protected void setRowPinnedStatus(PinnedStatus pinnedStatus) { + if (mEntry != null) mEntry.setRowPinnedStatus(pinnedStatus); + mPinnedStatus.setValue(pinnedStatus); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt index 600270c7189a..80225c47e9ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLogger.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel.INFO @@ -52,7 +52,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { caller: String, isEnabled: Boolean, notifEntryKey: String, - outcome: String + outcome: String, ) { buffer.log( TAG, @@ -63,7 +63,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str3 = outcome bool1 = isEnabled }, - { "$str1\n\t=> AC[isEnabled:$bool1] update: $str2\n\t=> $str3" } + { "$str1\n\t=> AC[isEnabled:$bool1] update: $str2\n\t=> $str3" }, ) } @@ -71,7 +71,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { caller: String, isEnabled: Boolean, notifEntryKey: String, - outcome: String + outcome: String, ) { buffer.log( TAG, @@ -82,7 +82,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str3 = outcome bool1 = isEnabled }, - { "$str1\n\t=> AC[isEnabled:$bool1] delete: $str2\n\t=> $str3" } + { "$str1\n\t=> AC[isEnabled:$bool1] delete: $str2\n\t=> $str3" }, ) } @@ -99,7 +99,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { long1 = delayMillis str2 = reason }, - { "schedule auto remove of $str1 in $long1 ms reason: $str2" } + { "schedule auto remove of $str1 in $long1 ms reason: $str2" }, ) } @@ -111,7 +111,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str1 = entry.logKey str2 = reason }, - { "request: reschedule auto remove of $str1 reason: $str2" } + { "request: reschedule auto remove of $str1 reason: $str2" }, ) } @@ -124,7 +124,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { long1 = delayMillis str2 = reason }, - { "reschedule auto remove of $str1 in $long1 ms reason: $str2" } + { "reschedule auto remove of $str1 in $long1 ms reason: $str2" }, ) } @@ -136,7 +136,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str1 = entry.logKey str2 = reason ?: "unknown" }, - { "request: cancel auto remove of $str1 reason: $str2" } + { "request: cancel auto remove of $str1 reason: $str2" }, ) } @@ -148,7 +148,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str1 = entry.logKey str2 = reason ?: "unknown" }, - { "cancel auto remove of $str1 reason: $str2" } + { "cancel auto remove of $str1 reason: $str2" }, ) } @@ -161,7 +161,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str2 = reason bool1 = isWaiting }, - { "request: $str2 => remove entry $str1 isWaiting: $isWaiting" } + { "request: $str2 => remove entry $str1 isWaiting: $isWaiting" }, ) } @@ -174,7 +174,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str2 = reason bool1 = isWaiting }, - { "$str2 => remove entry $str1 isWaiting: $isWaiting" } + { "$str2 => remove entry $str1 isWaiting: $isWaiting" }, ) } @@ -190,7 +190,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { key: String, releaseImmediately: Boolean, isWaiting: Boolean, - reason: String + reason: String, ) { buffer.log( TAG, @@ -204,7 +204,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { { "remove notification $str1 releaseImmediately: $bool1 isWaiting: $bool2 " + "reason: $str2" - } + }, ) } @@ -216,7 +216,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { str1 = logKey(key) str2 = reason }, - { "remove notification $str1 when headsUpEntry is null, reason: $str2" } + { "remove notification $str1 when headsUpEntry is null, reason: $str2" }, ) } @@ -233,7 +233,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { bool1 = alert bool2 = hasEntry }, - { "request: update notification $str1 alert: $bool1 hasEntry: $bool2" } + { "request: update notification $str1 alert: $bool1 hasEntry: $bool2" }, ) } @@ -246,7 +246,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { bool1 = alert bool2 = hasEntry }, - { "update notification $str1 alert: $bool1 hasEntry: $bool2" } + { "update notification $str1 alert: $bool1 hasEntry: $bool2" }, ) } @@ -259,7 +259,7 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { bool1 = updatePostTime str2 = reason ?: "unknown" }, - { "update entry $str1 updatePostTime: $bool1 reason: $str2" } + { "update entry $str1 updatePostTime: $bool1 reason: $str2" }, ) } @@ -268,20 +268,20 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { TAG, INFO, { int1 = packageSnoozeLengthMs }, - { "snooze length changed: ${int1}ms" } + { "snooze length changed: ${int1}ms" }, ) } - fun logSetEntryPinned(entry: NotificationEntry, isPinned: Boolean, reason: String) { + fun logSetEntryPinned(entry: NotificationEntry, pinnedStatus: PinnedStatus, reason: String) { buffer.log( TAG, VERBOSE, { str1 = entry.logKey - bool1 = isPinned str2 = reason + str3 = pinnedStatus.name }, - { "$str2 => set entry pinned $str1 pinned: $bool1" } + { "$str2 => set entry pinned $str1 pinned: $str3" }, ) } @@ -290,16 +290,12 @@ constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) { TAG, INFO, { bool1 = hasPinnedNotification }, - { "has pinned notification changed to $bool1" } + { "has pinned notification changed to $bool1" }, ) } fun logRemoveEntryAfterExpand(entry: NotificationEntry) { - buffer.log(TAG, VERBOSE, { - str1 = entry.logKey - }, { - "remove entry after expand: $str1" - }) + buffer.log(TAG, VERBOSE, { str1 = entry.logKey }, { "remove entry after expand: $str1" }) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpModule.kt index 83551e9b8294..f9502eeff270 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpModule.kt @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone +package com.android.systemui.statusbar.notification.headsup import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.policy.BaseHeadsUpManager -import com.android.systemui.statusbar.policy.HeadsUpManager import dagger.Binds import dagger.Module @Module interface HeadsUpModule { - @Binds @SysUISingleton fun bindsHeadsUpManager(hum: BaseHeadsUpManager): HeadsUpManager + @Binds @SysUISingleton fun bindsHeadsUpManager(hum: HeadsUpManagerImpl): HeadsUpManager } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpNotificationViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpNotificationViewControllerEmptyImpl.kt index 021d30144b32..84754e530055 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpNotificationViewControllerEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpNotificationViewControllerEmptyImpl.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification +package com.android.systemui.statusbar.notification.headsup import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper.HeadsUpNotificationViewController +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper.HeadsUpNotificationViewController /** Empty impl of [HeadsUpNotificationViewController] for use with Scene Container */ class HeadsUpNotificationViewControllerEmptyImpl : HeadsUpNotificationViewController { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpTouchHelper.java index a61463823613..3b6b9ed636fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpTouchHelper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification; +package com.android.systemui.statusbar.notification.headsup; import android.content.Context; import android.os.RemoteException; @@ -27,7 +27,6 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; -import com.android.systemui.statusbar.policy.HeadsUpManager; /** * A helper class to handle touches on the heads-up views. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpUtil.java index f4a1975fab52..40da232ae598 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpUtil.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import android.annotation.NonNull; import android.annotation.Nullable; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/OnHeadsUpChangedListener.java index de3bf0462d5b..b1fd78402570 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/OnHeadsUpChangedListener.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.notification.headsup; import android.annotation.NonNull; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/PinnedStatus.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/PinnedStatus.kt new file mode 100644 index 000000000000..af1805476f05 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/PinnedStatus.kt @@ -0,0 +1,39 @@ +/* + * 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.notification.headsup + +/** + * A status representing whether and how a notification is pinned. + * + * @property isPinned true if a notification should be "pinned", meaning that a notification should + * stay on top of the screen. + */ +enum class PinnedStatus(val isPinned: Boolean) { + /** This notification is not pinned. */ + NotPinned(isPinned = false), + /** + * This notification is pinned by the system - likely because when the notification was added or + * updated, it required pinning. + */ + PinnedBySystem(isPinned = true), + /** + * This notification is pinned because the user did an explicit action to pin it (like tapping + * the notification chip in the status bar). + */ + // TODO(b/364653005): Use this status when a user taps the notification chip. + PinnedByUser(isPinned = true), +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/StatusBarHeadsUpChangeListener.java index 7145ffe1f515..23eb50700060 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/StatusBarHeadsUpChangeListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.statusbar.notification.headsup; import com.android.systemui.CoreStartable; import com.android.systemui.dagger.SysUISingleton; @@ -26,14 +26,13 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import javax.inject.Inject; /** - * Ties the status bar to {@link com.android.systemui.statusbar.policy.HeadsUpManager}. + * Ties the status bar to {@link HeadsUpManager}. */ @SysUISingleton public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, CoreStartable { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt index ec0827b4c478..caa6ccf9a5fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt @@ -52,13 +52,13 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor import com.android.systemui.statusbar.StatusBarState.SHADE import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE import com.android.systemui.statusbar.policy.BatteryController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.NotificationChannels import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SystemSettings @@ -102,7 +102,7 @@ class PeekDisabledSuppressor( globalSettings.registerContentObserverSync( globalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED), /* notifyForDescendants = */ true, - observer + observer, ) // QQQ: Do we need to register for SETTING_HEADS_UP_TICKER? It seems unused. @@ -139,7 +139,7 @@ class PeekPackageSnoozedSuppressor(private val headsUpManager: HeadsUpManager) : class PeekAlreadyBubbledSuppressor( private val statusBarStateController: StatusBarStateController, - private val bubbles: Optional<Bubbles> + private val bubbles: Optional<Bubbles>, ) : VisualInterruptionFilter(types = setOf(PEEK), reason = "already bubbled") { override fun shouldSuppress(entry: NotificationEntry) = when { @@ -164,7 +164,7 @@ class PeekNotImportantSuppressor : class PeekDeviceNotInUseSuppressor( private val powerManager: PowerManager, - private val statusBarStateController: StatusBarStateController + private val statusBarStateController: StatusBarStateController, ) : VisualInterruptionCondition(types = setOf(PEEK), reason = "device not in use") { override fun shouldSuppress() = when { @@ -177,7 +177,7 @@ class PeekOldWhenSuppressor(private val systemClock: SystemClock) : VisualInterruptionFilter( types = setOf(PEEK), reason = "has old `when`", - uiEventId = HUN_SUPPRESSED_OLD_WHEN + uiEventId = HUN_SUPPRESSED_OLD_WHEN, ) { private fun whenAge(entry: NotificationEntry) = systemClock.currentTimeMillis() - entry.sbn.notification.getWhen() @@ -206,7 +206,7 @@ class PulseEffectSuppressor : class PulseLockscreenVisibilityPrivateSuppressor : VisualInterruptionFilter( types = setOf(PULSE), - reason = "hidden by lockscreen visibility override" + reason = "hidden by lockscreen visibility override", ) { override fun shouldSuppress(entry: NotificationEntry) = entry.ranking.lockscreenVisibilityOverride == VISIBILITY_PRIVATE @@ -220,7 +220,7 @@ class PulseLowImportanceSuppressor : class HunGroupAlertBehaviorSuppressor : VisualInterruptionFilter( types = setOf(PEEK, PULSE), - reason = "suppressive group alert behavior" + reason = "suppressive group alert behavior", ) { override fun shouldSuppress(entry: NotificationEntry) = entry.sbn.let { it.isGroup && it.notification.suppressAlertingDueToGrouping() } @@ -282,11 +282,7 @@ class AvalancheSuppressor( private val notificationManager: NotificationManager, private val logger: VisualInterruptionDecisionLogger, private val systemSettings: SystemSettings, -) : - VisualInterruptionFilter( - types = setOf(PEEK, PULSE), - reason = "avalanche", - ) { +) : VisualInterruptionFilter(types = setOf(PEEK, PULSE), reason = "avalanche") { val TAG = "AvalancheSuppressor" private val prefs = context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE) @@ -324,7 +320,7 @@ class AvalancheSuppressor( ALLOW_FSI_WITH_PERMISSION_ON, ALLOW_COLORIZED, ALLOW_EMERGENCY, - SUPPRESS + SUPPRESS, } enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum { @@ -354,6 +350,7 @@ class AvalancheSuppressor( AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_EMERGENCY(1868), @UiEvent(doc = "HUN allowed during avalanche because it is a car warning") AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_WARNING(1869); + override fun getId(): Int { return id } @@ -399,7 +396,7 @@ class AvalancheSuppressor( val bundle = Bundle() bundle.putString( Notification.EXTRA_SUBSTITUTE_APP_NAME, - context.getString(com.android.internal.R.string.android_system_label) + context.getString(com.android.internal.R.string.android_system_label), ) val builder = @@ -452,7 +449,8 @@ class AvalancheSuppressor( if (entry.sbn.notification.category == CATEGORY_CAR_EMERGENCY) { uiEventLogger.log( - AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_EMERGENCY) + AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_CAR_EMERGENCY + ) return State.ALLOW_CATEGORY_CAR_EMERGENCY } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 450067a969e2..f586051feb56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -47,7 +47,7 @@ import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.EventLog; import com.android.systemui.util.settings.GlobalSettings; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt index 52336be742cd..b831b9457acd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt @@ -32,6 +32,7 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.FullScreenIntentDecision import com.android.systemui.statusbar.notification.interruption.VisualInterruptionSuppressor.EventLogData @@ -41,7 +42,6 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.EventLog import com.android.systemui.util.settings.GlobalSettings diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt new file mode 100644 index 000000000000..f400d605cb43 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt @@ -0,0 +1,157 @@ +/* + * 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.notification.promoted + +import android.app.Notification +import android.app.Notification.BigPictureStyle +import android.app.Notification.BigTextStyle +import android.app.Notification.CallStyle +import android.app.Notification.EXTRA_CHRONOMETER_COUNT_DOWN +import android.app.Notification.EXTRA_SUB_TEXT +import android.app.Notification.EXTRA_TEXT +import android.app.Notification.EXTRA_TITLE +import android.app.Notification.ProgressStyle +import android.content.Context +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When +import javax.inject.Inject + +@SysUISingleton +class PromotedNotificationContentExtractor +@Inject +constructor( + private val promotedNotificationsProvider: PromotedNotificationsProvider, + private val context: Context, + private val logger: PromotedNotificationLogger, +) { + fun extractContent( + entry: NotificationEntry, + recoveredBuilder: Notification.Builder, + ): PromotedNotificationContentModel? { + if (!PromotedNotificationContentModel.featureFlagEnabled()) { + logger.logExtractionSkipped(entry, "feature flags disabled") + return null + } + + if (!promotedNotificationsProvider.shouldPromote(entry)) { + logger.logExtractionSkipped(entry, "shouldPromote returned false") + return null + } + + val notification = entry.sbn.notification + if (notification == null) { + logger.logExtractionFailed(entry, "entry.sbn.notification is null") + return null + } + + val contentBuilder = PromotedNotificationContentModel.Builder(entry.key) + + // TODO: Pitch a fit if style is unsupported or mandatory fields are missing once + // FLAG_PROMOTED_ONGOING is set reliably and we're not testing status bar chips. + + contentBuilder.skeletonSmallIcon = entry.icons.aodIcon?.sourceIcon + contentBuilder.appName = notification.loadHeaderAppName(context) + contentBuilder.subText = notification.subText() + contentBuilder.time = notification.extractWhen() + contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs + contentBuilder.profileBadgeResId = null // TODO + contentBuilder.title = notification.title() + contentBuilder.text = notification.text() + contentBuilder.skeletonLargeIcon = null // TODO + + recoveredBuilder.style?.extractContent(contentBuilder) + ?: run { contentBuilder.style = Style.Ineligible } + + return contentBuilder.build().also { logger.logExtractionSucceeded(entry, it) } + } +} + +private fun Notification.title(): CharSequence? = extras?.getCharSequence(EXTRA_TITLE) + +private fun Notification.text(): CharSequence? = extras?.getCharSequence(EXTRA_TEXT) + +private fun Notification.subText(): String? = extras?.getString(EXTRA_SUB_TEXT) + +private fun Notification.chronometerCountDown(): Boolean = + extras?.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, /* defaultValue= */ false) ?: false + +private fun Notification.extractWhen(): When? { + val time = `when` + val showsTime = showsTime() + val showsChronometer = showsChronometer() + val countDown = chronometerCountDown() + + return when { + showsTime -> When(time, When.Mode.Absolute) + showsChronometer -> When(time, if (countDown) When.Mode.CountDown else When.Mode.CountUp) + else -> null + } +} + +private fun Notification.Style.extractContent( + contentBuilder: PromotedNotificationContentModel.Builder +) { + contentBuilder.style = + when (this) { + is BigPictureStyle -> { + extractContent(contentBuilder) + Style.BigPicture + } + + is BigTextStyle -> { + extractContent(contentBuilder) + Style.BigText + } + + is CallStyle -> { + extractContent(contentBuilder) + Style.Call + } + + is ProgressStyle -> { + extractContent(contentBuilder) + Style.Progress + } + + else -> Style.Ineligible + } +} + +private fun BigPictureStyle.extractContent( + contentBuilder: PromotedNotificationContentModel.Builder +) { + // TODO? +} + +private fun BigTextStyle.extractContent(contentBuilder: PromotedNotificationContentModel.Builder) { + // TODO? +} + +private fun CallStyle.extractContent(contentBuilder: PromotedNotificationContentModel.Builder) { + contentBuilder.personIcon = null // TODO + contentBuilder.personName = null // TODO + contentBuilder.verificationIcon = null // TODO + contentBuilder.verificationText = null // TODO +} + +private fun ProgressStyle.extractContent(contentBuilder: PromotedNotificationContentModel.Builder) { + // TODO: Create NotificationProgressModel.toSkeleton, or something similar. + contentBuilder.progress = createProgressModel(0xffffffff.toInt(), 0x00000000) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt new file mode 100644 index 000000000000..13ad1413e89d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt @@ -0,0 +1,71 @@ +/* + * 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.notification.promoted + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.LogLevel.ERROR +import com.android.systemui.log.core.LogLevel.INFO +import com.android.systemui.log.dagger.NotificationLog +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.logKey +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import javax.inject.Inject + +class PromotedNotificationLogger +@Inject +constructor(@NotificationLog private val buffer: LogBuffer) { + fun logExtractionSkipped(entry: NotificationEntry, reason: String) { + buffer.log( + EXTRACTION_TAG, + INFO, + { + str1 = entry.logKey + str2 = reason + }, + { "extraction skipped: $str2 for $str1" }, + ) + } + + fun logExtractionFailed(entry: NotificationEntry, reason: String) { + buffer.log( + EXTRACTION_TAG, + ERROR, + { + str1 = entry.logKey + str2 = reason + }, + { "extraction failed: $str2 for $str1" }, + ) + } + + fun logExtractionSucceeded( + entry: NotificationEntry, + content: PromotedNotificationContentModel, + ) { + buffer.log( + EXTRACTION_TAG, + INFO, + { + str1 = entry.logKey + str2 = content.toString() + }, + { "extraction succeeded: $str2 for $str1" }, + ) + } +} + +private const val EXTRACTION_TAG = "PromotedNotificationContentExtractor" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt index 691dc6f5ccac..947d9e3e9547 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.promoted import android.app.Notification.FLAG_PROMOTED_ONGOING import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import javax.inject.Inject /** A provider for making decisions on which notifications should be promoted. */ @@ -30,7 +31,7 @@ interface PromotedNotificationsProvider { @SysUISingleton open class PromotedNotificationsProviderImpl @Inject constructor() : PromotedNotificationsProvider { override fun shouldPromote(entry: NotificationEntry): Boolean { - if (!PromotedNotificationUi.isEnabled) { + if (!PromotedNotificationContentModel.featureFlagEnabled()) { return false } return (entry.sbn.notification.flags and FLAG_PROMOTED_ONGOING) != 0 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index a90a1053e01c..b7ab996a608c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -36,6 +36,7 @@ import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Path; import android.graphics.Point; +import android.graphics.Rect; import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; @@ -101,6 +102,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.logging.NotificationCounters; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -108,6 +110,7 @@ import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderVi import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction; import com.android.systemui.statusbar.notification.row.wrapper.NotificationCompactMessagingTemplateViewWrapper; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; +import com.android.systemui.statusbar.notification.shared.NotificationAddXOnHoverToDismiss; import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization; import com.android.systemui.statusbar.notification.shared.TransparentHeaderFix; import com.android.systemui.statusbar.notification.stack.AmbientState; @@ -118,13 +121,14 @@ import com.android.systemui.statusbar.notification.stack.NotificationChildrenCon import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.SwipeableView; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; import com.android.systemui.statusbar.policy.RemoteInputView; import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; import com.android.systemui.util.Compile; import com.android.systemui.util.DumpUtilsKt; +import com.android.systemui.util.ListenerSet; import com.android.systemui.wmshell.BubblesManager; import java.io.PrintWriter; @@ -277,7 +281,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationMenuRowPlugin mMenuRow; private ViewStub mGutsStub; private boolean mIsSystemChildExpanded; - private boolean mIsPinned; + private PinnedStatus mPinnedStatus = PinnedStatus.NotPinned; private boolean mExpandAnimationRunning; private AboveShelfChangedListener mAboveShelfChangedListener; private HeadsUpManager mHeadsUpManager; @@ -430,6 +434,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private float mBottomRoundnessDuringLaunchAnimation; private float mSmallRoundness; + private ListenerSet<DismissButtonTargetVisibilityListener> + mDismissButtonTargetVisibilityListeners + = new ListenerSet(); + public NotificationContentView[] getLayouts() { return Arrays.copyOf(mLayouts, mLayouts.length); } @@ -739,6 +747,73 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } + public interface DismissButtonTargetVisibilityListener { + // Called when the notification dismiss button's target visibility changes. + // NOTE: This can be called when the dismiss button already has the target visibility. + void onTargetVisibilityChanged(boolean targetVisible); + } + + public void addDismissButtonTargetStateListener( + DismissButtonTargetVisibilityListener listener) { + if (NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode()) { + return; + } + + mDismissButtonTargetVisibilityListeners.addIfAbsent(listener); + } + + public void removeDismissButtonTargetStateListener( + DismissButtonTargetVisibilityListener listener) { + if (NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode()) { + return; + } + + mDismissButtonTargetVisibilityListeners.remove(listener); + } + + @Override + public boolean onInterceptHoverEvent(MotionEvent event) { + if (!NotificationAddXOnHoverToDismiss.isEnabled()) { + return super.onInterceptHoverEvent(event); + } + + // Do not bother checking the dismiss button's target visibility if the notification cannot + // be dismissed. + if (!canEntryBeDismissed()) { + return false; + } + + final Boolean targetVisible = getDismissButtonTargetVisibilityIfAny(event); + if (targetVisible != null) { + for (DismissButtonTargetVisibilityListener listener : + mDismissButtonTargetVisibilityListeners) { + listener.onTargetVisibilityChanged(targetVisible.booleanValue()); + } + } + + // Do not consume the hover event so that children still have a chance to process it. + return false; + } + + private @Nullable Boolean getDismissButtonTargetVisibilityIfAny(MotionEvent event) { + // Returns the dismiss button's target visibility resulted by `event`. Returns null if the + // target visibility should not change. + + if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { + // The notification dismiss button should be hidden when the hover exit event is located + // outside of the notification. NOTE: The hover exit event can be inside the + // notification if hover moves from one hoverable child to another. + final Rect localBounds = new Rect(0, 0, this.getWidth(), this.getActualHeight()); + if (!localBounds.contains((int) event.getX(), (int) event.getY())) { + return Boolean.FALSE; + } + } else if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) { + return Boolean.TRUE; + } + + return null; + } + private void updateLimitsForView(NotificationContentView layout) { View contractedView = layout.getContractedChild(); boolean customView = contractedView != null @@ -1154,17 +1229,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView /** * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this * the notification will be rendered on top of the screen. - * - * @param pinned whether it is pinned */ - public void setPinned(boolean pinned) { + public void setPinnedStatus(PinnedStatus pinnedStatus) { int intrinsicHeight = getIntrinsicHeight(); boolean wasAboveShelf = isAboveShelf(); - mIsPinned = pinned; + mPinnedStatus = pinnedStatus; if (intrinsicHeight != getIntrinsicHeight()) { notifyHeightChanged(/* needsAnimation= */ false); } - if (pinned) { + if (pinnedStatus.isPinned()) { setAnimationRunning(true); mExpandedWhenPinned = false; } else if (mExpandedWhenPinned) { @@ -1178,7 +1251,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public boolean isPinned() { - return mIsPinned; + return mPinnedStatus.isPinned(); } @Override @@ -1536,6 +1609,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mPrivateLayout.getSingleLineView(); } + /** + * Whether this row is displayed over the unoccluded lockscreen. Returns false on the + * locked shade. + */ public boolean isOnKeyguard() { return mOnKeyguard; } @@ -2214,6 +2291,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mTranslateableViews.remove(mGutsStub); // We don't handle focus highlight in this view, it's done in background drawable instead setDefaultFocusHighlightEnabled(false); + + if (NotificationAddXOnHoverToDismiss.isEnabled()) { + addDismissButtonTargetStateListener(findViewById(R.id.backgroundNormal)); + } } /** @@ -2820,7 +2901,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } - void setOnKeyguard(boolean onKeyguard) { + /** @see #isOnKeyguard() */ + public void setOnKeyguard(boolean onKeyguard) { if (onKeyguard != mOnKeyguard) { boolean wasAboveShelf = isAboveShelf(); final boolean wasExpanded = isExpanded(); @@ -3749,7 +3831,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public boolean isAboveShelf() { return (canShowHeadsUp() - && (mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf) + && (mPinnedStatus.isPinned() + || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf) || mExpandAnimationRunning || mChildIsExpanding)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index baad616cbf02..a150f7f1f54e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -41,6 +41,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.ColorUpdateLogger; import com.android.systemui.statusbar.notification.FeedbackIcon; @@ -58,7 +59,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationChildrenCon import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.ui.view.NotificationRowStatsLogger; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent; import com.android.systemui.util.time.SystemClock; @@ -378,15 +379,19 @@ public class ExpandableNotificationRowController implements NotifViewController mView.getEntry().setInitializationTime(mClock.elapsedRealtime()); mPluginManager.addPluginListener(mView, NotificationMenuRowPlugin.class, false /* Allow multiple */); - mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD); - mStatusBarStateController.addCallback(mStatusBarStateListener); + if (!SceneContainerFlag.isEnabled()) { + mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD); + mStatusBarStateController.addCallback(mStatusBarStateListener); + } mSettingsController.addCallback(BUBBLES_SETTING_URI, mSettingsListener); } @Override public void onViewDetachedFromWindow(View v) { mPluginManager.removePluginListener(mView); - mStatusBarStateController.removeCallback(mStatusBarStateListener); + if (!SceneContainerFlag.isEnabled()) { + mStatusBarStateController.removeCallback(mStatusBarStateListener); + } mSettingsController.removeCallback(BUBBLES_SETTING_URI, mSettingsListener); } }); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java index 77f5717d0e91..11db2fc77170 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java @@ -52,7 +52,7 @@ import com.android.systemui.res.R; import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import javax.inject.Inject; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java index d0db5145e0ff..34ef63944f14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java @@ -21,7 +21,9 @@ import static com.android.systemui.util.ColorUtilKt.hexColorString; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Canvas; +import android.graphics.Path; import android.graphics.PorterDuff; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; @@ -36,6 +38,7 @@ import com.android.internal.util.ContrastColorUtil; import com.android.settingslib.Utils; import com.android.systemui.Dumpable; import com.android.systemui.res.R; +import com.android.systemui.statusbar.notification.shared.NotificationAddXOnHoverToDismiss; import com.android.systemui.util.DrawableDumpKt; import java.io.PrintWriter; @@ -44,7 +47,8 @@ import java.util.Arrays; /** * A view that can be used for both the dimmed and normal background of an notification. */ -public class NotificationBackgroundView extends View implements Dumpable { +public class NotificationBackgroundView extends View implements Dumpable, + ExpandableNotificationRow.DismissButtonTargetVisibilityListener { private final boolean mDontModifyCorners; private Drawable mBackground; @@ -66,6 +70,11 @@ public class NotificationBackgroundView extends View implements Dumpable { private final ColorStateList mLightColoredStatefulColors; private final ColorStateList mDarkColoredStatefulColors; private final int mNormalColor; + private final int convexR = 9; + private final int concaveR = 22; + + // True only if the dismiss button is visible. + private boolean mDrawDismissButtonCutout = false; public NotificationBackgroundView(Context context, AttributeSet attrs) { super(context, attrs); @@ -80,6 +89,18 @@ public class NotificationBackgroundView extends View implements Dumpable { } @Override + public void onTargetVisibilityChanged(boolean targetVisible) { + if (NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode()) { + return; + } + + if (mDrawDismissButtonCutout != targetVisible) { + mDrawDismissButtonCutout = targetVisible; + invalidate(); + } + } + + @Override protected void onDraw(Canvas canvas) { if (mClipTopAmount + mClipBottomAmount < getActualHeight() || mExpandAnimationRunning) { canvas.save(); @@ -87,12 +108,87 @@ public class NotificationBackgroundView extends View implements Dumpable { canvas.clipRect(0, mClipTopAmount, getWidth(), getActualHeight() - mClipBottomAmount); } - draw(canvas, mBackground); + + if (!NotificationAddXOnHoverToDismiss.isEnabled()) { + draw(canvas, mBackground); + canvas.restore(); + return; + } + + Rect backgroundBounds = null; + if (mBackground != null || mDrawDismissButtonCutout) { + backgroundBounds = calculateBackgroundBounds(); + } + + if (mDrawDismissButtonCutout) { + canvas.clipPath(calculateDismissButtonCutoutPath(backgroundBounds)); + } + + if (mBackground != null) { + mBackground.setBounds(backgroundBounds); + mBackground.draw(canvas); + } + canvas.restore(); } } + private Path calculateDismissButtonCutoutPath(Rect backgroundBounds) { + // TODO(b/365585705): Adapt to RTL after the UX design is finalized. + + NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode(); + + Path path = new Path(); + + final int left = backgroundBounds.left; + final int right = backgroundBounds.right; + final int top = backgroundBounds.top; + final int bottom = backgroundBounds.bottom; + + // Generate the path clockwise from the left-top corner. + path.moveTo(left, top); + path.lineTo(right - 2 * convexR - concaveR, top); + path.quadTo(right - convexR - concaveR, top, right - convexR - concaveR, + top + convexR); + path.quadTo(right - convexR - concaveR, top + convexR + concaveR, right - convexR, + top + convexR + concaveR); + path.quadTo(right, top + convexR + concaveR, right, top + 2 * convexR + concaveR); + path.lineTo(right, bottom); + path.lineTo(left, bottom); + path.lineTo(left, top); + + return path; + } + + private Rect calculateBackgroundBounds() { + NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode(); + + int top = 0; + int bottom = getActualHeight(); + if (mBottomIsRounded + && mBottomAmountClips + && !mExpandAnimationRunning) { + bottom -= mClipBottomAmount; + } + final boolean isRtl = isLayoutRtl(); + final int width = getWidth(); + final int actualWidth = getActualWidth(); + + int left = isRtl ? width - actualWidth : 0; + int right = isRtl ? width : actualWidth; + + if (mExpandAnimationRunning) { + // Horizontally center this background view inside of the container + left = (int) ((width - actualWidth) / 2.0f); + right = (int) (left + actualWidth); + } + + return new Rect(left, top, right, bottom); + } + private void draw(Canvas canvas, Drawable drawable) { + NotificationAddXOnHoverToDismiss.assertInLegacyMode(); + if (drawable != null) { int top = 0; int bottom = getActualHeight(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 41abac1d47f7..6e05e8e8b80e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -54,6 +54,8 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor; +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction; @@ -92,6 +94,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final SmartReplyStateInflater mSmartReplyStateInflater; private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider; private final HeadsUpStyleProvider mHeadsUpStyleProvider; + private final PromotedNotificationContentExtractor mPromotedNotificationContentExtractor; private final NotificationRowContentBinderLogger mLogger; @@ -105,6 +108,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder SmartReplyStateInflater smartRepliesInflater, NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider, HeadsUpStyleProvider headsUpStyleProvider, + PromotedNotificationContentExtractor promotedNotificationContentExtractor, NotificationRowContentBinderLogger logger) { NotificationRowContentBinderRefactor.assertInLegacyMode(); mRemoteViewCache = remoteViewCache; @@ -115,6 +119,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mSmartReplyStateInflater = smartRepliesInflater; mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider; mHeadsUpStyleProvider = headsUpStyleProvider; + mPromotedNotificationContentExtractor = promotedNotificationContentExtractor; mLogger = logger; } @@ -165,6 +170,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mSmartReplyStateInflater, mNotifLayoutInflaterFactoryProvider, mHeadsUpStyleProvider, + mPromotedNotificationContentExtractor, mLogger); if (mInflateSynchronously) { task.onPostExecute(task.doInBackground()); @@ -913,6 +919,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder NotificationContentView privateLayout = row.getPrivateLayout(); NotificationContentView publicLayout = row.getPublicLayout(); logger.logAsyncTaskProgress(entry, "finishing"); + + if (PromotedNotificationContentModel.featureFlagEnabled()) { + entry.setPromotedNotificationContentModel(result.mExtractedPromotedNotificationContent); + } + boolean setRepliesAndActions = true; if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { if (result.inflatedContentView != null) { @@ -1123,6 +1134,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final SmartReplyStateInflater mSmartRepliesInflater; private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider; private final HeadsUpStyleProvider mHeadsUpStyleProvider; + private final PromotedNotificationContentExtractor mPromotedNotificationContentExtractor; private final NotificationRowContentBinderLogger mLogger; private AsyncInflationTask( @@ -1142,6 +1154,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder SmartReplyStateInflater smartRepliesInflater, NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider, HeadsUpStyleProvider headsUpStyleProvider, + PromotedNotificationContentExtractor promotedNotificationContentExtractor, NotificationRowContentBinderLogger logger) { mEntry = entry; mRow = row; @@ -1160,6 +1173,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mIsMediaInQS = isMediaFlagEnabled; mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider; mHeadsUpStyleProvider = headsUpStyleProvider; + mPromotedNotificationContentExtractor = promotedNotificationContentExtractor; mLogger = logger; entry.setInflationTask(this); } @@ -1276,6 +1290,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder ); } + if (PromotedNotificationContentModel.featureFlagEnabled()) { + mLogger.logAsyncTaskProgress(mEntry, "extracting promoted notification content"); + result.mExtractedPromotedNotificationContent = mPromotedNotificationContentExtractor + .extractContent(mEntry, recoveredBuilder); + mLogger.logAsyncTaskProgress(mEntry, "extracted promoted notification content: " + + result.mExtractedPromotedNotificationContent); + } + mLogger.logAsyncTaskProgress(mEntry, "getting row image resolver (on wrong thread!)"); final NotificationInlineImageResolver imageResolver = mRow.getImageResolver(); @@ -1377,6 +1399,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder @VisibleForTesting static class InflationProgress { + PromotedNotificationContentModel mExtractedPromotedNotificationContent; + private RemoteViews newContentView; private RemoteViews newHeadsUpView; private RemoteViews newExpandedView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 5ff9bc61f3ce..ea508748fada 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -72,7 +72,7 @@ import com.android.systemui.statusbar.notification.collection.render.NotifGutsVi import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.wmshell.BubblesManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt index d0c033bb10b0..c7d80e9d03ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.row import android.annotation.SuppressLint +import android.app.Flags import android.app.Notification import android.content.Context import android.content.ContextWrapper @@ -46,6 +47,8 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.notification.ConversationNotificationProcessor import com.android.systemui.statusbar.notification.InflationException import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP @@ -95,6 +98,7 @@ constructor( private val smartReplyStateInflater: SmartReplyStateInflater, private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider, private val headsUpStyleProvider: HeadsUpStyleProvider, + private val promotedNotificationContentExtractor: PromotedNotificationContentExtractor, private val logger: NotificationRowContentBinderLogger, ) : NotificationRowContentBinder { @@ -147,6 +151,7 @@ constructor( /* isMediaFlagEnabled = */ smartReplyStateInflater, notifLayoutInflaterFactoryProvider, headsUpStyleProvider, + promotedNotificationContentExtractor, logger, ) if (inflateSynchronously) { @@ -166,6 +171,7 @@ constructor( builder: Notification.Builder, packageContext: Context, smartRepliesInflater: SmartReplyStateInflater, + promotedNotificationContentExtractor: PromotedNotificationContentExtractor, ): InflationProgress { val systemUIContext = row.context val result = @@ -182,6 +188,7 @@ constructor( notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider, headsUpStyleProvider = headsUpStyleProvider, conversationProcessor = conversationProcessor, + promotedNotificationContentExtractor = promotedNotificationContentExtractor, logger = logger, ) inflateSmartReplyViews( @@ -372,6 +379,7 @@ constructor( private val smartRepliesInflater: SmartReplyStateInflater, private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider, private val headsUpStyleProvider: HeadsUpStyleProvider, + private val promotedNotificationContentExtractor: PromotedNotificationContentExtractor, private val logger: NotificationRowContentBinderLogger, ) : AsyncTask<Void, Void, Result<InflationProgress>>(), InflationCallback, InflationTask { private val context: Context @@ -442,6 +450,7 @@ constructor( notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider, headsUpStyleProvider = headsUpStyleProvider, conversationProcessor = conversationProcessor, + promotedNotificationContentExtractor = promotedNotificationContentExtractor, logger = logger, ) logger.logAsyncTaskProgress( @@ -582,6 +591,7 @@ constructor( @VisibleForTesting val packageContext: Context, val remoteViews: NewRemoteViews, val contentModel: NotificationContentModel, + val extractedPromotedNotificationContentModel: PromotedNotificationContentModel?, ) { var inflatedContentView: View? = null @@ -670,8 +680,23 @@ constructor( notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider, headsUpStyleProvider: HeadsUpStyleProvider, conversationProcessor: ConversationNotificationProcessor, + promotedNotificationContentExtractor: PromotedNotificationContentExtractor, logger: NotificationRowContentBinderLogger, ): InflationProgress { + val promoted = + if (PromotedNotificationContentModel.featureFlagEnabled()) { + logger.logAsyncTaskProgress(entry, "extracting promoted notification content") + val extracted = + promotedNotificationContentExtractor.extractContent(entry, builder) + logger.logAsyncTaskProgress( + entry, + "extracted promoted notification content: {extracted}", + ) + extracted + } else { + null + } + // process conversations and extract the messaging style val messagingStyle = if (entry.ranking.isConversation) { @@ -734,6 +759,7 @@ constructor( packageContext = packageContext, remoteViews = remoteViews, contentModel = contentModel, + extractedPromotedNotificationContentModel = promoted, ) } @@ -1393,6 +1419,11 @@ constructor( logger.logAsyncTaskProgress(entry, "finishing") entry.setContentModel(result.contentModel) + if (PromotedNotificationContentModel.featureFlagEnabled()) { + entry.promotedNotificationContentModel = + result.extractedPromotedNotificationContentModel + } + result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) } setContentViewsFromRemoteViews( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index b622defbef98..e9eecdd8a26f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.notification.row.wrapper; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y; import android.app.Notification; @@ -48,6 +51,7 @@ import com.android.systemui.statusbar.notification.Roundable; import com.android.systemui.statusbar.notification.RoundableState; import com.android.systemui.statusbar.notification.TransformState; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.shared.NotificationAddXOnHoverToDismiss; import java.util.Stack; @@ -115,6 +119,10 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple resolveHeaderViews(); addFeedbackOnClickListener(row); addCloseButtonOnClickListener(row); + + if (NotificationAddXOnHoverToDismiss.isEnabled()) { + mRow.addDismissButtonTargetStateListener(mHoverListener); + } } @Override @@ -166,13 +174,34 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple } } + private ExpandableNotificationRow.DismissButtonTargetVisibilityListener mHoverListener = new + ExpandableNotificationRow.DismissButtonTargetVisibilityListener() { + @Override + public void onTargetVisibilityChanged(boolean targetVisible) { + NotificationAddXOnHoverToDismiss.isUnexpectedlyInLegacyMode(); + + if (mCloseButton != null) { + mCloseButton.setVisibility(targetVisible ? VISIBLE : GONE); + } + } + }; + + @Override + public void setRemoved() { + super.setRemoved(); + + if (NotificationAddXOnHoverToDismiss.isEnabled()) { + mRow.removeDismissButtonTargetStateListener(mHoverListener); + } + } + /** * Shows the given feedback icon, or hides the icon if null. */ @Override public void setFeedbackIcon(@Nullable FeedbackIcon icon) { if (mFeedbackIcon != null) { - mFeedbackIcon.setVisibility(icon != null ? View.VISIBLE : View.GONE); + mFeedbackIcon.setVisibility(icon != null ? VISIBLE : GONE); if (icon != null) { if (mFeedbackIcon instanceof ImageButton) { ((ImageButton) mFeedbackIcon).setImageResource(icon.getIconRes()); @@ -266,7 +295,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple boolean expandable, View.OnClickListener onClickListener, boolean requestLayout) { - mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE); + mExpandButton.setVisibility(expandable ? VISIBLE : GONE); mExpandButton.setOnClickListener(expandable ? onClickListener : null); if (mAltExpandTarget != null) { mAltExpandTarget.setOnClickListener(expandable ? onClickListener : null); @@ -294,7 +323,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple @Override public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) { if (mAudiblyAlertedIcon != null) { - mAudiblyAlertedIcon.setVisibility(audiblyAlerted ? View.VISIBLE : View.GONE); + mAudiblyAlertedIcon.setVisibility(audiblyAlerted ? VISIBLE : GONE); } } @@ -371,6 +400,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple ((DateTimeView) timeView).setTime(whenMillis); } } + protected void addTransformedViews(View... views) { for (View view : views) { if (view != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationAddXOnHoverToDismiss.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationAddXOnHoverToDismiss.kt new file mode 100644 index 000000000000..0961874b2276 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationAddXOnHoverToDismiss.kt @@ -0,0 +1,41 @@ +/* + * 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.notification.shared + +import com.android.systemui.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for reading or using the notification dismiss button on hover flag state. */ +@Suppress("NOTHING_TO_INLINE") +object NotificationAddXOnHoverToDismiss { + const val FLAG_NAME = Flags.FLAG_NOTIFICATION_ADD_X_ON_HOVER_TO_DISMISS + + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + @JvmStatic + inline val isEnabled + get() = Flags.notificationAddXOnHoverToDismiss() + + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 777142194221..ad3611796d62 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -39,7 +39,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.policy.AvalancheController; +import com.android.systemui.statusbar.notification.headsup.AvalancheController; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java index 5c9a0b939dc7..f85545ef95f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java @@ -100,6 +100,9 @@ public interface NotificationListContainer extends */ void addContainerViewAt(View v, int index); + /** Sets whether the notificatios are displayed on the unoccluded lockscreen. */ + void setOnLockscreen(boolean isOnKeyguard); + /** * Sets the maximum number of notifications to display. * 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 57af8ea19722..223475e6e3b8 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 @@ -99,7 +99,7 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.ColorUpdateLogger; import com.android.systemui.statusbar.notification.FakeShadowView; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.LaunchAnimationParameters; import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -127,7 +127,7 @@ import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrol import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; -import com.android.systemui.statusbar.policy.HeadsUpUtil; +import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil; import com.android.systemui.statusbar.policy.ScrollAdapter; import com.android.systemui.statusbar.policy.SplitShadeStateController; import com.android.systemui.util.Assert; @@ -575,6 +575,7 @@ public class NotificationStackScrollLayout @Nullable private SplitShadeStateController mSplitShadeStateController = null; private boolean mIsSmallLandscapeLockscreenEnabled = false; private boolean mSuppressHeightUpdates; + private boolean mIsOnLockscreen; /** Pass splitShadeStateController to view and update split shade */ public void passSplitShadeStateController(SplitShadeStateController splitShadeStateController) { @@ -3228,9 +3229,12 @@ public class NotificationStackScrollLayout private void onViewAddedInternal(ExpandableView child) { updateHideSensitiveForChild(child); child.setOnHeightChangedListener(mOnChildHeightChangedListener); - if (child instanceof ExpandableNotificationRow) { + if (child instanceof ExpandableNotificationRow row) { NotificationEntry entry = ((ExpandableNotificationRow) child).getEntry(); entry.addOnSensitivityChangedListener(mOnChildSensitivityChangedListener); + if (SceneContainerFlag.isEnabled()) { + row.setOnKeyguard(mIsOnLockscreen); + } } generateAddAnimation(child, false /* fromMoreCard */); updateAnimationState(child); @@ -4752,8 +4756,11 @@ public class NotificationStackScrollLayout } } - void goToFullShade(long delay) { - SceneContainerFlag.assertInLegacyMode(); + /** + * Requests an animation for the next stack height update, to animate from the constrained stack + * displayed on the lock screen, to the scrollable stack displayed in the expanded shade. + */ + public void animateGoToFullShade(long delay) { mGoToFullShadeNeedsAnimation = true; mGoToFullShadeDelay = delay; mNeedsAnimation = true; @@ -5356,12 +5363,38 @@ public class NotificationStackScrollLayout shelf.bind(mAmbientState, this, mController.getNotificationRoundnessManager()); } + /** + * Whether the notifications are displayed over the unoccluded lockscreen. Returns false on the + * locked shade. + */ + public boolean isOnLockscreen() { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return false; + return mIsOnLockscreen; + } + + /** @see #isOnLockscreen() */ + public void setOnLockscreen(boolean isOnLockscreen) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; + if (mIsOnLockscreen != isOnLockscreen) { + mIsOnLockscreen = isOnLockscreen; + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof ExpandableNotificationRow childRow) { + childRow.setOnKeyguard(isOnLockscreen); + } + } + } + } + public void setMaxDisplayedNotifications(int maxDisplayedNotifications) { if (mMaxDisplayedNotifications != maxDisplayedNotifications) { mMaxDisplayedNotifications = maxDisplayedNotifications; if (SceneContainerFlag.isEnabled()) { updateIntrinsicStackHeight(); updateStackEndHeightAndStackHeight(mAmbientState.getExpansionFraction()); + if (maxDisplayedNotifications == -1) { + animateGoToFullShade(0); + } } else { updateContentHeight(); } 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 dc1a191ce233..e89645d7cb94 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 @@ -98,9 +98,9 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ColorUpdateLogger; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.HeadsUpNotificationViewControllerEmptyImpl; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper; -import com.android.systemui.statusbar.notification.HeadsUpTouchHelper.HeadsUpNotificationViewController; +import com.android.systemui.statusbar.notification.headsup.HeadsUpNotificationViewControllerEmptyImpl; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper.HeadsUpNotificationViewController; import com.android.systemui.statusbar.notification.LaunchAnimationParameters; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; @@ -138,8 +138,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.statusbar.policy.SplitShadeStateController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -1222,7 +1222,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { public void goToFullShade(long delay) { SceneContainerFlag.assertInLegacyMode(); - mView.goToFullShade(delay); + mView.animateGoToFullShade(delay); } public void setOverScrollAmount(float amount, boolean onTop, boolean animate, @@ -1603,6 +1603,12 @@ public class NotificationStackScrollLayoutController implements Dumpable { } } + /** Sets whether the NSSL is displayed over the unoccluded Lockscreen. */ + public void setOnLockscreen(boolean isOnLockscreen) { + if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; + mNotificationListContainer.setOnLockscreen(isOnLockscreen); + } + /** * Set the maximum number of notifications that can currently be displayed */ @@ -2029,6 +2035,11 @@ public class NotificationStackScrollLayoutController implements Dumpable { } @Override + public void setOnLockscreen(boolean isOnLockscreen) { + mView.setOnLockscreen(isOnLockscreen); + } + + @Override public void setMaxDisplayedNotifications(int maxNotifications) { mView.setMaxDisplayedNotifications(maxNotifications); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java index ef145578bbac..b2ffa4aa8233 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java @@ -33,7 +33,7 @@ import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.NotificationFadeAware.FadeOptimizedNotification; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.row.ExpandableView; -import com.android.systemui.statusbar.policy.HeadsUpUtil; +import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil; import java.io.PrintWriter; import java.lang.reflect.Field; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt index 4a55dfaaf7ea..ea714608ea66 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt @@ -118,8 +118,12 @@ constructor( } launch { - viewModel.getMaxNotifications(calculateMaxNotifications).collect { - controller.setMaxDisplayedNotifications(it) + viewModel.getLockscreenDisplayConfig(calculateMaxNotifications).collect { + (isOnLockscreen, maxNotifications) -> + if (SceneContainerFlag.isEnabled) { + controller.setOnLockscreen(isOnLockscreen) + } + controller.setMaxDisplayedNotifications(maxNotifications) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index fb60f266e188..a55a165d9b66 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -718,9 +718,11 @@ constructor( * When expanding or when the user is interacting with the shade, keep the count stable; do not * emit a value. */ - fun getMaxNotifications(calculateSpace: (Float, Boolean) -> Int): Flow<Int> { + fun getLockscreenDisplayConfig( + calculateSpace: (Float, Boolean) -> Int + ): Flow<LockscreenDisplayConfig> { val showLimitedNotifications = isOnLockscreenWithoutShade - val showUnlimitedNotifications = + val showUnlimitedNotificationsAndIsOnLockScreen = combine( isOnLockscreen, keyguardInteractor.statusBarState, @@ -730,28 +732,42 @@ constructor( ) .onStart { emit(false) }, ) { isOnLockscreen, statusBarState, showAllNotifications -> - statusBarState == SHADE_LOCKED || !isOnLockscreen || showAllNotifications + (statusBarState == SHADE_LOCKED || !isOnLockscreen || showAllNotifications) to + isOnLockscreen } + @Suppress("UNCHECKED_CAST") return combineTransform( showLimitedNotifications, - showUnlimitedNotifications, + showUnlimitedNotificationsAndIsOnLockScreen, shadeInteractor.isUserInteracting, availableHeight, interactor.notificationStackChanged, interactor.useExtraShelfSpace, ) { flows -> val showLimitedNotifications = flows[0] as Boolean - val showUnlimitedNotifications = flows[1] as Boolean + val (showUnlimitedNotifications, isOnLockscreen) = + flows[1] as Pair<Boolean, Boolean> val isUserInteracting = flows[2] as Boolean val availableHeight = flows[3] as Float val useExtraShelfSpace = flows[5] as Boolean if (!isUserInteracting) { if (showLimitedNotifications) { - emit(calculateSpace(availableHeight, useExtraShelfSpace)) + emit( + LockscreenDisplayConfig( + isOnLockscreen = isOnLockscreen, + maxNotifications = + calculateSpace(availableHeight, useExtraShelfSpace), + ) + ) } else if (showUnlimitedNotifications) { - emit(-1) + emit( + LockscreenDisplayConfig( + isOnLockscreen = isOnLockscreen, + maxNotifications = -1, + ) + ) } } } @@ -775,9 +791,9 @@ constructor( SceneContainerFlag.assertInLegacyMode() return combine( - getMaxNotifications(calculateMaxNotifications).map { - val height = calculateHeight(it) - if (it == 0) { + getLockscreenDisplayConfig(calculateMaxNotifications).map { (_, maxNotifications) -> + val height = calculateHeight(maxNotifications) + if (maxNotifications == 0) { height - shelfHeight } else { height @@ -815,4 +831,13 @@ constructor( */ data class FloatAtEnd(val width: Int) : HorizontalPosition } + + /** + * Data class representing a configuration for displaying Notifications on the Lockscreen. + * + * @param isOnLockscreen is the user on the lockscreen + * @param maxNotifications Limit for the max number of top-level Notifications to be displayed. + * A value of -1 indicates no limit. + */ + data class LockscreenDisplayConfig(val isOnLockscreen: Boolean, val maxNotifications: Int) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.kt new file mode 100644 index 000000000000..636e1c45bc81 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.kt @@ -0,0 +1,65 @@ +/* + * 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 + +import android.content.Context +import android.view.MotionEvent +import com.android.systemui.statusbar.AutoHideUiElement +import java.io.PrintWriter + +/** + * Controls the auto-hide behavior of system bars (status bar, navigation bar). + * + * This interface provides methods to manage the auto-hide schedule of system bars, allowing them to + * be shown or hidden. + */ +interface AutoHideController { + /** + * Sets a [AutoHideUiElement] status bar that should be controlled by the [AutoHideController]. + */ + fun setStatusBar(element: AutoHideUiElement) + + /** + * Sets a [AutoHideUiElement] navigation bar that should be controlled by the + * [AutoHideController]. + */ + fun setNavigationBar(element: AutoHideUiElement) + + /** Resumes the auto-hide behavior that was previously suspended. */ + fun resumeSuspendedAutoHide() + + /** Suspends the auto-hide behavior. */ + fun suspendAutoHide() + + /** Schedules or cancels auto hide behavior based on current system bar state. */ + fun touchAutoHide() + + /** Hides system bars on user touch if the interaction requires them to be hidden. */ + fun checkUserAutoHide(event: MotionEvent) + + /** Called when work should stop and resources should be released. */ + fun stop() + + /** Dumps the current state of the [AutoHideController] */ + fun dump(pw: PrintWriter) + + /** Injectable factory for creating a [AutoHideController]. */ + interface Factory { + /** Create an [AutoHideController] */ + fun create(context: Context): AutoHideController + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerImpl.java index 1358cfd3e8da..4fbfbb254064 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 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. @@ -28,7 +28,6 @@ import android.view.accessibility.AccessibilityManager; import androidx.annotation.NonNull; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.AutoHideUiElement; @@ -36,9 +35,7 @@ import java.io.PrintWriter; import javax.inject.Inject; -/** A controller to control all auto-hide things. Also see {@link AutoHideUiElement}. */ -@SysUISingleton -public class AutoHideController { +public class AutoHideControllerImpl implements AutoHideController { private static final String TAG = "AutoHideController"; private static final int AUTO_HIDE_TIMEOUT_MS = 2250; private static final int USER_AUTO_HIDE_TIMEOUT_MS = 350; @@ -61,7 +58,7 @@ public class AutoHideController { }; @Inject - public AutoHideController(Context context, + public AutoHideControllerImpl(Context context, @Main Handler handler, IWindowManager iWindowManager) { mAccessibilityManager = context.getSystemService(AccessibilityManager.class); @@ -70,18 +67,12 @@ public class AutoHideController { mDisplayId = context.getDisplayId(); } - /** - * Sets a {@link AutoHideUiElement} status bar that should be controlled by the - * {@link AutoHideController}. - */ + @Override public void setStatusBar(AutoHideUiElement element) { mStatusBar = element; } - /** - * Sets a {@link AutoHideUiElement} navigation bar that should be controlled by the - * {@link AutoHideController}. - */ + @Override public void setNavigationBar(AutoHideUiElement element) { mNavigationBar = element; } @@ -102,6 +93,7 @@ public class AutoHideController { } } + @Override public void resumeSuspendedAutoHide() { if (mAutoHideSuspended) { scheduleAutoHide(); @@ -112,6 +104,7 @@ public class AutoHideController { } } + @Override public void suspendAutoHide() { mHandler.removeCallbacks(mAutoHide); Runnable checkBarModesRunnable = getCheckBarModesRunnable(); @@ -121,7 +114,7 @@ public class AutoHideController { mAutoHideSuspended = isAnyTransientBarShown(); } - /** Schedules or cancels auto hide behavior based on current system bar state. */ + @Override public void touchAutoHide() { // update transient bar auto hide if (isAnyTransientBarShown()) { @@ -156,6 +149,7 @@ public class AutoHideController { FLAG_CONTENT_CONTROLS); } + @Override public void checkUserAutoHide(MotionEvent event) { boolean shouldHide = isAnyTransientBarShown() && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar. @@ -196,6 +190,12 @@ public class AutoHideController { return false; } + @Override + public void stop() { + mHandler.removeCallbacks(mAutoHide); + } + + @Override public void dump(@NonNull PrintWriter pw) { pw.println("AutoHideController:"); pw.println("\tmAutoHideSuspended=" + mAutoHideSuspended); @@ -205,10 +205,7 @@ public class AutoHideController { pw.println("\tgetUserAutoHideTimeout=" + getUserAutoHideTimeout()); } - /** - * Injectable factory for creating a {@link AutoHideController}. - */ - public static class Factory { + public static class Factory implements AutoHideController.Factory { private final Handler mHandler; private final IWindowManager mIWindowManager; @@ -219,8 +216,9 @@ public class AutoHideController { } /** Create an {@link AutoHideController} */ + @Override public AutoHideController create(Context context) { - return new AutoHideController(context, mHandler, mIWindowManager); + return new AutoHideControllerImpl(context, mHandler, mIWindowManager); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt new file mode 100644 index 000000000000..744f96918eef --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.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.statusbar.phone + +import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.display.data.repository.DisplayRepository +import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository +import com.android.systemui.display.data.repository.PerDisplayStore +import com.android.systemui.display.data.repository.PerDisplayStoreImpl +import com.android.systemui.display.data.repository.SingleDisplayStore +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope + +/** Provides per display instances of [AutoHideController] */ +interface AutoHideControllerStore : PerDisplayStore<AutoHideController> + +@SysUISingleton +class MultiDisplayAutoHideControllerStore +@Inject +constructor( + @Background backgroundApplicationScope: CoroutineScope, + displayRepository: DisplayRepository, + private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository, + private val autoHideControllerFactory: AutoHideControllerImpl.Factory, +) : + AutoHideControllerStore, + PerDisplayStoreImpl<AutoHideController>(backgroundApplicationScope, displayRepository) { + + init { + StatusBarConnectedDisplays.assertInNewMode() + } + + override fun createInstanceForDisplay(displayId: Int): AutoHideController { + val displayWindowProperties = + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) + return autoHideControllerFactory.create(displayWindowProperties.context) + } + + override suspend fun onDisplayRemovalAction(instance: AutoHideController) { + instance.stop() + } + + override val instanceClass = AutoHideController::class.java +} + +@SysUISingleton +class SingleDisplayAutoHideControllerStore +@Inject +constructor(defaultController: AutoHideController) : + AutoHideControllerStore, + PerDisplayStore<AutoHideController> by SingleDisplayStore(defaultController) { + + init { + StatusBarConnectedDisplays.assertInLegacyMode() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java index 5209d0f1551e..7f95fb072ede 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java @@ -66,7 +66,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; 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 db294934c9ed..7fa6707eb99d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -221,7 +221,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController.Configurati import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.ExtensionController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java index ec92990441ce..57e26d708f26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -54,8 +54,8 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.util.Assert; import com.android.systemui.util.CopyOnLoopListenerSet; import com.android.systemui.util.IListenerSet; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 8de03d83d7af..b9639a7a90f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -47,9 +47,9 @@ import com.android.systemui.statusbar.notification.stack.NotificationRoundnessMa import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarScope; import com.android.systemui.statusbar.policy.Clock; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.util.ViewController; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt index a6374a66806b..cd9b9d244e0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone +import android.content.Context import android.view.WindowInsetsController import com.android.internal.colorextraction.ColorExtractor import com.android.internal.view.AppearanceRegion @@ -64,4 +65,8 @@ interface LightBarController : CoreStartable { scrimBehindAlpha: Float, scrimInFrontColor: ColorExtractor.GradientColors, ) + + fun interface Factory { + fun create(context: Context): LightBarController + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java index ccb9a119d92f..1a4f3ca5b07f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java @@ -22,6 +22,7 @@ import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT; +import android.content.Context; import android.graphics.Rect; import android.util.Log; import android.view.Display; @@ -34,12 +35,15 @@ import androidx.annotation.Nullable; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.internal.view.AppearanceRegion; +import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.data.model.StatusBarAppearance; +import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore; import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository; +import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.Compile; import com.android.systemui.util.kotlin.JavaAdapterKt; @@ -55,6 +59,8 @@ import kotlinx.coroutines.CoroutineScope; import java.io.PrintWriter; import java.util.ArrayList; +import javax.inject.Inject; + /** * Controls how light status bar flag applies to the icons. */ @@ -67,6 +73,7 @@ public class LightBarControllerImpl implements private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f; + private final int mDisplayId; private final CoroutineScope mCoroutineScope; private final SysuiDarkIconDispatcher mStatusBarIconController; private final BatteryController mBatteryController; @@ -140,6 +147,7 @@ public class LightBarControllerImpl implements DumpManager dumpManager, @Main CoroutineContext mainContext, BiometricUnlockController biometricUnlockController) { + mDisplayId = displayId; mCoroutineScope = coroutineScope; mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher; mBatteryController = batteryController; @@ -155,7 +163,12 @@ public class LightBarControllerImpl implements @Override public void start() { - mDumpManager.registerCriticalDumpable(mDumpableName, this); + if (mDisplayId == Display.DEFAULT_DISPLAY) { + // Can only register on default display, because NavigationBar creates its own instance + // as well as PerDisplayStore. + // TODO: b/380394368 - make sure there is only one instance per display. + mDumpManager.registerCriticalDumpable(mDumpableName, this); + } mBatteryController.addCallback(this); mNavigationMode = mNavModeController.addListener(mNavigationModeListener); JavaAdapterKt.collectFlow( @@ -490,4 +503,36 @@ public class LightBarControllerImpl implements DarkIconDispatcher darkIconDispatcher, StatusBarModePerDisplayRepository statusBarModePerDisplayRepository); } + + public static class LegacyFactory implements LightBarController.Factory { + + private final Factory mFactory; + private final CoroutineScope mApplicationScope; + private final DarkIconDispatcherStore mDarkIconDispatcherStore; + private final StatusBarModeRepositoryStore mStatusBarModeRepositoryStore; + + @Inject + public LegacyFactory( + LightBarControllerImpl.Factory factory, + @Application CoroutineScope applicationScope, + DarkIconDispatcherStore darkIconDispatcherStore, + StatusBarModeRepositoryStore statusBarModeRepositoryStore) { + mFactory = factory; + mApplicationScope = applicationScope; + mDarkIconDispatcherStore = darkIconDispatcherStore; + mStatusBarModeRepositoryStore = statusBarModeRepositoryStore; + } + + @NonNull + @Override + public LightBarController create(@NonNull Context context) { + // TODO: b/380394368 - Make sure correct per display instances are used. + return mFactory.create( + context.getDisplayId(), + mApplicationScope, + mDarkIconDispatcherStore.getDefaultDisplay(), + mStatusBarModeRepositoryStore.getDefaultDisplay() + ); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index af98311c937f..e33baf7c33ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -79,8 +79,8 @@ import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyS import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.HeadsUpUtil; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.wmshell.BubblesManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 79ea59c27090..b3cc047251ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -69,7 +69,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import java.util.Set; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java index d2c2003112f6..c09b9c52bb53 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java @@ -47,8 +47,8 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.util.kotlin.JavaAdapter; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt index 58386b0cad7c..42b9d5b12afc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt @@ -35,7 +35,12 @@ import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerSt import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStoreModule import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.events.PrivacyDotViewControllerModule +import com.android.systemui.statusbar.phone.AutoHideController +import com.android.systemui.statusbar.phone.AutoHideControllerImpl +import com.android.systemui.statusbar.phone.AutoHideControllerStore import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks +import com.android.systemui.statusbar.phone.MultiDisplayAutoHideControllerStore +import com.android.systemui.statusbar.phone.SingleDisplayAutoHideControllerStore import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStoreImpl @@ -75,6 +80,9 @@ interface StatusBarPhoneModule { @Binds fun statusBarInitializer(@Default impl: StatusBarInitializerImpl): StatusBarInitializer + @Binds + fun autoHideControllerFactory(impl: AutoHideControllerImpl.Factory): AutoHideController.Factory + companion object { /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */ @Provides @@ -186,5 +194,32 @@ interface StatusBarPhoneModule { singleDisplayStoreLazy.get() } } + + @Provides + @SysUISingleton + fun autoHideStore( + singleDisplayLazy: Lazy<SingleDisplayAutoHideControllerStore>, + multiDisplayLazy: Lazy<MultiDisplayAutoHideControllerStore>, + ): AutoHideControllerStore { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayLazy.get() + } else { + singleDisplayLazy.get() + } + } + + @Provides + @SysUISingleton + @IntoMap + @ClassKey(AutoHideControllerStore::class) + fun storeAsCoreStartable( + multiDisplayLazy: Lazy<MultiDisplayAutoHideControllerStore> + ): CoreStartable { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayLazy.get() + } else { + CoreStartable.NOP + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java index a115baad257d..52f80fbf50fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java @@ -24,6 +24,7 @@ import android.media.MediaRouter; import android.media.MediaRouter.RouteInfo; import android.media.projection.MediaProjectionInfo; import android.media.projection.MediaProjectionManager; +import android.media.projection.StopReason; import android.os.Handler; import android.util.ArrayMap; @@ -190,7 +191,7 @@ public class CastControllerImpl implements CastController { if (isProjection) { final MediaProjectionInfo projection = (MediaProjectionInfo) device.getTag(); if (Objects.equals(mProjectionManager.getActiveProjectionInfo(), projection)) { - mProjectionManager.stopActiveProjection(); + mProjectionManager.stopActiveProjection(StopReason.STOP_QS_TILE); } else { mLogger.logStopCastingNoProjection(projection); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt index 616992eb0865..56c9e9abbc36 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt @@ -41,8 +41,8 @@ import android.view.ViewGroup import android.view.accessibility.AccessibilityNodeInfo import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction import android.widget.Button -import com.android.systemui.res.R import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import com.android.systemui.shared.system.ActivityManagerWrapper import com.android.systemui.shared.system.DevicePolicyManagerWrapper import com.android.systemui.shared.system.PackageManagerWrapper @@ -50,6 +50,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.NotificationUiAdjustment import com.android.systemui.statusbar.SmartReplyController import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.logging.NotificationLogger import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.policy.InflatedSmartReplyState.SuppressedActions @@ -63,40 +64,42 @@ import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlin.system.measureTimeMillis - /** Returns whether we should show the smart reply view and its smart suggestions. */ fun shouldShowSmartReplyView( entry: NotificationEntry, - smartReplyState: InflatedSmartReplyState + smartReplyState: InflatedSmartReplyState, ): Boolean { - if (smartReplyState.smartReplies == null && - smartReplyState.smartActions == null) { + if (smartReplyState.smartReplies == null && smartReplyState.smartActions == null) { // There are no smart replies and no smart actions. return false } // If we are showing the spinner we don't want to add the buttons. - val showingSpinner = entry.sbn.notification.extras - .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false) + val showingSpinner = + entry.sbn.notification.extras.getBoolean( + Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, + false, + ) if (showingSpinner) { return false } // If we are keeping the notification around while sending we don't want to add the buttons. - return !entry.sbn.notification.extras - .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false) + return !entry.sbn.notification.extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false) } /** Determines if two [InflatedSmartReplyState] are visually similar. */ fun areSuggestionsSimilar( left: InflatedSmartReplyState?, - right: InflatedSmartReplyState? -): Boolean = when { - left === right -> true - left == null || right == null -> false - left.hasPhishingAction != right.hasPhishingAction -> false - left.smartRepliesList != right.smartRepliesList -> false - left.suppressedActionIndices != right.suppressedActionIndices -> false - else -> !NotificationUiAdjustment.areDifferent(left.smartActionsList, right.smartActionsList) -} + right: InflatedSmartReplyState?, +): Boolean = + when { + left === right -> true + left == null || right == null -> false + left.hasPhishingAction != right.hasPhishingAction -> false + left.smartRepliesList != right.smartRepliesList -> false + left.suppressedActionIndices != right.suppressedActionIndices -> false + else -> + !NotificationUiAdjustment.areDifferent(left.smartActionsList, right.smartActionsList) + } interface SmartReplyStateInflater { fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState @@ -106,181 +109,211 @@ interface SmartReplyStateInflater { notifPackageContext: Context, entry: NotificationEntry, existingSmartReplyState: InflatedSmartReplyState?, - newSmartReplyState: InflatedSmartReplyState + newSmartReplyState: InflatedSmartReplyState, ): InflatedSmartReplyViewHolder } -/*internal*/ class SmartReplyStateInflaterImpl @Inject constructor( +/*internal*/ class SmartReplyStateInflaterImpl +@Inject +constructor( private val constants: SmartReplyConstants, private val activityManagerWrapper: ActivityManagerWrapper, private val packageManagerWrapper: PackageManagerWrapper, private val devicePolicyManagerWrapper: DevicePolicyManagerWrapper, private val smartRepliesInflater: SmartReplyInflater, - private val smartActionsInflater: SmartActionInflater + private val smartActionsInflater: SmartActionInflater, ) : SmartReplyStateInflater { override fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState = - chooseSmartRepliesAndActions(entry) + chooseSmartRepliesAndActions(entry) override fun inflateSmartReplyViewHolder( sysuiContext: Context, notifPackageContext: Context, entry: NotificationEntry, existingSmartReplyState: InflatedSmartReplyState?, - newSmartReplyState: InflatedSmartReplyState + newSmartReplyState: InflatedSmartReplyState, ): InflatedSmartReplyViewHolder { if (!shouldShowSmartReplyView(entry, newSmartReplyState)) { return InflatedSmartReplyViewHolder( - null /* smartReplyView */, - null /* smartSuggestionButtons */) + null /* smartReplyView */, + null, /* smartSuggestionButtons */ + ) } // Only block clicks if the smart buttons are different from the previous set - to avoid // scenarios where a user incorrectly cannot click smart buttons because the // notification is updated. val delayOnClickListener = - !areSuggestionsSimilar(existingSmartReplyState, newSmartReplyState) + !areSuggestionsSimilar(existingSmartReplyState, newSmartReplyState) val smartReplyView = SmartReplyView.inflate(sysuiContext, constants) val smartReplies = newSmartReplyState.smartReplies smartReplyView.setSmartRepliesGeneratedByAssistant(smartReplies?.fromAssistant ?: false) - val smartReplyButtons = smartReplies?.let { - smartReplies.choices.asSequence().mapIndexed { index, choice -> - smartRepliesInflater.inflateReplyButton( + val smartReplyButtons = + smartReplies?.let { + smartReplies.choices.asSequence().mapIndexed { index, choice -> + smartRepliesInflater.inflateReplyButton( smartReplyView, entry, smartReplies, index, choice, - delayOnClickListener) - } - } ?: emptySequence() + delayOnClickListener, + ) + } + } ?: emptySequence() - val smartActionButtons = newSmartReplyState.smartActions?.let { smartActions -> - val themedPackageContext = + val smartActionButtons = + newSmartReplyState.smartActions?.let { smartActions -> + val themedPackageContext = ContextThemeWrapper(notifPackageContext, sysuiContext.theme) - smartActions.actions.asSequence() + smartActions.actions + .asSequence() .filter { it.actionIntent != null } .mapIndexed { index, action -> smartActionsInflater.inflateActionButton( - smartReplyView, - entry, - smartActions, - index, - action, - delayOnClickListener, - themedPackageContext) + smartReplyView, + entry, + smartActions, + index, + action, + delayOnClickListener, + themedPackageContext, + ) } - } ?: emptySequence() + } ?: emptySequence() return InflatedSmartReplyViewHolder( - smartReplyView, - (smartReplyButtons + smartActionButtons).toList()) + smartReplyView, + (smartReplyButtons + smartActionButtons).toList(), + ) } /** * Chose what smart replies and smart actions to display. App generated suggestions take - * precedence. So if the app provides any smart replies, we don't show any - * replies or actions generated by the NotificationAssistantService (NAS), and if the app - * provides any smart actions we also don't show any NAS-generated replies or actions. + * precedence. So if the app provides any smart replies, we don't show any replies or actions + * generated by the NotificationAssistantService (NAS), and if the app provides any smart + * actions we also don't show any NAS-generated replies or actions. */ fun chooseSmartRepliesAndActions(entry: NotificationEntry): InflatedSmartReplyState { val notification = entry.sbn.notification val remoteInputActionPair = notification.findRemoteInputActionPair(false /* freeform */) val freeformRemoteInputActionPair = - notification.findRemoteInputActionPair(true /* freeform */) + notification.findRemoteInputActionPair(true /* freeform */) if (!constants.isEnabled) { if (DEBUG) { - Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for " + - entry.sbn.key) + Log.d( + TAG, + "Smart suggestions not enabled, not adding suggestions for " + entry.sbn.key, + ) } return InflatedSmartReplyState(null, null, null, false) } // Only use smart replies from the app if they target P or above. We have this check because // the smart reply API has been used for other things (Wearables) in the past. The API to // add smart actions is new in Q so it doesn't require a target-sdk check. - val enableAppGeneratedSmartReplies = (!constants.requiresTargetingP() || - entry.targetSdk >= Build.VERSION_CODES.P) + val enableAppGeneratedSmartReplies = + (!constants.requiresTargetingP() || entry.targetSdk >= Build.VERSION_CODES.P) val appGeneratedSmartActions = notification.contextualActions - var smartReplies: SmartReplies? = when { - enableAppGeneratedSmartReplies -> remoteInputActionPair?.let { pair -> - pair.second.actionIntent?.let { actionIntent -> - if (pair.first.choices?.isNotEmpty() == true) - SmartReplies( - pair.first.choices.asList(), - pair.first, - actionIntent, - false /* fromAssistant */) - else null - } + var smartReplies: SmartReplies? = + when { + enableAppGeneratedSmartReplies -> + remoteInputActionPair?.let { pair -> + pair.second.actionIntent?.let { actionIntent -> + if (pair.first.choices?.isNotEmpty() == true) + SmartReplies( + pair.first.choices.asList(), + pair.first, + actionIntent, + false, /* fromAssistant */ + ) + else null + } + } + else -> null + } + var smartActions: SmartActions? = + when { + appGeneratedSmartActions.isNotEmpty() -> + SmartActions(appGeneratedSmartActions, false /* fromAssistant */) + else -> null } - else -> null - } - var smartActions: SmartActions? = when { - appGeneratedSmartActions.isNotEmpty() -> - SmartActions(appGeneratedSmartActions, false /* fromAssistant */) - else -> null - } // Apps didn't provide any smart replies / actions, use those from NAS (if any). if (smartReplies == null && smartActions == null) { val entryReplies = entry.smartReplies val entryActions = entry.smartActions - if (entryReplies.isNotEmpty() && + if ( + entryReplies.isNotEmpty() && freeformRemoteInputActionPair != null && freeformRemoteInputActionPair.second.allowGeneratedReplies && - freeformRemoteInputActionPair.second.actionIntent != null) { - smartReplies = SmartReplies( + freeformRemoteInputActionPair.second.actionIntent != null + ) { + smartReplies = + SmartReplies( entryReplies, freeformRemoteInputActionPair.first, freeformRemoteInputActionPair.second.actionIntent, - true /* fromAssistant */) + true, /* fromAssistant */ + ) } - if (entryActions.isNotEmpty() && - notification.allowSystemGeneratedContextualActions) { - val systemGeneratedActions: List<Notification.Action> = when { - activityManagerWrapper.isLockTaskKioskModeActive -> - // Filter actions if we're in kiosk-mode - we don't care about screen - // pinning mode, since notifications aren't shown there anyway. - filterAllowlistedLockTaskApps(entryActions) - else -> entryActions - } + if (entryActions.isNotEmpty() && notification.allowSystemGeneratedContextualActions) { + val systemGeneratedActions: List<Notification.Action> = + when { + activityManagerWrapper.isLockTaskKioskModeActive -> + // Filter actions if we're in kiosk-mode - we don't care about screen + // pinning mode, since notifications aren't shown there anyway. + filterAllowlistedLockTaskApps(entryActions) + else -> entryActions + } smartActions = SmartActions(systemGeneratedActions, true /* fromAssistant */) } } - val hasPhishingAction = smartActions?.actions?.any { - it.isContextual && it.semanticAction == - Notification.Action.SEMANTIC_ACTION_CONVERSATION_IS_PHISHING - } ?: false + val hasPhishingAction = + smartActions?.actions?.any { + it.isContextual && + it.semanticAction == + Notification.Action.SEMANTIC_ACTION_CONVERSATION_IS_PHISHING + } ?: false var suppressedActions: SuppressedActions? = null if (hasPhishingAction) { // If there is a phishing action, calculate the indices of the actions with RemoteInput // as those need to be hidden from the view. - val suppressedActionIndices = notification.actions.mapIndexedNotNull { index, action -> - if (action.remoteInputs?.isNotEmpty() == true) index else null - } + val suppressedActionIndices = + notification.actions.mapIndexedNotNull { index, action -> + if (action.remoteInputs?.isNotEmpty() == true) index else null + } suppressedActions = SuppressedActions(suppressedActionIndices) } - return InflatedSmartReplyState(smartReplies, smartActions, suppressedActions, - hasPhishingAction) + return InflatedSmartReplyState( + smartReplies, + smartActions, + suppressedActions, + hasPhishingAction, + ) } /** - * Filter actions so that only actions pointing to allowlisted apps are permitted. - * This filtering is only meaningful when in lock-task mode. + * Filter actions so that only actions pointing to allowlisted apps are permitted. This + * filtering is only meaningful when in lock-task mode. */ private fun filterAllowlistedLockTaskApps( actions: List<Notification.Action> - ): List<Notification.Action> = actions.filter { action -> - // Only allow actions that are explicit (implicit intents are not handled in lock-task - // mode), and link to allowlisted apps. - action.actionIntent?.intent?.let { intent -> - packageManagerWrapper.resolveActivity(intent, 0 /* flags */) - }?.let { resolveInfo -> - devicePolicyManagerWrapper.isLockTaskPermitted(resolveInfo.activityInfo.packageName) - } ?: false - } + ): List<Notification.Action> = + actions.filter { action -> + // Only allow actions that are explicit (implicit intents are not handled in lock-task + // mode), and link to allowlisted apps. + action.actionIntent + ?.intent + ?.let { intent -> packageManagerWrapper.resolveActivity(intent, 0 /* flags */) } + ?.let { resolveInfo -> + devicePolicyManagerWrapper.isLockTaskPermitted( + resolveInfo.activityInfo.packageName + ) + } ?: false + } } interface SmartActionInflater { @@ -291,7 +324,7 @@ interface SmartActionInflater { actionIndex: Int, action: Notification.Action, delayOnClickListener: Boolean, - packageContext: Context + packageContext: Context, ): Button } @@ -310,28 +343,32 @@ private fun loadIconDrawableWithTimeout( val bitmap: Bitmap? val durationMillis = measureTimeMillis { val source = ImageDecoder.createSource(packageContext.contentResolver, icon.uri) - bitmap = ImageDecoder.decodeBitmap(source) { decoder, _, _ -> - decoder.setTargetSize(targetSize, targetSize) - decoder.allocator = ImageDecoder.ALLOCATOR_DEFAULT - } + bitmap = + ImageDecoder.decodeBitmap(source) { decoder, _, _ -> + decoder.setTargetSize(targetSize, targetSize) + decoder.allocator = ImageDecoder.ALLOCATOR_DEFAULT + } } if (durationMillis > ICON_TASK_TIMEOUT_MS) { Log.w(TAG, "Loading $icon took ${durationMillis / 1000f} sec") } checkNotNull(bitmap) { "ImageDecoder.decodeBitmap() returned null" } } - val bitmap = runCatching { - iconTaskThreadPool.execute(bitmapTask) - bitmapTask.get(ICON_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS) - }.getOrElse { ex -> - Log.e(TAG, "Failed to load $icon: $ex") - bitmapTask.cancel(true) - return null - } + val bitmap = + runCatching { + iconTaskThreadPool.execute(bitmapTask) + bitmapTask.get(ICON_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS) + } + .getOrElse { ex -> + Log.e(TAG, "Failed to load $icon: $ex") + bitmapTask.cancel(true) + return null + } // TODO(b/288561520): rewrite Icon so that we don't need to duplicate this logic val bitmapDrawable = BitmapDrawable(packageContext.resources, bitmap) - val result = if (icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP) - AdaptiveIconDrawable(null, bitmapDrawable) else bitmapDrawable + val result = + if (icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP) AdaptiveIconDrawable(null, bitmapDrawable) + else bitmapDrawable if (icon.hasTint()) { result.mutate() result.setTintList(icon.tintList) @@ -340,11 +377,13 @@ private fun loadIconDrawableWithTimeout( return result } -/* internal */ class SmartActionInflaterImpl @Inject constructor( +/* internal */ class SmartActionInflaterImpl +@Inject +constructor( private val constants: SmartReplyConstants, private val activityStarter: ActivityStarter, private val smartReplyController: SmartReplyController, - private val headsUpManager: HeadsUpManager + private val headsUpManager: HeadsUpManager, ) : SmartActionInflater { override fun inflateActionButton( @@ -354,17 +393,18 @@ private fun loadIconDrawableWithTimeout( actionIndex: Int, action: Notification.Action, delayOnClickListener: Boolean, - packageContext: Context + packageContext: Context, ): Button = - (LayoutInflater.from(parent.context) - .inflate(R.layout.smart_action_button, parent, false) as Button - ).apply { + (LayoutInflater.from(parent.context).inflate(R.layout.smart_action_button, parent, false) + as Button) + .apply { text = action.title - // We received the Icon from the application - so use the Context of the application to + // We received the Icon from the application - so use the Context of the application + // to // reference icon resources. - val newIconSize = context.resources - .getDimensionPixelSize(R.dimen.smart_action_button_icon_size) + val newIconSize = + context.resources.getDimensionPixelSize(R.dimen.smart_action_button_icon_size) val iconDrawable = loadIconDrawableWithTimeout(action.getIcon(), packageContext, newIconSize) ?: GradientDrawable() @@ -372,13 +412,15 @@ private fun loadIconDrawableWithTimeout( // Add the action icon to the Smart Action button. setCompoundDrawablesRelative(iconDrawable, null, null, null) - val onClickListener = View.OnClickListener { - onSmartActionClick(entry, smartActions, actionIndex, action) - } + val onClickListener = + View.OnClickListener { + onSmartActionClick(entry, smartActions, actionIndex, action) + } setOnClickListener( - if (delayOnClickListener) - DelayedOnClickListener(onClickListener, constants.onClickInitDelay) - else onClickListener) + if (delayOnClickListener) + DelayedOnClickListener(onClickListener, constants.onClickInitDelay) + else onClickListener + ) // Mark this as an Action button (layoutParams as SmartReplyView.LayoutParams).mButtonType = SmartButtonType.ACTION @@ -388,18 +430,31 @@ private fun loadIconDrawableWithTimeout( entry: NotificationEntry, smartActions: SmartActions, actionIndex: Int, - action: Notification.Action + action: Notification.Action, ) = - if (smartActions.fromAssistant && - SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY == action.semanticAction) { - entry.row.doSmartActionClick(entry.row.x.toInt() / 2, - entry.row.y.toInt() / 2, SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY) - smartReplyController - .smartActionClicked(entry, actionIndex, action, smartActions.fromAssistant) + if ( + smartActions.fromAssistant && + SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY == action.semanticAction + ) { + entry.row.doSmartActionClick( + entry.row.x.toInt() / 2, + entry.row.y.toInt() / 2, + SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY, + ) + smartReplyController.smartActionClicked( + entry, + actionIndex, + action, + smartActions.fromAssistant, + ) } else { activityStarter.startPendingIntentDismissingKeyguard(action.actionIntent, entry.row) { - smartReplyController - .smartActionClicked(entry, actionIndex, action, smartActions.fromAssistant) + smartReplyController.smartActionClicked( + entry, + actionIndex, + action, + smartActions.fromAssistant, + ) } } } @@ -411,16 +466,18 @@ interface SmartReplyInflater { smartReplies: SmartReplies, replyIndex: Int, choice: CharSequence, - delayOnClickListener: Boolean + delayOnClickListener: Boolean, ): Button } -class SmartReplyInflaterImpl @Inject constructor( +class SmartReplyInflaterImpl +@Inject +constructor( private val constants: SmartReplyConstants, private val keyguardDismissUtil: KeyguardDismissUtil, private val remoteInputManager: NotificationRemoteInputManager, private val smartReplyController: SmartReplyController, - private val context: Context + private val context: Context, ) : SmartReplyInflater { override fun inflateReplyButton( @@ -429,37 +486,35 @@ class SmartReplyInflaterImpl @Inject constructor( smartReplies: SmartReplies, replyIndex: Int, choice: CharSequence, - delayOnClickListener: Boolean + delayOnClickListener: Boolean, ): Button = - (LayoutInflater.from(parent.context) - .inflate(R.layout.smart_reply_button, parent, false) as Button - ).apply { + (LayoutInflater.from(parent.context).inflate(R.layout.smart_reply_button, parent, false) + as Button) + .apply { text = choice - val onClickListener = View.OnClickListener { - onSmartReplyClick( - entry, - smartReplies, - replyIndex, - parent, - this, - choice) - } + val onClickListener = + View.OnClickListener { + onSmartReplyClick(entry, smartReplies, replyIndex, parent, this, choice) + } setOnClickListener( - if (delayOnClickListener) - DelayedOnClickListener(onClickListener, constants.onClickInitDelay) - else onClickListener) - accessibilityDelegate = object : View.AccessibilityDelegate() { - override fun onInitializeAccessibilityNodeInfo( - host: View, - info: AccessibilityNodeInfo - ) { - super.onInitializeAccessibilityNodeInfo(host, info) - val label = parent.resources - .getString(R.string.accessibility_send_smart_reply) - val action = AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, label) - info.addAction(action) + if (delayOnClickListener) + DelayedOnClickListener(onClickListener, constants.onClickInitDelay) + else onClickListener + ) + accessibilityDelegate = + object : View.AccessibilityDelegate() { + override fun onInitializeAccessibilityNodeInfo( + host: View, + info: AccessibilityNodeInfo, + ) { + super.onInitializeAccessibilityNodeInfo(host, info) + val label = + parent.resources.getString(R.string.accessibility_send_smart_reply) + val action = + AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, label) + info.addAction(action) + } } - } // TODO: probably shouldn't do this here, bad API // Mark this as a Reply button (layoutParams as SmartReplyView.LayoutParams).mButtonType = SmartButtonType.REPLY @@ -471,39 +526,52 @@ class SmartReplyInflaterImpl @Inject constructor( replyIndex: Int, smartReplyView: SmartReplyView, button: Button, - choice: CharSequence - ) = keyguardDismissUtil.executeWhenUnlocked(!entry.isRowPinned) { - val canEditBeforeSend = constants.getEffectiveEditChoicesBeforeSending( - smartReplies.remoteInput.editChoicesBeforeSending) - if (canEditBeforeSend) { - remoteInputManager.activateRemoteInput( + choice: CharSequence, + ) = + keyguardDismissUtil.executeWhenUnlocked(!entry.isRowPinned) { + val canEditBeforeSend = + constants.getEffectiveEditChoicesBeforeSending( + smartReplies.remoteInput.editChoicesBeforeSending + ) + if (canEditBeforeSend) { + remoteInputManager.activateRemoteInput( button, arrayOf(smartReplies.remoteInput), smartReplies.remoteInput, smartReplies.pendingIntent, - NotificationEntry.EditedSuggestionInfo(choice, replyIndex)) - } else { - smartReplyController.smartReplySent( + NotificationEntry.EditedSuggestionInfo(choice, replyIndex), + ) + } else { + smartReplyController.smartReplySent( entry, replyIndex, button.text, NotificationLogger.getNotificationLocation(entry).toMetricsEventEnum(), - false /* modifiedBeforeSending */) - entry.setHasSentReply() - try { - val intent = createRemoteInputIntent(smartReplies, choice) - val opts = ActivityOptions.makeBasic() - opts.setPendingIntentBackgroundActivityStartMode( - ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) - smartReplies.pendingIntent.send(context, 0, intent, /* onFinished */null, - /* handler */ null, /* requiredPermission */ null, opts.toBundle()) - } catch (e: PendingIntent.CanceledException) { - Log.w(TAG, "Unable to send smart reply", e) + false, /* modifiedBeforeSending */ + ) + entry.setHasSentReply() + try { + val intent = createRemoteInputIntent(smartReplies, choice) + val opts = ActivityOptions.makeBasic() + opts.setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED + ) + smartReplies.pendingIntent.send( + context, + 0, + intent, /* onFinished */ + null, + /* handler */ null, /* requiredPermission */ + null, + opts.toBundle(), + ) + } catch (e: PendingIntent.CanceledException) { + Log.w(TAG, "Unable to send smart reply", e) + } + smartReplyView.hideSmartSuggestions() } - smartReplyView.hideSmartSuggestions() + false // do not defer } - false // do not defer - } private fun createRemoteInputIntent(smartReplies: SmartReplies, choice: CharSequence): Intent { val results = Bundle() @@ -516,12 +584,11 @@ class SmartReplyInflaterImpl @Inject constructor( } /** - * An OnClickListener wrapper that blocks the underlying OnClickListener for a given amount of - * time. + * An OnClickListener wrapper that blocks the underlying OnClickListener for a given amount of time. */ private class DelayedOnClickListener( private val mActualListener: View.OnClickListener, - private val mInitDelayMs: Long + private val mInitDelayMs: Long, ) : View.OnClickListener { private val mInitTimeMs = SystemClock.elapsedRealtime() @@ -535,7 +602,7 @@ private class DelayedOnClickListener( } private fun hasFinishedInitialization(): Boolean = - SystemClock.elapsedRealtime() >= mInitTimeMs + mInitDelayMs + SystemClock.elapsedRealtime() >= mInitTimeMs + mInitDelayMs } private const val TAG = "SmartReplyViewInflater" @@ -544,12 +611,12 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) // convenience function that swaps parameter order so that lambda can be placed at the end private fun KeyguardDismissUtil.executeWhenUnlocked( requiresShadeOpen: Boolean, - onDismissAction: () -> Boolean + onDismissAction: () -> Boolean, ) = executeWhenUnlocked(onDismissAction, requiresShadeOpen, false) // convenience function that swaps parameter order so that lambda can be placed at the end private fun ActivityStarter.startPendingIntentDismissingKeyguard( intent: PendingIntent, associatedView: View?, - runnable: () -> Unit -) = startPendingIntentDismissingKeyguard(intent, runnable::invoke, associatedView)
\ No newline at end of file + runnable: () -> Unit, +) = startPendingIntentDismissingKeyguard(intent, runnable::invoke, associatedView) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java index bf13ceb5666a..4bc5f2303ee8 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java @@ -45,6 +45,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.provider.Settings; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; @@ -56,6 +58,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.logging.CarrierTextManagerLogger; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.kosmos.KosmosJavaAdapter; @@ -200,6 +203,7 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testAirplaneMode() { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); reset(mCarrierTextCallback); @@ -220,8 +224,32 @@ public class CarrierTextManagerTest extends SysuiTestCase { assertEquals(AIRPLANE_MODE_TEXT, captor.getValue().carrierText); } + @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testAirplaneMode_flagEnabled() { + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(0)).thenReturn( + TelephonyManager.SIM_STATE_READY); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + mCarrierTextManager.updateCarrierText(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertEquals(AIRPLANE_MODE_TEXT, captor.getValue().carrierText); + } + /** regression test for b/281706473, caused by sending NULL plmn / spn to the logger */ @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testAirplaneMode_noSim_nullPlmn_nullSpn_doesNotCrash() { // GIVEN - sticy broadcast that returns a null PLMN and null SPN Intent stickyIntent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED); @@ -263,7 +291,52 @@ public class CarrierTextManagerTest extends SysuiTestCase { // No assert, this test should not crash } + /** regression test for b/281706473, caused by sending NULL plmn / spn to the logger */ + @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testAirplaneMode_noSim_nullPlmn_nullSpn_doesNotCrash_flagEnabled() { + // GIVEN - sticy broadcast that returns a null PLMN and null SPN + Intent stickyIntent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED); + stickyIntent.putExtra(TelephonyManager.EXTRA_SHOW_PLMN, true); + stickyIntent.removeExtra(TelephonyManager.EXTRA_PLMN); + stickyIntent.putExtra(TelephonyManager.EXTRA_SHOW_SPN, true); + stickyIntent.removeExtra(TelephonyManager.EXTRA_SPN); + + mCarrierTextManager = new CarrierTextManager.Builder( + getContextSpyForStickyBroadcast(stickyIntent), + mContext.getResources(), + mWifiRepository, + mSatelliteViewModel, + mJavaAdapter, + mTelephonyManager, + mTelephonyListenerManager, + mWakefulnessLifecycle, + mMainExecutor, + mBgExecutor, + mKeyguardUpdateMonitor, + mLogger + ) + .setShowAirplaneMode(true) + .setShowMissingSim(true) + .build(); + + // GIVEN - airplane mode is off (causing CTM to fetch the sticky broadcast) + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(0)) + .thenReturn(TelephonyManager.SIM_STATE_NOT_READY); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + // WHEN CTM fetches the broadcast and attempts to log the result, no crash results + mCarrierTextManager.updateCarrierText(); + + // No assert, this test should not crash + } + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCardIOError() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -299,6 +372,44 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCardIOError_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(0)).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(1)).thenReturn( + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + mCarrierTextManager.mCallback.onSimStateChanged(3, 1, + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertEquals("TEST_CARRIER" + SEPARATOR + INVALID_CARD_TEXT, captor.getValue().carrierText); + // There's only one subscription in the list + assertEquals(1, captor.getValue().listOfCarriers.length); + assertEquals(TEST_CARRIER, captor.getValue().listOfCarriers[0]); + + // Now it becomes single SIM active mode. + reset(mCarrierTextCallback); + when(mTelephonyManager.getActiveModemCount()).thenReturn(1); + // Update carrier text. It should ignore error state of subId 3 in inactive slotId. + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertEquals("TEST_CARRIER", captor.getValue().carrierText); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testWrongSlots() { reset(mCarrierTextCallback); when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn( @@ -313,6 +424,22 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testWrongSlots_flagEnabled() { + reset(mCarrierTextCallback); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn( + new ArrayList<>()); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + // This should not produce an out of bounds error, even though there are no subscriptions + mCarrierTextManager.mCallback.onSimStateChanged(0, -3, + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + mCarrierTextManager.mCallback.onSimStateChanged(0, 3, TelephonyManager.SIM_STATE_READY); + verify(mCarrierTextCallback, never()).updateCarrierInfo(any()); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testMoreSlotsThanSubs() { reset(mCarrierTextCallback); when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn( @@ -335,6 +462,29 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testMoreSlotsThanSubs_flagEnabled() { + reset(mCarrierTextCallback); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn( + new ArrayList<>()); + + // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the + // same answer as KeyguardUpdateMonitor. Remove when this is addressed + when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn( + new ArrayList<>()); + + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + // This should not produce an out of bounds error, even though there are no subscriptions + mCarrierTextManager.mCallback.onSimStateChanged(0, 1, + TelephonyManager.SIM_STATE_CARD_IO_ERROR); + + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo( + any(CarrierTextManager.CarrierTextCallbackInfo.class)); + } + + @Test public void testCallback() { reset(mCarrierTextCallback); mCarrierTextManager.postToCallback(mCarrierTextCallbackInfo); @@ -360,6 +510,7 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCreateInfo_OneValidSubscription() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -385,6 +536,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCreateInfo_OneValidSubscription_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + CarrierTextManager.CarrierTextCallbackInfo info = captor.getValue(); + assertEquals(1, info.listOfCarriers.length); + assertEquals(TEST_CARRIER, info.listOfCarriers[0]); + assertEquals(1, info.subscriptionIds.length); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCreateInfo_OneValidSubscriptionWithRoaming() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -410,6 +588,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCreateInfo_OneValidSubscriptionWithRoaming_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION_ROAMING); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + CarrierTextManager.CarrierTextCallbackInfo info = captor.getValue(); + assertEquals(1, info.listOfCarriers.length); + assertTrue(info.listOfCarriers[0].toString().contains(TEST_CARRIER)); + assertEquals(1, info.subscriptionIds.length); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_noTextOnReadySimWhenNull() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -434,6 +639,32 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_noTextOnReadySimWhenNull_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION_NULL); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertTrue("Carrier text should be empty, instead it's " + captor.getValue().carrierText, + TextUtils.isEmpty(captor.getValue().carrierText)); + assertFalse("No SIM should be available", captor.getValue().anySimReady); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_noTextOnReadySimWhenNull_airplaneMode_wifiOn() { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); reset(mCarrierTextCallback); @@ -472,6 +703,46 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_noTextOnReadySimWhenNull_airplaneMode_wifiOn_flagEnabled() { + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION_NULL); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + assertFalse(mWifiRepository.isWifiConnectedWithValidSsid()); + mWifiRepository.setWifiNetwork( + WifiNetworkModel.Active.Companion.of( + /* isValidated= */ false, + /* level= */ 0, + /* ssid= */ "", + /* hotspotDeviceType= */ WifiNetworkModel.HotspotDeviceType.NONE)); + assertTrue(mWifiRepository.isWifiConnectedWithValidSsid()); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + ServiceState ss = mock(ServiceState.class); + when(ss.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE); + mKeyguardUpdateMonitor.mServiceStates.put(TEST_SUBSCRIPTION_NULL.getSubscriptionId(), ss); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertFalse("No SIM should be available", captor.getValue().anySimReady); + // There's no airplane mode if at least one SIM is State.READY and there's wifi + assertFalse("Device should not be in airplane mode", captor.getValue().airplaneMode); + assertNotEquals(AIRPLANE_MODE_TEXT, captor.getValue().carrierText); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void carrierText_satelliteTextNull_isSatelliteFalse_textNotUsed() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -497,6 +768,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void carrierText_satelliteTextNull_isSatelliteFalse_textNotUsed_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + // WHEN the satellite text is null + mSatelliteViewModel.getCarrierText().setValue(null); + mTestScope.getTestScheduler().runCurrent(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + + // THEN satellite mode is false and the default subscription carrier text is used + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertThat(captor.getValue().isInSatelliteMode).isFalse(); + assertThat(captor.getValue().carrierText).isEqualTo(TEST_CARRIER); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void carrierText_hasSatelliteText_isSatelliteTrue_textUsed() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -522,6 +820,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void carrierText_hasSatelliteText_isSatelliteTrue_textUsed_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + // WHEN the satellite text is non-null + mSatelliteViewModel.getCarrierText().setValue("Satellite Test Text"); + mTestScope.getTestScheduler().runCurrent(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + + // THEN satellite mode is true and the satellite text is used + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertThat(captor.getValue().isInSatelliteMode).isTrue(); + assertThat(captor.getValue().carrierText).isEqualTo("Satellite Test Text"); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void carrierText_satelliteTextUpdates_autoTriggersCallback() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -559,6 +884,45 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void carrierText_satelliteTextUpdates_autoTriggersCallback_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + // WHEN the satellite text is set + mSatelliteViewModel.getCarrierText().setValue("Test satellite text"); + mTestScope.getTestScheduler().runCurrent(); + + // THEN we should automatically re-trigger #updateCarrierText and get callback info + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + // AND use the satellite text as the carrier text + assertThat(captor.getValue().isInSatelliteMode).isTrue(); + assertThat(captor.getValue().carrierText).isEqualTo("Test satellite text"); + + // WHEN the satellite text is reset to null + reset(mCarrierTextCallback); + mSatelliteViewModel.getCarrierText().setValue(null); + mTestScope.getTestScheduler().runCurrent(); + + // THEN we should automatically re-trigger #updateCarrierText and get callback info + // that doesn't include the satellite info + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertThat(captor.getValue().isInSatelliteMode).isFalse(); + assertThat(captor.getValue().carrierText).isEqualTo(TEST_CARRIER); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void carrierText_updatedWhileNotListening_getsNewValueWhenListening() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -602,6 +966,50 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void carrierText_updatedWhileNotListening_getsNewValueWhenListening_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + mSatelliteViewModel.getCarrierText().setValue("Old satellite text"); + mTestScope.getTestScheduler().runCurrent(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertThat(captor.getValue().carrierText).isEqualTo("Old satellite text"); + + // WHEN we stop listening + reset(mCarrierTextCallback); + mCarrierTextManager.setListening(null); + + // AND the satellite text updates + mSatelliteViewModel.getCarrierText().setValue("New satellite text"); + + // THEN we don't get new callback info because we aren't listening + verify(mCarrierTextCallback, never()).updateCarrierInfo(any()); + + // WHEN we start listening again + reset(mCarrierTextCallback); + mCarrierTextManager.setListening(mCarrierTextCallback); + + // THEN we should automatically re-trigger #updateCarrierText and get callback info + // that includes the new satellite state and text + mTestScope.getTestScheduler().runCurrent(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + assertThat(captor.getValue().isInSatelliteMode).isTrue(); + assertThat(captor.getValue().carrierText).isEqualTo("New satellite text"); + } + + @Test public void testCreateInfo_noSubscriptions() { reset(mCarrierTextCallback); when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn( @@ -622,6 +1030,7 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_oneValidSubscription() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -644,6 +1053,30 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_oneValidSubscription_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertThat(captor.getValue().carrierText).isEqualTo(TEST_CARRIER); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_twoValidSubscriptions() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -668,6 +1101,32 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_twoValidSubscriptions_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())).thenReturn( + TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertEquals(TEST_CARRIER + SEPARATOR + TEST_CARRIER, + captor.getValue().carrierText); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_oneDisabledSub() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -693,6 +1152,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_oneDisabledSub_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())) + .thenReturn(TelephonyManager.SIM_STATE_READY) + .thenReturn(TelephonyManager.SIM_STATE_NOT_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertEquals(TEST_CARRIER, + captor.getValue().carrierText); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_firstDisabledSub() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -718,6 +1204,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_firstDisabledSub_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())) + .thenReturn(TelephonyManager.SIM_STATE_NOT_READY) + .thenReturn(TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertEquals(TEST_CARRIER, + captor.getValue().carrierText); + } + + @Test + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) public void testCarrierText_threeSubsMiddleDisabled() { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); @@ -744,6 +1257,33 @@ public class CarrierTextManagerTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testCarrierText_threeSubsMiddleDisabled_flagEnabled() { + reset(mCarrierTextCallback); + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + list.add(TEST_SUBSCRIPTION); + list.add(TEST_SUBSCRIPTION); + when(mKeyguardUpdateMonitor.getSimStateForSlotId(anyInt())) + .thenReturn(TelephonyManager.SIM_STATE_READY) + .thenReturn(TelephonyManager.SIM_STATE_NOT_READY) + .thenReturn(TelephonyManager.SIM_STATE_READY); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list); + mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); + + ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor = + ArgumentCaptor.forClass( + CarrierTextManager.CarrierTextCallbackInfo.class); + + mCarrierTextManager.updateCarrierText(); + FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor); + verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); + + assertEquals(TEST_CARRIER + SEPARATOR + TEST_CARRIER, + captor.getValue().carrierText); + } + + @Test public void testGetStatusForIccState() { when(mKeyguardUpdateMonitor.isDeviceProvisioned()).thenReturn(false); assertEquals(CarrierTextManager.StatusMode.SimMissingLocked, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index a39ca5de787d..839a2bdf5588 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -103,6 +103,7 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.service.dreams.IDreamManager; @@ -200,8 +201,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private static final String TEST_CARRIER_2 = "TEST_CARRIER_2"; private static final int TEST_CARRIER_ID = 1; private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24"; - private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", 0, - TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "", + private static final int TEST_SLOT_ID = 3; + private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", + TEST_SLOT_ID, TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "", DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID, TEST_CARRIER_ID, 0); private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(2, "", 0, @@ -483,7 +485,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testSimStateInitialized() { + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testSimStateInitialized_flagDisabled() { cleanupKeyguardUpdateMonitor(); final int subId = 3; final int state = TelephonyManager.SIM_STATE_ABSENT; @@ -500,6 +503,24 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testSimStateInitialized_flagEnabled() { + cleanupKeyguardUpdateMonitor(); + final int state = TelephonyManager.SIM_STATE_ABSENT; + final int slotId = 0; + final int subId = 3; + when(mTelephonyManager.getActiveModemCount()).thenReturn(1); + when(mTelephonyManager.getSimState(anyInt())).thenReturn(state); + when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[]{subId}); + + KeyguardUpdateMonitor testKUM = new TestableKeyguardUpdateMonitor(mContext); + + mTestableLooper.processAllMessages(); + + assertThat(testKUM.getSimStateForSlotId(slotId)).isEqualTo(state); + } + + @Test public void testIgnoresSimStateCallback_rebroadcast() { Intent intent = defaultSimStateChangedIntent(); @@ -1240,7 +1261,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testActiveSubscriptionBecomesInactive() { + @DisableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testActiveSubscriptionBecomesInactive_flagDisabled() { List<SubscriptionInfo> list = new ArrayList<>(); list.add(TEST_SUBSCRIPTION); when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list); @@ -1263,6 +1285,28 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + @EnableFlags(Flags.FLAG_SIM_PIN_USE_SLOT_ID) + public void testActiveSubscriptionBecomesInactive_flagEnabled() { + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list); + mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged( + TEST_SUBSCRIPTION.getSubscriptionId()); + mTestableLooper.processAllMessages(); + assertThat(mKeyguardUpdateMonitor.mSimDatasBySlotId.get(TEST_SLOT_ID)) + .isNotNull(); + + when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()) + .thenReturn(new ArrayList<>()); + mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged( + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + mTestableLooper.processAllMessages(); + + assertThat(mKeyguardUpdateMonitor.mSimDatasBySlotId.get(TEST_SLOT_ID)) + .isNull(); + } + + @Test public void testActiveSubscriptionList_filtersProvisioningNetworks() { List<SubscriptionInfo> list = new ArrayList<>(); list.add(TEST_SUBSCRIPTION_PROVISIONING); diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt index 425aad2bd43c..4aaa82e4a16d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt @@ -404,7 +404,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { @Test fun creatingRunnerWithLazyInitializationThrows_whenTheFlagsAreDisabled() { assertThrows(IllegalStateException::class.java) { - activityTransitionAnimator.createRunner(controller, initializeLazily = true) + activityTransitionAnimator.createRunner(controller, longLived = true) } } @@ -414,7 +414,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { ) @Test fun runnerCreatesDelegateLazily_whenPostingTimeouts() { - val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = true) + val runner = activityTransitionAnimator.createRunner(controller, longLived = true) assertNull(runner.delegate) runner.postTimeouts() assertNotNull(runner.delegate) @@ -426,7 +426,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { ) @Test fun runnerCreatesDelegateLazily_onAnimationStart() { - val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = true) + val runner = activityTransitionAnimator.createRunner(controller, longLived = true) assertNull(runner.delegate) // The delegate is cleaned up after execution (which happens in another thread), so what we @@ -458,7 +458,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { ) @Test fun runnerCreatesDelegateLazily_onAnimationTakeover() { - val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = true) + val runner = activityTransitionAnimator.createRunner(controller, longLived = true) assertNull(runner.delegate) // The delegate is cleaned up after execution (which happens in another thread), so what we @@ -489,7 +489,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { ) @Test fun animationTakeoverThrows_whenTheFlagsAreDisabled() { - val runner = activityTransitionAnimator.createRunner(controller, initializeLazily = false) + val runner = activityTransitionAnimator.createRunner(controller, longLived = false) assertThrows(IllegalStateException::class.java) { runner.takeOverAnimation( arrayOf(fakeWindow()), diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt index 4d138b488645..5d622eaeb1aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt @@ -122,9 +122,24 @@ class AudioSharingDeviceItemActionInteractorTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_AUDIO_SHARING_QS_DIALOG_IMPROVEMENT) + fun testOnClick_connectedAudioSharingMediaDevice_flagOff_previewOn_createDialog() { + with(kosmos) { + testScope.runTest { + whenever(BluetoothUtils.isAudioSharingPreviewEnabled(any())).thenReturn(true) + bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true) + actionInteractorImpl.onClick(connectedAudioSharingMediaDeviceItem, dialog) + verify(dialogTransitionAnimator) + .showFromDialog(any(), any(), eq(null), anyBoolean()) + } + } + } + + @Test + @DisableFlags(Flags.FLAG_AUDIO_SHARING_QS_DIALOG_IMPROVEMENT) fun testOnClick_connectedAudioSharingMediaDevice_flagOff_shouldLaunchSettings() { with(kosmos) { testScope.runTest { + whenever(BluetoothUtils.isAudioSharingPreviewEnabled(any())).thenReturn(false) bluetoothTileDialogAudioSharingRepository.setAudioSharingAvailable(true) whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) actionInteractorImpl.onClick(connectedAudioSharingMediaDeviceItem, dialog) diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt index 2c1718176571..bfbdc50c32b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt @@ -24,9 +24,9 @@ import android.os.Handler import android.os.Looper import android.os.PatternMatcher import android.os.UserHandle -import androidx.test.filters.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger import com.android.systemui.dump.DumpManager @@ -40,8 +40,9 @@ import java.util.concurrent.Executor import junit.framework.Assert.assertSame import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.advanceUntilIdle -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -68,39 +69,28 @@ class BroadcastDispatcherTest : SysuiTestCase() { val DEFAULT_PERMISSION: String? = null fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() + const val TEST_ACTION = "TEST_ACTION" const val TEST_SCHEME = "TEST_SCHEME" const val TEST_PATH = "TEST_PATH" const val TEST_TYPE = "test/type" } - @Mock - private lateinit var mockContext: Context - @Mock - private lateinit var mockUBRUser0: UserBroadcastDispatcher - @Mock - private lateinit var mockUBRUser1: UserBroadcastDispatcher - @Mock - private lateinit var broadcastReceiver: BroadcastReceiver - @Mock - private lateinit var broadcastReceiverOther: BroadcastReceiver - @Mock - private lateinit var intentFilter: IntentFilter - @Mock - private lateinit var intentFilterOther: IntentFilter - @Mock - private lateinit var mockHandler: Handler - @Mock - private lateinit var logger: BroadcastDispatcherLogger - @Mock - private lateinit var userTracker: UserTracker - @Mock - private lateinit var removalPendingStore: PendingRemovalStore + @Mock private lateinit var mockContext: Context + @Mock private lateinit var mockUBRUser0: UserBroadcastDispatcher + @Mock private lateinit var mockUBRUser1: UserBroadcastDispatcher + @Mock private lateinit var broadcastReceiver: BroadcastReceiver + @Mock private lateinit var broadcastReceiverOther: BroadcastReceiver + @Mock private lateinit var intentFilter: IntentFilter + @Mock private lateinit var intentFilterOther: IntentFilter + @Mock private lateinit var mockHandler: Handler + @Mock private lateinit var logger: BroadcastDispatcherLogger + @Mock private lateinit var userTracker: UserTracker + @Mock private lateinit var removalPendingStore: PendingRemovalStore private lateinit var mainExecutor: Executor - @Captor - private lateinit var argumentCaptor: ArgumentCaptor<ReceiverData> + @Captor private lateinit var argumentCaptor: ArgumentCaptor<ReceiverData> private lateinit var testableLooper: TestableLooper private lateinit var broadcastDispatcher: BroadcastDispatcher @@ -112,7 +102,8 @@ class BroadcastDispatcherTest : SysuiTestCase() { mainExecutor = FakeExecutor(FakeSystemClock()) `when`(mockContext.mainExecutor).thenReturn(mainExecutor) - broadcastDispatcher = TestBroadcastDispatcher( + broadcastDispatcher = + TestBroadcastDispatcher( mockContext, mainExecutor, testableLooper.looper, @@ -121,7 +112,8 @@ class BroadcastDispatcherTest : SysuiTestCase() { logger, userTracker, removalPendingStore, - mapOf(0 to mockUBRUser0, 1 to mockUBRUser1)) + mapOf(0 to mockUBRUser0, 1 to mockUBRUser1), + ) // These should be valid filters `when`(intentFilter.countActions()).thenReturn(1) @@ -131,10 +123,18 @@ class BroadcastDispatcherTest : SysuiTestCase() { @Test fun testAddingReceiverToCorrectUBR() { - broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, - mockHandler, user0) broadcastDispatcher.registerReceiverWithHandler( - broadcastReceiverOther, intentFilterOther, mockHandler, user1) + broadcastReceiver, + intentFilter, + mockHandler, + user0, + ) + broadcastDispatcher.registerReceiverWithHandler( + broadcastReceiverOther, + intentFilterOther, + mockHandler, + user1, + ) testableLooper.processAllMessages() @@ -152,7 +152,11 @@ class BroadcastDispatcherTest : SysuiTestCase() { fun testAddingReceiverToCorrectUBR_executor() { broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mainExecutor, user0) broadcastDispatcher.registerReceiver( - broadcastReceiverOther, intentFilterOther, mainExecutor, user1) + broadcastReceiverOther, + intentFilterOther, + mainExecutor, + user1, + ) testableLooper.processAllMessages() @@ -169,7 +173,10 @@ class BroadcastDispatcherTest : SysuiTestCase() { @Test fun testAddReceiverDefaultFlag_handler() { broadcastDispatcher.registerReceiverWithHandler( - broadcastReceiver, intentFilter, mockHandler) + broadcastReceiver, + intentFilter, + mockHandler, + ) testableLooper.processAllMessages() verify(mockUBRUser0).registerReceiver(capture(argumentCaptor), eq(DEFAULT_FLAG)) @@ -183,7 +190,11 @@ class BroadcastDispatcherTest : SysuiTestCase() { val flag = 3 broadcastDispatcher.registerReceiverWithHandler( - broadcastReceiver, intentFilter, mockHandler, flags = flag) + broadcastReceiver, + intentFilter, + mockHandler, + flags = flag, + ) testableLooper.processAllMessages() verify(mockUBRUser0).registerReceiver(capture(argumentCaptor), eq(flag)) @@ -212,7 +223,7 @@ class BroadcastDispatcherTest : SysuiTestCase() { broadcastReceiver, intentFilter, flags = flag, - permission = permission + permission = permission, ) testableLooper.processAllMessages() @@ -250,10 +261,18 @@ class BroadcastDispatcherTest : SysuiTestCase() { @Test fun testRemovingReceiversRemovesFromAllUBR() { - broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, - mockHandler, user0) - broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, - mockHandler, user1) + broadcastDispatcher.registerReceiverWithHandler( + broadcastReceiver, + intentFilter, + mockHandler, + user0, + ) + broadcastDispatcher.registerReceiverWithHandler( + broadcastReceiver, + intentFilter, + mockHandler, + user1, + ) broadcastDispatcher.unregisterReceiver(broadcastReceiver) @@ -265,10 +284,18 @@ class BroadcastDispatcherTest : SysuiTestCase() { @Test fun testRemoveReceiverFromUser() { - broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, - mockHandler, user0) - broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, - mockHandler, user1) + broadcastDispatcher.registerReceiverWithHandler( + broadcastReceiver, + intentFilter, + mockHandler, + user0, + ) + broadcastDispatcher.registerReceiverWithHandler( + broadcastReceiver, + intentFilter, + mockHandler, + user1, + ) broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, user0) @@ -282,13 +309,17 @@ class BroadcastDispatcherTest : SysuiTestCase() { fun testRegisterCurrentAsActualUser() { `when`(userTracker.userId).thenReturn(user1.identifier) - broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter, - mockHandler, UserHandle.CURRENT) + broadcastDispatcher.registerReceiverWithHandler( + broadcastReceiver, + intentFilter, + mockHandler, + UserHandle.CURRENT, + ) testableLooper.processAllMessages() - verify(mockUBRUser1).registerReceiver( - capture(argumentCaptor), eq(Context.RECEIVER_EXPORTED)) + verify(mockUBRUser1) + .registerReceiver(capture(argumentCaptor), eq(Context.RECEIVER_EXPORTED)) assertSame(broadcastReceiver, argumentCaptor.value.receiver) } @@ -300,41 +331,38 @@ class BroadcastDispatcherTest : SysuiTestCase() { @Test(expected = IllegalArgumentException::class) fun testFilterMustNotContainDataScheme() { - val testFilter = IntentFilter(TEST_ACTION).apply { - addDataScheme(TEST_SCHEME) - } + val testFilter = IntentFilter(TEST_ACTION).apply { addDataScheme(TEST_SCHEME) } broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) } @Test(expected = IllegalArgumentException::class) fun testFilterMustNotContainDataAuthority() { - val testFilter = IntentFilter(TEST_ACTION).apply { - addDataAuthority(mock(IntentFilter.AuthorityEntry::class.java)) - } + val testFilter = + IntentFilter(TEST_ACTION).apply { + addDataAuthority(mock(IntentFilter.AuthorityEntry::class.java)) + } broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) } @Test(expected = IllegalArgumentException::class) fun testFilterMustNotContainDataPath() { - val testFilter = IntentFilter(TEST_ACTION).apply { - addDataPath(TEST_PATH, PatternMatcher.PATTERN_LITERAL) - } + val testFilter = + IntentFilter(TEST_ACTION).apply { + addDataPath(TEST_PATH, PatternMatcher.PATTERN_LITERAL) + } broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) } @Test(expected = IllegalArgumentException::class) fun testFilterMustNotContainDataType() { - val testFilter = IntentFilter(TEST_ACTION).apply { - addDataType(TEST_TYPE) - } + val testFilter = IntentFilter(TEST_ACTION).apply { addDataType(TEST_TYPE) } broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) } @Test(expected = IllegalArgumentException::class) fun testFilterMustNotSetPriority() { - val testFilter = IntentFilter(TEST_ACTION).apply { - priority = IntentFilter.SYSTEM_HIGH_PRIORITY - } + val testFilter = + IntentFilter(TEST_ACTION).apply { priority = IntentFilter.SYSTEM_HIGH_PRIORITY } broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) } @@ -366,12 +394,14 @@ class BroadcastDispatcherTest : SysuiTestCase() { val inOrderUser0 = inOrder(mockUBRUser0, removalPendingStore) inOrderUser0.verify(mockUBRUser0).unregisterReceiver(broadcastReceiver) - inOrderUser0.verify(removalPendingStore) + inOrderUser0 + .verify(removalPendingStore) .clearPendingRemoval(broadcastReceiver, UserHandle.USER_ALL) val inOrderUser1 = inOrder(mockUBRUser1, removalPendingStore) inOrderUser1.verify(mockUBRUser1).unregisterReceiver(broadcastReceiver) - inOrderUser1.verify(removalPendingStore) + inOrderUser1 + .verify(removalPendingStore) .clearPendingRemoval(broadcastReceiver, UserHandle.USER_ALL) } @@ -385,21 +415,21 @@ class BroadcastDispatcherTest : SysuiTestCase() { val inOrderUser1 = inOrder(mockUBRUser1, removalPendingStore) inOrderUser1.verify(mockUBRUser1).unregisterReceiver(broadcastReceiver) - inOrderUser1.verify(removalPendingStore) + inOrderUser1 + .verify(removalPendingStore) .clearPendingRemoval(broadcastReceiver, user1.identifier) } @Test - fun testBroadcastFlow() = runBlockingTest { - val flow = broadcastDispatcher.broadcastFlow(intentFilter, user1) { intent, receiver -> - intent to receiver - } + fun testBroadcastFlow() = runTest(UnconfinedTestDispatcher()) { + val flow = + broadcastDispatcher.broadcastFlow(intentFilter, user1) { intent, receiver -> + intent to receiver + } // Collect the values into collectedValues. val collectedValues = mutableListOf<Pair<Intent, BroadcastReceiver>>() - val job = launch { - flow.collect { collectedValues.add(it) } - } + val job = launch { flow.collect { collectedValues.add(it) } } testableLooper.processAllMessages() verify(mockUBRUser1).registerReceiver(capture(argumentCaptor), eq(DEFAULT_FLAG)) @@ -436,17 +466,18 @@ class BroadcastDispatcherTest : SysuiTestCase() { logger: BroadcastDispatcherLogger, userTracker: UserTracker, removalPendingStore: PendingRemovalStore, - var mockUBRMap: Map<Int, UserBroadcastDispatcher> - ) : BroadcastDispatcher( - context, - mainExecutor, - backgroundRunningLooper, - backgroundRunningExecutor, - dumpManager, - logger, - userTracker, - removalPendingStore - ) { + var mockUBRMap: Map<Int, UserBroadcastDispatcher>, + ) : + BroadcastDispatcher( + context, + mainExecutor, + backgroundRunningLooper, + backgroundRunningExecutor, + dumpManager, + logger, + userTracker, + removalPendingStore, + ) { override fun createUBRForUser(userId: Int): UserBroadcastDispatcher { return mockUBRMap.getOrDefault(userId, mock(UserBroadcastDispatcher::class.java)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt index 85ab746211d6..d0ce34c2d68d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt @@ -23,18 +23,20 @@ import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_OTH import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS import android.hardware.input.fakeInputManager -import android.os.SystemClock -import android.view.KeyEvent.ACTION_DOWN -import android.view.KeyEvent.KEYCODE_A -import android.view.KeyEvent.META_CTRL_ON -import android.view.KeyEvent.META_META_ON -import androidx.compose.ui.input.key.KeyEvent 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.keyboard.shortcut.shared.model.ShortcutCategoryType -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedStandardAddShortcutUiState +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedStandardDeleteShortcutUiState +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyDownEventWithActionKeyPressed +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyDownEventWithoutActionKeyPressed +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyUpEventWithActionKeyPressed +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardAddCustomShortcutRequestInfo +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardAddShortcutRequest +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardDeleteCustomShortcutRequestInfo import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shortcutCustomizationViewModelFactory import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper @@ -95,11 +97,21 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { } @Test + fun uiState_correctlyUpdatedWhenDeleteShortcutCustomizationIsRequested() { + testScope.runTest { + viewModel.onShortcutCustomizationRequested(standardDeleteCustomShortcutRequestInfo) + val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) + + assertThat(uiState).isEqualTo(expectedStandardDeleteShortcutUiState) + } + } + + @Test fun uiState_consumedOnAddDialogShown() { testScope.runTest { val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) - viewModel.onAddShortcutDialogShown() + viewModel.onDialogShown() assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).isDialogShowing) .isTrue() @@ -107,11 +119,25 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { } @Test + fun uiState_consumedOnDeleteDialogShown() { + testScope.runTest { + val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) + viewModel.onShortcutCustomizationRequested(standardDeleteCustomShortcutRequestInfo) + viewModel.onDialogShown() + + assertThat( + (uiState as ShortcutCustomizationUiState.DeleteShortcutDialog).isDialogShowing + ) + .isTrue() + } + } + + @Test fun uiState_inactiveAfterDialogIsDismissed() { testScope.runTest { val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) - viewModel.onAddShortcutDialogShown() + viewModel.onDialogShown() viewModel.onDialogDismissed() assertThat(uiState).isEqualTo(ShortcutCustomizationUiState.Inactive) } @@ -144,8 +170,8 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { fun uiState_errorMessage_isEmptyByDefault() { testScope.runTest { val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) - viewModel.onShortcutCustomizationRequested(allAppsShortcutCustomizationRequest) - viewModel.onAddShortcutDialogShown() + viewModel.onShortcutCustomizationRequested(standardAddCustomShortcutRequestInfo) + viewModel.onDialogShown() assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).errorMessage) .isEmpty() @@ -203,6 +229,21 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { } @Test + fun uiState_becomesInactiveAfterSuccessfullyDeletingShortcut() { + testScope.runTest { + val uiState by collectLastValue(viewModel.shortcutCustomizationUiState) + whenever(inputManager.getCustomInputGestures(any())) + .thenReturn(listOf(goHomeInputGestureData, allAppsInputGestureData)) + whenever(inputManager.removeCustomInputGesture(any())) + .thenReturn(CUSTOM_INPUT_GESTURE_RESULT_SUCCESS) + + openDeleteShortcutDialogAndDeleteShortcut() + + assertThat(uiState).isEqualTo(ShortcutCustomizationUiState.Inactive) + } + } + + @Test fun onKeyPressed_handlesKeyEvents_whereActionKeyIsAlsoPressed() { testScope.runTest { viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest) @@ -257,8 +298,8 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { } private suspend fun openAddShortcutDialogAndSetShortcut() { - viewModel.onShortcutCustomizationRequested(allAppsShortcutCustomizationRequest) - viewModel.onAddShortcutDialogShown() + viewModel.onShortcutCustomizationRequested(standardAddCustomShortcutRequestInfo) + viewModel.onDialogShown() viewModel.onKeyPressed(keyDownEventWithActionKeyPressed) viewModel.onKeyPressed(keyUpEventWithActionKeyPressed) @@ -266,61 +307,10 @@ class ShortcutCustomizationViewModelTest : SysuiTestCase() { viewModel.onSetShortcut() } - private val keyDownEventWithoutActionKeyPressed = - KeyEvent( - android.view.KeyEvent( - /* downTime = */ SystemClock.uptimeMillis(), - /* eventTime = */ SystemClock.uptimeMillis(), - /* action = */ ACTION_DOWN, - /* code = */ KEYCODE_A, - /* repeat = */ 0, - /* metaState = */ META_CTRL_ON, - ) - ) - - private val keyDownEventWithActionKeyPressed = - KeyEvent( - android.view.KeyEvent( - /* downTime = */ SystemClock.uptimeMillis(), - /* eventTime = */ SystemClock.uptimeMillis(), - /* action = */ ACTION_DOWN, - /* code = */ KEYCODE_A, - /* repeat = */ 0, - /* metaState = */ META_CTRL_ON or META_META_ON, - ) - ) - - private val keyUpEventWithActionKeyPressed = - KeyEvent( - android.view.KeyEvent( - /* downTime = */ SystemClock.uptimeMillis(), - /* eventTime = */ SystemClock.uptimeMillis(), - /* action = */ ACTION_DOWN, - /* code = */ KEYCODE_A, - /* repeat = */ 0, - /* metaState = */ 0, - ) - ) - - private val standardAddShortcutRequest = - ShortcutCustomizationRequestInfo.Add( - label = "Standard shortcut", - categoryType = ShortcutCategoryType.System, - subCategoryLabel = "Standard subcategory", - ) - - private val allAppsShortcutCustomizationRequest = - ShortcutCustomizationRequestInfo.Add( - label = "Open apps list", - categoryType = ShortcutCategoryType.System, - subCategoryLabel = "System controls", - ) - - private val expectedStandardAddShortcutUiState = - ShortcutCustomizationUiState.AddShortcutDialog( - shortcutLabel = "Standard shortcut", - defaultCustomShortcutModifierKey = - ShortcutKey.Icon.ResIdIcon(R.drawable.ic_ksh_key_meta), - isDialogShowing = false, - ) + private suspend fun openDeleteShortcutDialogAndDeleteShortcut() { + viewModel.onShortcutCustomizationRequested(standardDeleteCustomShortcutRequestInfo) + viewModel.onDialogShown() + + viewModel.deleteShortcutCurrentlyBeingCustomized() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java index bf4ef509ac80..eb1b44b75247 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java @@ -59,6 +59,7 @@ import android.os.Bundle; import android.os.PowerExemptionManager; import android.os.RemoteException; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.service.notification.StatusBarNotification; import android.testing.TestableLooper; @@ -242,10 +243,14 @@ public class MediaSwitchingControllerTest extends SysuiTestCase { mLocalMediaManager = spy(mMediaSwitchingController.mLocalMediaManager); when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false); mMediaSwitchingController.mLocalMediaManager = mLocalMediaManager; + mMediaSwitchingController.mInputRouteManager = new InputRouteManager(mContext, mAudioManager); mInputRouteManager = spy(mMediaSwitchingController.mInputRouteManager); mMediaSwitchingController.mInputRouteManager = mInputRouteManager; + when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) + .thenReturn(new AudioDeviceInfo[0]); + MediaDescription.Builder builder = new MediaDescription.Builder(); builder.setTitle(TEST_SONG); builder.setSubtitle(TEST_ARTIST); @@ -483,11 +488,11 @@ public class MediaSwitchingControllerTest extends SysuiTestCase { verify(mMediaDevice2, never()).setRangeZone(anyInt()); } + @DisableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) @Test public void onDeviceListUpdate_verifyDeviceListCallback() { // This test relies on mMediaSwitchingController.start being called while the selected - // device - // list has exactly one item, and that item's id is: + // device list has exactly one item, and that item's id is: // - Different from both ids in mMediaDevices. // - Different from the id of the route published by the device under test (usually the // built-in speakers). @@ -511,16 +516,54 @@ public class MediaSwitchingControllerTest extends SysuiTestCase { assertThat(devices.containsAll(mMediaDevices)).isTrue(); assertThat(devices.size()).isEqualTo(mMediaDevices.size()); + // There should be 2 non-MediaDevice items: the "Speakers & Display" title, and the "Connect + // a device" button. assertThat(mMediaSwitchingController.getMediaItemList().size()) .isEqualTo(mMediaDevices.size() + 2); verify(mCb).onDeviceListChanged(); } + @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @Test + public void onDeviceListUpdate_verifyDeviceListCallback_inputRouting() { + // This test relies on mMediaSwitchingController.start being called while the selected + // device list has exactly one item, and that item's id is: + // - Different from both ids in mMediaDevices. + // - Different from the id of the route published by the device under test (usually the + // built-in speakers). + // So mock the selected device to respect these two preconditions. + MediaDevice mockSelectedMediaDevice = Mockito.mock(MediaDevice.class); + when(mockSelectedMediaDevice.getId()).thenReturn(TEST_DEVICE_3_ID); + doReturn(List.of(mockSelectedMediaDevice)) + .when(mLocalMediaManager) + .getSelectedMediaDevice(); + + mMediaSwitchingController.start(mCb); + reset(mCb); + + mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); + final List<MediaDevice> devices = new ArrayList<>(); + for (MediaItem item : mMediaSwitchingController.getMediaItemList()) { + if (item.getMediaDevice().isPresent()) { + devices.add(item.getMediaDevice().get()); + } + } + + assertThat(devices.containsAll(mMediaDevices)).isTrue(); + assertThat(devices.size()).isEqualTo(mMediaDevices.size()); + // When input routing is enabled, there should be 4 non-MediaDevice items: one for + // the "Output" title, one for the "Speakers & Displays" title, one for the "Connect a + // device" button, and one for the "Input" title. + assertThat(mMediaSwitchingController.getMediaItemList().size()) + .isEqualTo(mMediaDevices.size() + 4); + verify(mCb).onDeviceListChanged(); + } + + @DisableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) @Test public void advanced_onDeviceListUpdateWithConnectedDeviceRemote_verifyItemSize() { // This test relies on mMediaSwitchingController.start being called while the selected - // device - // list has exactly one item, and that item's id is: + // device list has exactly one item, and that item's id is: // - Different from both ids in mMediaDevices. // - Different from the id of the route published by the device under test (usually the // built-in speakers). @@ -547,6 +590,7 @@ public class MediaSwitchingControllerTest extends SysuiTestCase { assertThat(devices.containsAll(mMediaDevices)).isTrue(); assertThat(devices.size()).isEqualTo(mMediaDevices.size()); + // There should be 1 non-MediaDevice item: the "Speakers & Display" title. assertThat(mMediaSwitchingController.getMediaItemList().size()) .isEqualTo(mMediaDevices.size() + 1); verify(mCb).onDeviceListChanged(); @@ -554,6 +598,45 @@ public class MediaSwitchingControllerTest extends SysuiTestCase { @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) @Test + public void advanced_onDeviceListUpdateWithConnectedDeviceRemote_verifyItemSize_inputRouting() { + // This test relies on mMediaSwitchingController.start being called while the selected + // device list has exactly one item, and that item's id is: + // - Different from both ids in mMediaDevices. + // - Different from the id of the route published by the device under test (usually the + // built-in speakers). + // So mock the selected device to respect these two preconditions. + MediaDevice mockSelectedMediaDevice = Mockito.mock(MediaDevice.class); + when(mockSelectedMediaDevice.getId()).thenReturn(TEST_DEVICE_3_ID); + doReturn(List.of(mockSelectedMediaDevice)) + .when(mLocalMediaManager) + .getSelectedMediaDevice(); + + when(mMediaDevice1.getFeatures()) + .thenReturn(ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)); + when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1); + mMediaSwitchingController.start(mCb); + reset(mCb); + + mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); + final List<MediaDevice> devices = new ArrayList<>(); + for (MediaItem item : mMediaSwitchingController.getMediaItemList()) { + if (item.getMediaDevice().isPresent()) { + devices.add(item.getMediaDevice().get()); + } + } + + assertThat(devices.containsAll(mMediaDevices)).isTrue(); + assertThat(devices.size()).isEqualTo(mMediaDevices.size()); + // When input routing is enabled, there should be 3 non-MediaDevice items: one for + // the "Output" title, one for the "Speakers & Displays" title, and one for the "Input" + // title. + assertThat(mMediaSwitchingController.getMediaItemList().size()) + .isEqualTo(mMediaDevices.size() + 3); + verify(mCb).onDeviceListChanged(); + } + + @EnableFlags(Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL) + @Test public void onInputDeviceListUpdate_verifyDeviceListCallback() { AudioDeviceInfo[] audioDeviceInfos = {}; when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt index fac5ecb49027..f23553eda3b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.click import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.performClick @@ -73,7 +74,7 @@ class ResizingTest : SysuiTestCase() { } @Test - fun toggleIconTile_shouldBeLarge() { + fun toggleIconTileWithA11yAction_shouldBeLarge() { var tiles by mutableStateOf(TestEditTiles) val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { @@ -89,7 +90,7 @@ class ResizingTest : SysuiTestCase() { } @Test - fun toggleLargeTile_shouldBeIcon() { + fun toggleLargeTileWithA11yAction_shouldBeIcon() { var tiles by mutableStateOf(TestEditTiles) val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { @@ -105,7 +106,7 @@ class ResizingTest : SysuiTestCase() { } @Test - fun resizedLarge_shouldBeIcon() { + fun tapOnIconResizingHandle_shouldBeLarge() { var tiles by mutableStateOf(TestEditTiles) val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { @@ -116,12 +117,32 @@ class ResizingTest : SysuiTestCase() { composeRule .onNodeWithContentDescription("tileA") .performClick() // Select - .performTouchInput { // Resize down - swipeRight() + .performTouchInput { // Tap on resizing handle + click(centerRight) + } + composeRule.waitForIdle() + + assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(2) + } + + @Test + fun tapOnLargeResizingHandle_shouldBeIcon() { + var tiles by mutableStateOf(TestEditTiles) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) + composeRule.setContent { + EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } + } + composeRule.waitForIdle() + + composeRule + .onNodeWithContentDescription("tileD_large") + .performClick() // Select + .performTouchInput { // Tap on resizing handle + click(centerRight) } composeRule.waitForIdle() - assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(1) + assertThat(tiles.find { it.tile.tileSpec.spec == "tileD_large" }?.width).isEqualTo(1) } @Test @@ -134,6 +155,26 @@ class ResizingTest : SysuiTestCase() { composeRule.waitForIdle() composeRule + .onNodeWithContentDescription("tileA") + .performClick() // Select + .performTouchInput { // Resize up + swipeRight(startX = right, endX = right * 2) + } + composeRule.waitForIdle() + + assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(2) + } + + @Test + fun resizedLarge_shouldBeIcon() { + var tiles by mutableStateOf(TestEditTiles) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) + composeRule.setContent { + EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } + } + composeRule.waitForIdle() + + composeRule .onNodeWithContentDescription("tileD_large") .performClick() // Select .performTouchInput { // Resize down 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 859f84edda39..e7fb470cfa76 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -33,6 +33,7 @@ import androidx.lifecycle.LifecycleOwner import androidx.test.filters.SmallTest import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags +import com.android.systemui.Flags.FLAG_COMMUNAL_HUB_ON_MOBILE import com.android.systemui.Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX import com.android.systemui.SysuiTestCase import com.android.systemui.ambient.touch.TouchHandler @@ -630,6 +631,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } } + @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE) @Test fun onTouchEvent_shadeInteracting_movesNotDispatched() = with(kosmos) { @@ -686,6 +688,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } } + @DisableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE) @Test fun onTouchEvent_bouncerInteracting_movesNotDispatched() = with(kosmos) { @@ -718,6 +721,19 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } } + @EnableFlags(FLAG_COMMUNAL_HUB_ON_MOBILE) + @Test + fun onTouchEvent_onLockscreenAndGlanceableHubV2_touchIgnored() = + with(kosmos) { + testScope.runTest { + // On lockscreen. + goToScene(CommunalScenes.Blank) + + assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() + verify(containerView, never()).onTouchEvent(DOWN_EVENT) + } + } + @Test fun disposeView_destroysTouchMonitor() { clearInvocations(touchMonitor) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index 61d14b73204a..58864f635190 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -76,7 +76,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.FakeEventLog; import com.android.systemui.util.settings.FakeGlobalSettings; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java index f870200c2b00..28577b336b6e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java @@ -61,7 +61,7 @@ import com.android.systemui.statusbar.notification.logging.nano.Notifications; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index d8a23d6bd32e..5aee92939ed5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -68,6 +68,7 @@ import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.notification.FeedbackIcon; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization; @@ -394,7 +395,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { ExpandableNotificationRow row = mNotificationTestHelper.createRow(); AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); row.setAboveShelfChangedListener(listener); - row.setPinned(true); + row.setPinnedStatus(PinnedStatus.PinnedBySystem); verify(listener).onAboveShelfStateChanged(true); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt index 723c0d701305..2d35ea5d4e95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt @@ -65,11 +65,11 @@ import com.android.systemui.statusbar.notification.AssistantFeedbackController import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.notification.headsup.headsUpManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.notificationLockscreenUserManager import com.android.systemui.statusbar.policy.deviceProvisionedController -import com.android.systemui.statusbar.policy.headsUpManager import com.android.systemui.testKosmos import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.wmshell.BubblesManager 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 d2350bc82667..11b19f95c1c0 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 @@ -103,7 +103,7 @@ import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrim import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.policy.AvalancheController; +import com.android.systemui.statusbar.notification.headsup.AvalancheController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index c9f8463d510c..8b4c3355443c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -198,7 +198,7 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ExtensionController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.window.StatusBarWindowController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index cace60ce4c0e..2f30b745a4a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -95,7 +95,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index a213c85eaa62..1d4b8e14695d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -58,6 +58,7 @@ import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import kotlin.sequences.Sequence; diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index aad8b4ba1191..af14edd10f5f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -154,7 +154,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -2575,78 +2575,6 @@ public class BubblesTest extends SysuiTestCase { @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) @Test - public void testEventLogging_bubbleBar_dragBarLeft() { - mBubbleProperties.mIsBubbleBarEnabled = true; - mPositioner.setIsLargeScreen(true); - mPositioner.setBubbleBarLocation(BubbleBarLocation.RIGHT); - FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); - mBubbleController.registerBubbleStateListener(bubbleStateListener); - - mEntryListener.onEntryAdded(mRow); - assertBarMode(); - - mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT, - BubbleBarLocation.UpdateSource.DRAG_BAR); - - verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BAR); - } - - @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) - @Test - public void testEventLogging_bubbleBar_dragBarRight() { - mBubbleProperties.mIsBubbleBarEnabled = true; - mPositioner.setIsLargeScreen(true); - mPositioner.setBubbleBarLocation(BubbleBarLocation.LEFT); - FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); - mBubbleController.registerBubbleStateListener(bubbleStateListener); - - mEntryListener.onEntryAdded(mRow); - assertBarMode(); - - mBubbleController.setBubbleBarLocation(BubbleBarLocation.RIGHT, - BubbleBarLocation.UpdateSource.DRAG_BAR); - - verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BAR); - } - - @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) - @Test - public void testEventLogging_bubbleBar_dragBubbleLeft() { - mBubbleProperties.mIsBubbleBarEnabled = true; - mPositioner.setIsLargeScreen(true); - mPositioner.setBubbleBarLocation(BubbleBarLocation.RIGHT); - FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); - mBubbleController.registerBubbleStateListener(bubbleStateListener); - - mEntryListener.onEntryAdded(mRow); - assertBarMode(); - - mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT, - BubbleBarLocation.UpdateSource.DRAG_BUBBLE); - - verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BUBBLE); - } - - @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) - @Test - public void testEventLogging_bubbleBar_dragBubbleRight() { - mBubbleProperties.mIsBubbleBarEnabled = true; - mPositioner.setIsLargeScreen(true); - mPositioner.setBubbleBarLocation(BubbleBarLocation.LEFT); - FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); - mBubbleController.registerBubbleStateListener(bubbleStateListener); - - mEntryListener.onEntryAdded(mRow); - assertBarMode(); - - mBubbleController.setBubbleBarLocation(BubbleBarLocation.RIGHT, - BubbleBarLocation.UpdateSource.DRAG_BUBBLE); - - verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BUBBLE); - } - - @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) - @Test public void testEventLogging_bubbleBar_expandAndCollapse() { mBubbleProperties.mIsBubbleBarEnabled = true; mPositioner.setIsLargeScreen(true); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt index 50631409308c..0ba7c8574d85 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt @@ -62,6 +62,7 @@ import com.android.systemui.statusbar.NotificationShadeDepthController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.collection.NotifCollection +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController @@ -74,7 +75,6 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.ZenModeController import com.android.systemui.statusbar.window.StatusBarWindowController @@ -173,7 +173,7 @@ data class TestMocksModule( interface Bindings { @Binds fun bindStatusBarStateController( - sysuiStatusBarStateController: SysuiStatusBarStateController, + sysuiStatusBarStateController: SysuiStatusBarStateController ): StatusBarStateController } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorKosmos.kt index 4f4d1da42303..e0c0fbd7f033 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorKosmos.kt @@ -16,12 +16,14 @@ package com.android.systemui.bluetooth.qsdialog +import android.content.applicationContext import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher val Kosmos.audioSharingInteractor: AudioSharingInteractor by Kosmos.Fixture { AudioSharingInteractorImpl( + applicationContext, localBluetoothManager, bluetoothTileDialogAudioSharingRepository, testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt index e36ad427b43e..35e85bb1e68d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.brightness.ui.viewmodel import com.android.systemui.brightness.domain.interactor.brightnessPolicyEnforcementInteractor import com.android.systemui.brightness.domain.interactor.screenBrightnessInteractor +import com.android.systemui.classifier.domain.interactor.falsingInteractor import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory import com.android.systemui.kosmos.Kosmos import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor @@ -33,6 +34,7 @@ val Kosmos.brightnessSliderViewModelFactory: BrightnessSliderViewModel.Factory b hapticsViewModelFactory = sliderHapticsViewModelFactory, brightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor, supportsMirroring = allowsMirroring, + falsingInteractor = falsingInteractor, brightnessWarningToast = brightnessWarningToast, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt index 78ea70086605..ddcc92691f5b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt @@ -57,6 +57,10 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository { addDisplay(display(type, id = displayId)) } + suspend fun addDisplays(vararg displays: Display) { + displays.forEach { addDisplay(it) } + } + suspend fun addDisplay(display: Display) { flow.value += display displayAdditionEventFlow.emit(display) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelKosmos.kt index b24b3ad05117..71746b505a48 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/dreams/ui/viewmodel/DreamUserActionsViewModelKosmos.kt @@ -16,6 +16,7 @@ package com.android.systemui.dreams.ui.viewmodel +import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.shade.domain.interactor.shadeInteractor @@ -23,6 +24,7 @@ import com.android.systemui.shade.domain.interactor.shadeInteractor val Kosmos.dreamUserActionsViewModel by Kosmos.Fixture { DreamUserActionsViewModel( + communalInteractor = communalInteractor, deviceUnlockedInteractor = deviceUnlockedInteractor, shadeInteractor = shadeInteractor, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt index 721c0b8339db..2c8581608739 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt @@ -102,10 +102,7 @@ val Kosmos.defaultShortcutCategoriesRepository by ) } -val Kosmos.inputGestureMaps by - Kosmos.Fixture { - InputGestureMaps(applicationContext) - } +val Kosmos.inputGestureMaps by Kosmos.Fixture { InputGestureMaps(applicationContext) } val Kosmos.customShortcutCategoriesRepository by Kosmos.Fixture { @@ -116,7 +113,7 @@ val Kosmos.customShortcutCategoriesRepository by testDispatcher, shortcutCategoriesUtils, applicationContext, - inputGestureMaps + inputGestureMaps, ) } 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 007d2297e387..f88ed07046ab 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 @@ -16,18 +16,24 @@ package com.android.systemui.keyguard.domain.interactor +import com.android.internal.widget.lockPatternUtils import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.keyguardRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.user.domain.interactor.selectedUserInteractor val Kosmos.keyguardEnabledInteractor by Kosmos.Fixture { KeyguardEnabledInteractor( applicationCoroutineScope, + testDispatcher, keyguardRepository, biometricSettingsRepository, - keyguardDismissTransitionInteractor, + selectedUserInteractor, + lockPatternUtils, + { keyguardDismissTransitionInteractor }, internalTransitionInteractor = internalKeyguardTransitionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt index 39236c7c9fae..2423949cdacc 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt @@ -24,5 +24,6 @@ val Kosmos.keyguardLockWhileAwakeInteractor by KeyguardLockWhileAwakeInteractor( biometricSettingsRepository = biometricSettingsRepository, keyguardEnabledInteractor = keyguardEnabledInteractor, + keyguardServiceLockNowInteractor = keyguardServiceLockNowInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.kt new file mode 100644 index 000000000000..29335c590a75 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardServiceLockNowInteractor.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.keyguard.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope + +val Kosmos.keyguardServiceLockNowInteractor by + Kosmos.Fixture { KeyguardServiceLockNowInteractor(backgroundScope = testScope) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt index 63e168d018bf..4aa132c1d3af 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt @@ -41,5 +41,7 @@ val Kosmos.keyguardWakeDirectlyToGoneInteractor by lockPatternUtils, fakeSettings, selectedUserInteractor, + keyguardEnabledInteractor, + keyguardServiceLockNowInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt index b5e6f75c7915..c0b39b1df7d5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.ui.viewmodel +import android.content.applicationContext import android.content.res.mainResources import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor @@ -28,12 +29,13 @@ import com.android.systemui.statusbar.ui.systemBarUtilsProxy val Kosmos.keyguardClockViewModel by Kosmos.Fixture { KeyguardClockViewModel( + context = applicationContext, keyguardClockInteractor = keyguardClockInteractor, applicationScope = applicationCoroutineScope, aodNotificationIconViewModel = notificationIconContainerAlwaysOnDisplayViewModel, shadeInteractor = shadeInteractor, systemBarUtils = systemBarUtilsProxy, configurationInteractor = configurationInteractor, - resources = mainResources + resources = mainResources, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt index a25b29fd18cb..2311c0a23db8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt @@ -20,6 +20,7 @@ import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor val Kosmos.lockscreenUserActionsViewModel by Fixture { @@ -27,5 +28,6 @@ val Kosmos.lockscreenUserActionsViewModel by Fixture { deviceEntryInteractor = deviceEntryInteractor, communalInteractor = communalInteractor, shadeInteractor = shadeInteractor, + occlusionInteractor = sceneContainerOcclusionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt index 72cb1dfe38db..1556058d51ba 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt @@ -1,8 +1,12 @@ package com.android.systemui.kosmos import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.FlowValue import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.settings.brightness.ui.BrightnessWarningToast +import com.android.systemui.util.mockito.mock import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.test.StandardTestDispatcher @@ -32,12 +36,15 @@ fun Kosmos.useStandardTestDispatcher() = apply { testDispatcher = StandardTestDi fun Kosmos.useUnconfinedTestDispatcher() = apply { testDispatcher = UnconfinedTestDispatcher() } var Kosmos.testScope by Fixture { TestScope(testDispatcher) } -var Kosmos.applicationCoroutineScope by Fixture { testScope.backgroundScope } +var Kosmos.backgroundScope by Fixture { testScope.backgroundScope } +var Kosmos.applicationCoroutineScope by Fixture { backgroundScope } var Kosmos.testCase: SysuiTestCase by Fixture() var Kosmos.backgroundCoroutineContext: CoroutineContext by Fixture { - testScope.backgroundScope.coroutineContext + backgroundScope.coroutineContext } var Kosmos.mainCoroutineContext: CoroutineContext by Fixture { testScope.coroutineContext } +var Kosmos.brightnessWarningToast: BrightnessWarningToast by + Kosmos.Fixture { mock<BrightnessWarningToast>() } /** * Run this test body with a [Kosmos] as receiver, and using the [testScope] currently installed in @@ -49,3 +56,5 @@ fun Kosmos.runTest(testBody: suspend Kosmos.() -> Unit) = fun Kosmos.runCurrent() = testScope.runCurrent() fun <T> Kosmos.collectLastValue(flow: Flow<T>) = testScope.collectLastValue(flow) + +fun <T> Kosmos.collectValues(flow: Flow<T>): FlowValue<List<T>> = testScope.collectValues(flow) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt index d631f926176d..e6256a58a365 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt @@ -17,6 +17,7 @@ package com.android.systemui.mediaprojection.data.repository import android.app.ActivityManager +import android.media.projection.StopReason import com.android.systemui.mediaprojection.data.model.MediaProjectionState import kotlinx.coroutines.flow.MutableStateFlow @@ -28,7 +29,7 @@ class FakeMediaProjectionRepository : MediaProjectionRepository { var stopProjectingInvoked = false - override suspend fun stopProjecting() { + override suspend fun stopProjecting(@StopReason stopReason: Int) { stopProjectingInvoked = true } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt index 562980d43485..06822a6c2339 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeQSTile.kt @@ -19,10 +19,12 @@ package com.android.systemui.qs import com.android.internal.logging.InstanceId import com.android.systemui.animation.Expandable import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.plugins.qs.TileDetailsViewModel class FakeQSTile(var user: Int, var available: Boolean = true) : QSTile { private var tileSpec: String? = null var destroyed = false + var hasDetailsViewModel: Boolean = true private var state = QSTile.State() val callbacks = mutableListOf<QSTile.Callback>() @@ -91,6 +93,13 @@ class FakeQSTile(var user: Int, var available: Boolean = true) : QSTile { return false } + override fun getDetailsViewModel(): FakeTileDetailsViewModel? { + if (hasDetailsViewModel) { + return FakeTileDetailsViewModel(tileSpec) + } + return null + } + fun changeState(newState: QSTile.State) { state = newState callbacks.forEach { it.onStateChanged(state) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeTileDetailsViewModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeTileDetailsViewModel.kt new file mode 100644 index 000000000000..555f019822c2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeTileDetailsViewModel.kt @@ -0,0 +1,48 @@ +/* + * 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.qs + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import com.android.systemui.plugins.qs.TileDetailsViewModel + +class FakeTileDetailsViewModel(var tileSpec: String?) : TileDetailsViewModel() { + private var _clickOnSettingsButton = 0 + + @Composable + override fun GetContentView() { + Text( + text = "Fake details content", + textAlign = TextAlign.Center, + fontWeight = FontWeight.ExtraBold, + ) + } + + override fun clickOnSettingsButton() { + _clickOnSettingsButton++ + } + + override fun getTitle(): String { + return tileSpec ?: " Fake title" + } + + override fun getSubTitle(): String { + return tileSpec ?: "Fake sub title" + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt index 45d5b387fea0..c574463eb258 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt @@ -18,10 +18,12 @@ package com.android.systemui.qs.composefragment.viewmodel import android.content.res.mainResources import androidx.lifecycle.LifecycleCoroutineScope +import com.android.systemui.classifier.domain.interactor.falsingInteractor import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.log.table.logcatTableLogBuffer import com.android.systemui.media.controls.ui.view.qqsMediaHost import com.android.systemui.media.controls.ui.view.qsMediaHost import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment @@ -57,7 +59,9 @@ val Kosmos.qsFragmentComposeViewModelFactory by configurationInteractor, largeScreenHeaderHelper, tileSquishinessInteractor, + falsingInteractor, inFirstPageViewModel, + logcatTableLogBuffer(this@Fixture), mediaInRowInLandscapeViewModelFactory, qqsMediaHost, qsMediaHost, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt index b5a6bf126719..6fe860cfd0d3 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt @@ -19,12 +19,14 @@ package com.android.systemui.qs.panels.domain.interactor import com.android.systemui.haptics.msdl.tileHapticsViewModelFactoryProvider import com.android.systemui.kosmos.Kosmos import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout +import com.android.systemui.qs.panels.ui.viewmodel.detailsViewModel import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.infiniteGridViewModelFactory val Kosmos.infiniteGridLayout by Kosmos.Fixture { InfiniteGridLayout( + detailsViewModel, iconTilesViewModel, infiniteGridViewModelFactory, tileHapticsViewModelFactoryProvider, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt new file mode 100644 index 000000000000..dc22905ba320 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt @@ -0,0 +1,22 @@ +/* + * 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.qs.panels.ui.viewmodel + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor + +val Kosmos.detailsViewModel by Kosmos.Fixture { DetailsViewModel(currentTilesInteractor) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt new file mode 100644 index 000000000000..b8d3ff425f20 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.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.qs.panels.ui.viewmodel + +import com.android.systemui.classifier.domain.interactor.falsingInteractor +import com.android.systemui.kosmos.Kosmos + +val Kosmos.editModeButtonViewModelFactory by + Kosmos.Fixture { + object : EditModeButtonViewModel.Factory { + override fun create(): EditModeButtonViewModel { + return EditModeButtonViewModel(editModeViewModel, falsingInteractor) + } + } + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt index 2e80293eafff..5c71ba2f8bbd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt @@ -16,6 +16,7 @@ package com.android.systemui.qs.panels.ui.viewmodel +import com.android.systemui.classifier.domain.interactor.falsingInteractor import com.android.systemui.development.ui.viewmodel.buildNumberViewModelFactory import com.android.systemui.kosmos.Kosmos import com.android.systemui.qs.panels.domain.interactor.paginatedGridInteractor @@ -28,5 +29,7 @@ val Kosmos.paginatedGridViewModel by paginatedGridInteractor, inFirstPageViewModel, buildNumberViewModelFactory, + editModeButtonViewModelFactory, + falsingInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt index a90876551d20..de9f629ef787 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.tiles.viewmodel import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.util.mockito.mock val Kosmos.qsTileViewModelAdaperFactory by @@ -28,6 +29,7 @@ val Kosmos.qsTileViewModelAdaperFactory by applicationCoroutineScope, mock(), qsTileViewModel, + testDispatcher, ) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt index ce103ec57a86..6afc0d803f8d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.ui.viewmodel import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModelFactory import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.panels.ui.viewmodel.detailsViewModel import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel import com.android.systemui.qs.panels.ui.viewmodel.quickQuickSettingsViewModelFactory import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel @@ -34,6 +35,7 @@ val Kosmos.quickSettingsContainerViewModelFactory by supportsBrightnessMirroring, tileGridViewModel, editModeViewModel, + detailsViewModel, ) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/DisabledContentInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/DisabledContentInteractorKosmos.kt new file mode 100644 index 000000000000..12d4e90dc469 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/DisabledContentInteractorKosmos.kt @@ -0,0 +1,25 @@ +/* + * 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.scene.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor + +val Kosmos.disabledContentInteractor by Fixture { + DisabledContentInteractor(disableFlagsInteractor = disableFlagsInteractor) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt index f84c3bdfdaf1..eb352baab0e4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt @@ -33,5 +33,6 @@ val Kosmos.sceneInteractor: SceneInteractor by sceneFamilyResolvers = { sceneFamilyResolvers }, deviceUnlockedInteractor = { deviceUnlockedInteractor }, keyguardEnabledInteractor = { keyguardEnabledInteractor }, + disabledContentInteractor = disabledContentInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt index 7e6a7271c561..82b5f6332b23 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt @@ -37,6 +37,7 @@ import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.testScope import com.android.systemui.model.sysUiState import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.scene.domain.interactor.disabledContentInteractor import com.android.systemui.scene.domain.interactor.sceneBackInteractor import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor @@ -83,5 +84,6 @@ val Kosmos.sceneContainerStartable by Fixture { alternateBouncerInteractor = alternateBouncerInteractor, vibratorHelper = vibratorHelper, msdlPlayer = msdlPlayer, + disabledContentInteractor = disabledContentInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt new file mode 100644 index 000000000000..dbaa0b1d5bf6 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt @@ -0,0 +1,34 @@ +/* + * 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.shade.data.repository + +import android.view.Display +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.shade.display.ShadeDisplayPolicy +import com.android.systemui.shade.display.SpecificDisplayIdPolicy + +val Kosmos.defaultShadeDisplayPolicy: ShadeDisplayPolicy by + Kosmos.Fixture { SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY) } + +val Kosmos.shadeDisplaysRepository: MutableShadeDisplaysRepository by + Kosmos.Fixture { + ShadeDisplaysRepositoryImpl( + defaultPolicy = defaultShadeDisplayPolicy, + bgScope = testScope.backgroundScope, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelKosmos.kt index 7097d3130aa0..694bb6e87ef6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModelKosmos.kt @@ -26,6 +26,7 @@ import com.android.systemui.qs.ui.adapter.qsSceneAdapter import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModelFactory import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor val Kosmos.shadeSceneContentViewModel: ShadeSceneContentViewModel by Fixture { @@ -35,6 +36,7 @@ val Kosmos.shadeSceneContentViewModel: ShadeSceneContentViewModel by Fixture { brightnessMirrorViewModelFactory = brightnessMirrorViewModelFactory, mediaCarouselInteractor = mediaCarouselInteractor, shadeInteractor = shadeInteractor, + disableFlagsInteractor = disableFlagsInteractor, footerActionsViewModelFactory = footerActionsViewModelFactory, footerActionsController = footerActionsController, unfoldTransitionInteractor = unfoldTransitionInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt index 980d65fde6de..7de22d8c8b43 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/FakeHeadsUpRowRepository.kt @@ -16,13 +16,27 @@ package com.android.systemui.statusbar.notification.data.repository +import com.android.systemui.statusbar.notification.headsup.PinnedStatus import kotlinx.coroutines.flow.MutableStateFlow class FakeHeadsUpRowRepository(override val key: String, override val elementKey: Any = Any()) : HeadsUpRowRepository { - constructor(key: String, isPinned: Boolean) : this(key = key) { - this.isPinned.value = isPinned + constructor( + key: String, + elementKey: Any = Any(), + isPinned: Boolean, + ) : this(key = key, elementKey = elementKey) { + this.pinnedStatus.value = if (isPinned) PinnedStatus.PinnedBySystem else PinnedStatus.NotPinned } - override val isPinned: MutableStateFlow<Boolean> = MutableStateFlow(false) + constructor( + key: String, + elementKey: Any = Any(), + pinnedStatus: PinnedStatus, + ) : this(key = key, elementKey = elementKey) { + this.pinnedStatus.value = pinnedStatus + } + + override val pinnedStatus: MutableStateFlow<PinnedStatus> = + MutableStateFlow(PinnedStatus.NotPinned) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/HeadsUpManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt index a4db00c9b6ae..de9485d48383 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/HeadsUpManagerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.notification.headsup import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt new file mode 100644 index 000000000000..88caf6e2ba30 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.promoted + +import com.android.systemui.statusbar.notification.collection.NotificationEntry + +class FakePromotedNotificationsProvider : PromotedNotificationsProvider { + val promotedEntries = mutableSetOf<NotificationEntry>() + + override fun shouldPromote(entry: NotificationEntry): Boolean { + return promotedEntries.contains(entry) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt new file mode 100644 index 000000000000..5e9f12b4b1cc --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.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.notification.promoted + +import android.content.applicationContext +import com.android.systemui.kosmos.Kosmos + +var Kosmos.promotedNotificationContentExtractor by + Kosmos.Fixture { + PromotedNotificationContentExtractor( + promotedNotificationsProvider, + applicationContext, + promotedNotificationLogger, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLoggerKosmos.kt new file mode 100644 index 000000000000..2805d1e02356 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLoggerKosmos.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.notification.promoted + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.log.logcatLogBuffer + +val Kosmos.promotedNotificationLogger by + Kosmos.Fixture { PromotedNotificationLogger(logcatLogBuffer("PromotedNotifLog")) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt index a7aa0b41a7aa..580f617a3cdf 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt @@ -18,4 +18,5 @@ package com.android.systemui.statusbar.notification.promoted import com.android.systemui.kosmos.Kosmos -val Kosmos.promotedNotificationsProvider by Kosmos.Fixture { PromotedNotificationsProviderImpl() } +var Kosmos.promotedNotificationsProvider: PromotedNotificationsProvider by + Kosmos.Fixture { PromotedNotificationsProviderImpl() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt index 31d390862540..2d3f68faf4b7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt @@ -36,6 +36,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.log.logcatLogBuffer import com.android.systemui.media.controls.util.MediaFeatureFlag import com.android.systemui.media.dialog.MediaOutputDialogManager import com.android.systemui.plugins.ActivityStarter @@ -60,9 +61,13 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.icon.IconBuilder import com.android.systemui.statusbar.notification.icon.IconManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLogger +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProviderImpl import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.CoordinateOnClickListener import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener @@ -77,7 +82,6 @@ import com.android.systemui.statusbar.notification.row.shared.NotificationRowCon import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.KeyguardDismissUtil -import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.SmartActionInflaterImpl import com.android.systemui.statusbar.policy.SmartReplyConstants import com.android.systemui.statusbar.policy.SmartReplyInflaterImpl @@ -221,6 +225,17 @@ class ExpandableNotificationRowBuilder( Mockito.mock(ConversationNotificationManager::class.java, STUB_ONLY), ) + val promotedNotificationsProvider = PromotedNotificationsProviderImpl() + val promotedNotificationLog = logcatLogBuffer("PromotedNotifLog") + val promotedNotificationLogger = PromotedNotificationLogger(promotedNotificationLog) + + val promotedNotificationContentExtractor = + PromotedNotificationContentExtractor( + promotedNotificationsProvider, + context, + promotedNotificationLogger, + ) + mContentBinder = if (NotificationRowContentBinderRefactor.isEnabled) NotificationRowContentBinderImpl( @@ -231,6 +246,7 @@ class ExpandableNotificationRowBuilder( smartReplyStateInflater, notifLayoutInflaterFactoryProvider, Mockito.mock(HeadsUpStyleProvider::class.java, STUB_ONLY), + promotedNotificationContentExtractor, Mockito.mock(NotificationRowContentBinderLogger::class.java, STUB_ONLY), ) else @@ -243,6 +259,7 @@ class ExpandableNotificationRowBuilder( smartReplyStateInflater, notifLayoutInflaterFactoryProvider, Mockito.mock(HeadsUpStyleProvider::class.java, STUB_ONLY), + promotedNotificationContentExtractor, Mockito.mock(NotificationRowContentBinderLogger::class.java, STUB_ONLY), ) mContentBinder.setInflateSynchronously(true) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt index 1e3897ba46c6..ec54c33f1156 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt @@ -22,7 +22,7 @@ import com.android.systemui.statusbar.notification.collection.coordinator.visual import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl import com.android.systemui.statusbar.notification.collection.notifCollection import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider -import com.android.systemui.statusbar.policy.headsUpManager +import com.android.systemui.statusbar.notification.headsup.headsUpManager var Kosmos.onUserInteractionCallback: OnUserInteractionCallback by Kosmos.Fixture { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt index e20ce270fc14..a5c4bfd61945 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.stack import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.statusbar.policy.AvalancheController +import com.android.systemui.statusbar.notification.headsup.AvalancheController import com.android.systemui.util.mockito.mock var Kosmos.stackScrollAlgorithmSectionProvider by Fixture { @@ -29,6 +29,4 @@ var Kosmos.stackScrollAlgorithmBypassController by Fixture { mock<StackScrollAlgorithm.BypassController>() } -var Kosmos.avalancheController by Fixture { - mock<AvalancheController>() -} +var Kosmos.avalancheController by Fixture { mock<AvalancheController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt index 9c3f510c8585..e972c2c77902 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt @@ -25,7 +25,7 @@ import com.android.systemui.scene.data.repository.windowRootViewVisibilityReposi import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor -import com.android.systemui.statusbar.policy.headsUpManager +import com.android.systemui.statusbar.notification.headsup.headsUpManager val Kosmos.windowRootViewVisibilityInteractor by Fixture { WindowRootViewVisibilityInteractor( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt index 090ce31bd43c..951ae59ebcf1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/AutoHideKosmos.kt @@ -16,9 +16,36 @@ package com.android.systemui.statusbar.phone +import android.content.Context +import android.os.Handler +import android.view.IWindowManager +import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.display.data.repository.fakeDisplayWindowPropertiesRepository import com.android.systemui.kosmos.Kosmos -import com.android.systemui.util.mockito.mock +import com.android.systemui.kosmos.applicationCoroutineScope +import org.mockito.Mockito.mock -val Kosmos.mockAutoHideController by Kosmos.Fixture { mock<AutoHideController>() } +val Kosmos.mockAutoHideController: AutoHideController by + Kosmos.Fixture { mock(AutoHideController::class.java) } var Kosmos.autoHideController by Kosmos.Fixture { mockAutoHideController } + +val Kosmos.fakeAutoHideControllerFactory by Kosmos.Fixture { FakeAutoHideControllerFactory() } + +val Kosmos.multiDisplayAutoHideControllerStore by + Kosmos.Fixture { + MultiDisplayAutoHideControllerStore( + applicationCoroutineScope, + displayRepository, + fakeDisplayWindowPropertiesRepository, + fakeAutoHideControllerFactory, + ) + } + +class FakeAutoHideControllerFactory : + AutoHideControllerImpl.Factory(mock(Handler::class.java), mock(IWindowManager::class.java)) { + + override fun create(context: Context): AutoHideControllerImpl { + return mock(AutoHideControllerImpl::class.java) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt index 4a6757db9481..f86824a1af6d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt @@ -26,10 +26,10 @@ import com.android.systemui.keyguard.wakefulnessLifecycle import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.shade.domain.interactor.shadeLockscreenInteractor +import com.android.systemui.statusbar.notification.headsup.headsUpManager import com.android.systemui.statusbar.notificationShadeWindowController import com.android.systemui.statusbar.policy.batteryController import com.android.systemui.statusbar.policy.deviceProvisionedController -import com.android.systemui.statusbar.policy.headsUpManager import com.android.systemui.statusbar.pulseExpansionHandler import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt index c6684af9d5eb..6083414240a6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt @@ -36,6 +36,7 @@ import com.android.systemui.shade.shadeController import com.android.systemui.statusbar.commandQueue import com.android.systemui.statusbar.notification.collection.provider.launchFullScreenIntentProvider import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider +import com.android.systemui.statusbar.notification.headsup.headsUpManager import com.android.systemui.statusbar.notification.notificationTransitionAnimatorControllerProvider import com.android.systemui.statusbar.notification.row.onUserInteractionCallback import com.android.systemui.statusbar.notificationClickNotifier @@ -43,7 +44,6 @@ import com.android.systemui.statusbar.notificationLockscreenUserManager import com.android.systemui.statusbar.notificationPresenter import com.android.systemui.statusbar.notificationRemoteInputManager import com.android.systemui.statusbar.notificationShadeWindowController -import com.android.systemui.statusbar.policy.headsUpManager import com.android.systemui.statusbar.policy.keyguardStateController import com.android.systemui.wmshell.bubblesManager import java.util.Optional diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt index 87ea1473bba1..6eb6fe87b6c1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerKosmos.kt @@ -24,12 +24,11 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.notification.headsup.headsUpManager import com.android.systemui.statusbar.notificationShadeWindowController import com.android.systemui.statusbar.policy.configurationController -import com.android.systemui.statusbar.policy.headsUpManager import com.android.systemui.util.kotlin.JavaAdapter import com.android.systemui.util.mockito.mock -import org.mockito.Mockito.mock var Kosmos.statusBarTouchableRegionManager by Kosmos.Fixture { diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp index a462297c07af..03ef4e67af1c 100644 --- a/packages/Vcn/service-b/Android.bp +++ b/packages/Vcn/service-b/Android.bp @@ -19,6 +19,19 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } +filegroup { + name: "vcn-location-sources", + srcs: select(release_flag("RELEASE_MOVE_VCN_TO_MAINLINE"), { + true: [ + "vcn-location-flag/module/com/android/server/vcn/VcnLocation.java", + ], + default: [ + "vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java", + ], + }), + visibility: ["//frameworks/base/services/core"], +} + java_library { name: "service-connectivity-b-pre-jarjar", sdk_version: "system_server_current", diff --git a/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java b/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java new file mode 100644 index 000000000000..6c7d24db8bac --- /dev/null +++ b/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn; + +/** + * Class to represent that VCN is in a mainline module + * + * <p>This class is used to check whether VCN is in the non-updatable platform or in a mainline + * module. + */ +// When VCN is in a mainline module, this class (module/com/android/server/vcn/VcnLocation.java) +// will be built in to the vcn-location-sources filegroup. When VCN is in the non-updatable +// platform, platform/com/android/server/vcn/VcnLocation.java will be built in to the filegroup +public class VcnLocation { + /** Indicate that VCN is the platform */ + public static final boolean IS_VCN_IN_MAINLINE = true; +} diff --git a/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java b/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java new file mode 100644 index 000000000000..c6c82a55de0f --- /dev/null +++ b/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java @@ -0,0 +1,32 @@ +/* + * 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.vcn; + +/** + * Class to represent that VCN is in the platform + * + * <p>This class is used to check whether VCN is in the non-updatable platform or in a mainline + * module. + */ +// When VCN is in a mainline module, module/com/android/server/vcn/VcnLocation.java +// will be built in to the vcn-location-sources filegroup. When VCN is in the non-updatable +// platform, this class (platform/com/android/server/vcn/VcnLocation.java) will be built in to the +// filegroup +public class VcnLocation { + /** Indicate that VCN is the platform */ + public static final boolean IS_VCN_IN_MAINLINE = false; +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java index 9b71f8050c80..de3c5f2c23ab 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java @@ -133,9 +133,6 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName()); - // This is needed to make AndroidJUnit4ClassRunner happy. - InstrumentationRegistry.registerInstance(null, Bundle.EMPTY); - // Hook point to allow more customization. runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null); diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java deleted file mode 100644 index 870a10a1f57e..000000000000 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.platform.test.ravenwood; - -import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import android.annotation.Nullable; -import android.app.ResourcesManager; -import android.content.res.Resources; -import android.view.DisplayAdjustments; - -import java.io.File; -import java.util.HashMap; - -/** - * Used to store various states associated with {@link RavenwoodConfig} that's inly needed - * in junit-impl. - * - * We don't want to put it in junit-src to avoid having to recompile all the downstream - * dependencies after changing this class. - * - * All members must be called from the runner's main thread. - */ -public class RavenwoodConfigState { - private static final String TAG = "RavenwoodConfigState"; - - private final RavenwoodConfig mConfig; - - // TODO: Move the other contexts from RavenwoodConfig to here too? They're used by - // RavenwoodRule too, but RavenwoodRule can probably use InstrumentationRegistry? - RavenwoodContext mSystemServerContext; - - public RavenwoodConfigState(RavenwoodConfig config) { - mConfig = config; - } - - /** Map from path -> resources. */ - private final HashMap<File, Resources> mCachedResources = new HashMap<>(); - - /** - * Load {@link Resources} from an APK, with cache. - */ - public Resources loadResources(@Nullable File apkPath) { - var cached = mCachedResources.get(apkPath); - if (cached != null) { - return cached; - } - - var fileToLoad = apkPath != null ? apkPath : new File(RAVENWOOD_EMPTY_RESOURCES_APK); - - assertTrue("File " + fileToLoad + " doesn't exist.", fileToLoad.isFile()); - - final String path = fileToLoad.getAbsolutePath(); - final var emptyPaths = new String[0]; - - ResourcesManager.getInstance().initializeApplicationPaths(path, emptyPaths); - - final var ret = ResourcesManager.getInstance().getResources(null, path, - emptyPaths, emptyPaths, emptyPaths, - emptyPaths, null, null, - new DisplayAdjustments().getCompatibilityInfo(), - RavenwoodRuntimeEnvironmentController.class.getClassLoader(), null); - - assertNotNull(ret); - - mCachedResources.put(apkPath, ret); - return ret; - } -} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java index ec00e8fea887..110de989b524 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java @@ -15,22 +15,10 @@ */ package android.platform.test.ravenwood; -import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicMember; - -import static org.junit.Assert.fail; - -import android.annotation.Nullable; import android.util.Log; -import com.android.ravenwood.common.RavenwoodRuntimeException; - -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.rules.TestRule; import org.junit.runner.Description; -import java.lang.reflect.Field; - /** * Used to store various states associated with the current test runner that's inly needed * in junit-impl. @@ -52,207 +40,35 @@ public final class RavenwoodRunnerState { mRunner = runner; } - /** - * The RavenwoodConfig used to configure the current Ravenwood environment. - * This can either come from mConfig or mRule. - */ - private RavenwoodConfig mCurrentConfig; - /** - * The RavenwoodConfig declared in the test class - */ - private RavenwoodConfig mConfig; - /** - * The RavenwoodRule currently in effect, declared in the test class - */ - private RavenwoodRule mRule; - private boolean mHasRavenwoodRule; private Description mMethodDescription; - public RavenwoodConfig getConfig() { - return mCurrentConfig; - } - public void enterTestRunner() { Log.i(TAG, "enterTestRunner: " + mRunner); - - mHasRavenwoodRule = hasRavenwoodRule(mRunner.mTestJavaClass); - mConfig = extractConfiguration(mRunner.mTestJavaClass); - - if (mConfig != null) { - if (mHasRavenwoodRule) { - fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class." - + " Suggest migrating to RavenwoodConfig."); - } - mCurrentConfig = mConfig; - } else if (!mHasRavenwoodRule) { - // If no RavenwoodConfig and no RavenwoodRule, use a default config - mCurrentConfig = new RavenwoodConfig.Builder().build(); - } - - if (mCurrentConfig != null) { - RavenwoodRuntimeEnvironmentController.init(mRunner); - } + RavenwoodRuntimeEnvironmentController.initForRunner(); } public void enterTestClass() { Log.i(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName()); - - if (mCurrentConfig != null) { - RavenwoodRuntimeEnvironmentController.init(mRunner); - } } public void exitTestClass() { Log.i(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName()); - try { - if (mCurrentConfig != null) { - RavenwoodRuntimeEnvironmentController.reset(); - } - } finally { - mConfig = null; - mRule = null; - } + RavenwoodRuntimeEnvironmentController.exitTestClass(); } public void enterTestMethod(Description description) { mMethodDescription = description; + RavenwoodRuntimeEnvironmentController.initForMethod(); } public void exitTestMethod() { mMethodDescription = null; - RavenwoodRuntimeEnvironmentController.reinit(); } public void enterRavenwoodRule(RavenwoodRule rule) { - if (!mHasRavenwoodRule) { - fail("If you have a RavenwoodRule in your test, make sure the field type is" - + " RavenwoodRule so Ravenwood can detect it."); - } - if (mRule != null) { - fail("Multiple nesting RavenwoodRule's are detected in the same class," - + " which is not supported."); - } - mRule = rule; - if (mCurrentConfig == null) { - mCurrentConfig = rule.getConfiguration(); - } - RavenwoodRuntimeEnvironmentController.init(mRunner); + RavenwoodRuntimeEnvironmentController.setSystemProperties(rule.mSystemProperties); } public void exitRavenwoodRule(RavenwoodRule rule) { - if (mRule != rule) { - fail("RavenwoodRule did not take effect."); - } - mRule = null; - } - - /** - * @return a configuration from a test class, if any. - */ - @Nullable - private static RavenwoodConfig extractConfiguration(Class<?> testClass) { - var field = findConfigurationField(testClass); - if (field == null) { - return null; - } - - try { - return (RavenwoodConfig) field.get(null); - } catch (IllegalAccessException e) { - throw new RavenwoodRuntimeException("Failed to fetch from the configuration field", e); - } - } - - /** - * @return true if the current target class (or its super classes) has any @Rule / @ClassRule - * fields of type RavenwoodRule. - * - * Note, this check won't detect cases where a Rule is of type - * {@link TestRule} and still be a {@link RavenwoodRule}. But that'll be detected at runtime - * as a failure, in {@link #enterRavenwoodRule}. - */ - private static boolean hasRavenwoodRule(Class<?> testClass) { - for (var field : testClass.getDeclaredFields()) { - if (!field.isAnnotationPresent(Rule.class) - && !field.isAnnotationPresent(ClassRule.class)) { - continue; - } - if (field.getType().equals(RavenwoodRule.class)) { - return true; - } - } - // JUnit supports rules as methods, so we need to check them too. - for (var method : testClass.getDeclaredMethods()) { - if (!method.isAnnotationPresent(Rule.class) - && !method.isAnnotationPresent(ClassRule.class)) { - continue; - } - if (method.getReturnType().equals(RavenwoodRule.class)) { - return true; - } - } - // Look into the super class. - if (!testClass.getSuperclass().equals(Object.class)) { - return hasRavenwoodRule(testClass.getSuperclass()); - } - return false; - } - - /** - * Find and return a field with @RavenwoodConfig.Config, which must be of type - * RavenwoodConfig. - */ - @Nullable - private static Field findConfigurationField(Class<?> testClass) { - Field foundField = null; - - for (var field : testClass.getDeclaredFields()) { - final var hasAnot = field.isAnnotationPresent(RavenwoodConfig.Config.class); - final var isType = field.getType().equals(RavenwoodConfig.class); - - if (hasAnot) { - if (isType) { - // Good, use this field. - if (foundField != null) { - fail(String.format( - "Class %s has multiple fields with %s", - testClass.getCanonicalName(), - "@RavenwoodConfig.Config")); - } - // Make sure it's static public - ensureIsPublicMember(field, true); - - foundField = field; - } else { - fail(String.format( - "Field %s.%s has %s but type is not %s", - testClass.getCanonicalName(), - field.getName(), - "@RavenwoodConfig.Config", - "RavenwoodConfig")); - return null; // unreachable - } - } else { - if (isType) { - fail(String.format( - "Field %s.%s does not have %s but type is %s", - testClass.getCanonicalName(), - field.getName(), - "@RavenwoodConfig.Config", - "RavenwoodConfig")); - return null; // unreachable - } else { - // Unrelated field, ignore. - continue; - } - } - } - if (foundField != null) { - return foundField; - } - if (!testClass.getSuperclass().equals(Object.class)) { - return findConfigurationField(testClass.getSuperclass()); - } - return null; } } diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java index 979076eaabfd..c2ed45d8f427 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -16,8 +16,11 @@ package android.platform.test.ravenwood; +import static android.os.Process.FIRST_APPLICATION_UID; +import static android.os.UserHandle.SYSTEM; import static android.platform.test.ravenwood.RavenwoodSystemServer.ANDROID_PACKAGE_NAME; +import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_RESOURCE_APK; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING; @@ -25,7 +28,9 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSIO import static com.android.ravenwood.common.RavenwoodCommonUtils.parseNullableInt; import static com.android.ravenwood.common.RavenwoodCommonUtils.withDefault; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -53,6 +58,7 @@ import android.provider.DeviceConfig_host; import android.system.ErrnoException; import android.system.Os; import android.util.Log; +import android.view.DisplayAdjustments; import androidx.test.platform.app.InstrumentationRegistry; @@ -62,7 +68,6 @@ import com.android.internal.os.RuntimeInit; import com.android.ravenwood.RavenwoodRuntimeNative; import com.android.ravenwood.RavenwoodRuntimeState; import com.android.ravenwood.common.RavenwoodCommonUtils; -import com.android.ravenwood.common.RavenwoodRuntimeException; import com.android.ravenwood.common.SneakyThrow; import com.android.server.LocalServices; import com.android.server.compat.PlatformCompat; @@ -74,8 +79,10 @@ import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.Random; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -85,8 +92,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; /** - * Responsible for initializing and de-initializing the environment, according to a - * {@link RavenwoodConfig}. + * Responsible for initializing and the environment. */ public class RavenwoodRuntimeEnvironmentController { private static final String TAG = "RavenwoodRuntimeEnvironmentController"; @@ -113,8 +119,6 @@ public class RavenwoodRuntimeEnvironmentController { private static ScheduledFuture<?> sPendingTimeout; - private static long sOriginalIdentityToken = -1; - /** * When enabled, attempt to detect uncaught exceptions from background threads. */ @@ -147,6 +151,10 @@ public class RavenwoodRuntimeEnvironmentController { return res; } + /** Map from path -> resources. */ + private static final HashMap<File, Resources> sCachedResources = new HashMap<>(); + private static Set<String> sAdoptedPermissions = Collections.emptySet(); + private static final Object sInitializationLock = new Object(); @GuardedBy("sInitializationLock") @@ -155,15 +163,18 @@ public class RavenwoodRuntimeEnvironmentController { @GuardedBy("sInitializationLock") private static Throwable sExceptionFromGlobalInit; - private static RavenwoodAwareTestRunner sRunner; private static RavenwoodSystemProperties sProps; private static final int DEFAULT_TARGET_SDK_LEVEL = VERSION_CODES.CUR_DEVELOPMENT; private static final String DEFAULT_PACKAGE_NAME = "com.android.ravenwoodtests.defaultname"; + private static final int sMyPid = new Random().nextInt(100, 32768); private static int sTargetSdkLevel; private static String sTestPackageName; private static String sTargetPackageName; + private static Instrumentation sInstrumentation; + private static final long sCallingIdentity = + packBinderIdentityToken(false, FIRST_APPLICATION_UID, sMyPid); /** * Initialize the global environment. @@ -182,7 +193,7 @@ public class RavenwoodRuntimeEnvironmentController { Log.e(TAG, "globalInit() failed", th); sExceptionFromGlobalInit = th; - throw th; + SneakyThrow.sneakyThrow(th); } } else { // Subsequent calls. If the first call threw, just throw the same error, to prevent @@ -197,10 +208,13 @@ public class RavenwoodRuntimeEnvironmentController { } } - private static void globalInitInner() { + private static void globalInitInner() throws IOException { if (RAVENWOOD_VERBOSE_LOGGING) { Log.v(TAG, "globalInit() called here...", new RuntimeException("NOT A CRASH")); } + if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) { + Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler); + } // Some process-wide initialization. (maybe redirect stdout/stderr) RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME); @@ -251,82 +265,32 @@ public class RavenwoodRuntimeEnvironmentController { loadRavenwoodProperties(); assertMockitoVersion(); - } - - private static void loadRavenwoodProperties() { - var props = RavenwoodSystemProperties.readProperties("ravenwood.properties"); - - sTargetSdkLevel = withDefault( - parseNullableInt(props.get("targetSdkVersionInt")), DEFAULT_TARGET_SDK_LEVEL); - sTargetPackageName = withDefault(props.get("packageName"), DEFAULT_PACKAGE_NAME); - sTestPackageName = withDefault(props.get("instPackageName"), sTargetPackageName); - - // TODO(b/377765941) Read them from the manifest too? - } - - /** - * Initialize the environment. - */ - public static void init(RavenwoodAwareTestRunner runner) { - if (RAVENWOOD_VERBOSE_LOGGING) { - Log.v(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE")); - } - if (sRunner == runner) { - return; - } - if (sRunner != null) { - reset(); - } - sRunner = runner; - try { - initInner(runner.mState.getConfig()); - } catch (Exception th) { - Log.e(TAG, "init() failed", th); - - RavenwoodCommonUtils.runIgnoringException(()-> reset()); - - SneakyThrow.sneakyThrow(th); - } - } - - private static void initInner(RavenwoodConfig config) throws IOException { - if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) { - maybeThrowPendingUncaughtException(false); - Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler); - } - - config.mTargetPackageName = sTargetPackageName; - config.mTestPackageName = sTestPackageName; - config.mTargetSdkLevel = sTargetSdkLevel; Log.i(TAG, "TargetPackageName=" + sTargetPackageName); Log.i(TAG, "TestPackageName=" + sTestPackageName); Log.i(TAG, "TargetSdkLevel=" + sTargetSdkLevel); - RavenwoodRuntimeState.sUid = config.mUid; - RavenwoodRuntimeState.sPid = config.mPid; - RavenwoodRuntimeState.sTargetSdkLevel = config.mTargetSdkLevel; - sOriginalIdentityToken = Binder.clearCallingIdentity(); - reinit(); - setSystemProperties(config.mSystemProperties); + RavenwoodRuntimeState.sUid = FIRST_APPLICATION_UID; + RavenwoodRuntimeState.sPid = sMyPid; + RavenwoodRuntimeState.sTargetSdkLevel = sTargetSdkLevel; ServiceManager.init$ravenwood(); LocalServices.removeAllServicesForTest(); - ActivityManager.init$ravenwood(config.mCurrentUser); + ActivityManager.init$ravenwood(SYSTEM.getIdentifier()); final var main = new HandlerThread(MAIN_THREAD_NAME); main.start(); Looper.setMainLooperForTest(main.getLooper()); final boolean isSelfInstrumenting = - Objects.equals(config.mTestPackageName, config.mTargetPackageName); + Objects.equals(sTestPackageName, sTargetPackageName); // This will load the resources from the apk set to `resource_apk` in the build file. // This is supposed to be the "target app"'s resources. final Supplier<Resources> targetResourcesLoader = () -> { var file = new File(RAVENWOOD_RESOURCE_APK); - return config.mState.loadResources(file.exists() ? file : null); + return loadResources(file.exists() ? file : null); }; // Set up test context's (== instrumentation context's) resources. @@ -337,18 +301,17 @@ public class RavenwoodRuntimeEnvironmentController { } else { instResourcesLoader = () -> { var file = new File(RAVENWOOD_INST_RESOURCE_APK); - return config.mState.loadResources(file.exists() ? file : null); + return loadResources(file.exists() ? file : null); }; } var instContext = new RavenwoodContext( - config.mTestPackageName, main, instResourcesLoader); + sTestPackageName, main, instResourcesLoader); var targetContext = new RavenwoodContext( - config.mTargetPackageName, main, targetResourcesLoader); + sTargetPackageName, main, targetResourcesLoader); // Set up app context. - var appContext = new RavenwoodContext( - config.mTargetPackageName, main, targetResourcesLoader); + var appContext = new RavenwoodContext(sTargetPackageName, main, targetResourcesLoader); appContext.setApplicationContext(appContext); if (isSelfInstrumenting) { instContext.setApplicationContext(appContext); @@ -357,42 +320,65 @@ public class RavenwoodRuntimeEnvironmentController { // When instrumenting into another APK, the test context doesn't have an app context. targetContext.setApplicationContext(appContext); } - config.mInstContext = instContext; - config.mTargetContext = targetContext; - final Supplier<Resources> systemResourcesLoader = () -> config.mState.loadResources(null); + final Supplier<Resources> systemResourcesLoader = () -> loadResources(null); - config.mState.mSystemServerContext = + var systemServerContext = new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader); - // Prepare other fields. - config.mInstrumentation = new Instrumentation(); - config.mInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation()); - InstrumentationRegistry.registerInstance(config.mInstrumentation, Bundle.EMPTY); + sInstrumentation = new Instrumentation(); + sInstrumentation.basicInit(instContext, targetContext, null); + InstrumentationRegistry.registerInstance(sInstrumentation, Bundle.EMPTY); + + RavenwoodSystemServer.init(systemServerContext); + + initializeCompatIds(); + } + + private static void loadRavenwoodProperties() { + var props = RavenwoodSystemProperties.readProperties("ravenwood.properties"); + + sTargetSdkLevel = withDefault( + parseNullableInt(props.get("targetSdkVersionInt")), DEFAULT_TARGET_SDK_LEVEL); + sTargetPackageName = withDefault(props.get("packageName"), DEFAULT_PACKAGE_NAME); + sTestPackageName = withDefault(props.get("instPackageName"), sTargetPackageName); + + // TODO(b/377765941) Read them from the manifest too? + } - RavenwoodSystemServer.init(config); + /** + * Partially reset and initialize before each test class invocation + */ + public static void initForRunner() { + var targetContext = sInstrumentation.getTargetContext(); + var instContext = sInstrumentation.getContext(); + // We need to recreate the mock UiAutomation for each test class, because sometimes tests + // will call Mockito.framework().clearInlineMocks() after execution. + sInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation()); - initializeCompatIds(config); + Process_ravenwood.reset(); + DeviceConfig_host.reset(); + Binder.restoreCallingIdentity(sCallingIdentity); if (ENABLE_TIMEOUT_STACKS) { sPendingTimeout = sTimeoutExecutor.schedule( RavenwoodRuntimeEnvironmentController::dumpStacks, TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); } + if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) { + maybeThrowPendingUncaughtException(false); + } } /** - * Partially re-initialize after each test method invocation + * Partially reset and initialize before each test method invocation */ - public static void reinit() { - // sRunner could be null, if there was a failure in the initialization. - if (sRunner != null) { - var config = sRunner.mState.getConfig(); - Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid)); - } + public static void initForMethod() { + // TODO(b/375272444): this is a hacky workaround to ensure binder identity + Binder.restoreCallingIdentity(sCallingIdentity); } - private static void initializeCompatIds(RavenwoodConfig config) { + private static void initializeCompatIds() { // Set up compat-IDs for the app side. // TODO: Inside the system server, all the compat-IDs should be enabled, // Due to the `AppCompatCallbacks.install(new long[0], new long[0])` call in @@ -400,8 +386,8 @@ public class RavenwoodRuntimeEnvironmentController { // Compat framework only uses the package name and the target SDK level. ApplicationInfo appInfo = new ApplicationInfo(); - appInfo.packageName = config.mTargetPackageName; - appInfo.targetSdkVersion = config.mTargetSdkLevel; + appInfo.packageName = sTargetPackageName; + appInfo.targetSdkVersion = sTargetSdkLevel; PlatformCompat platformCompat = null; try { @@ -418,65 +404,42 @@ public class RavenwoodRuntimeEnvironmentController { } /** - * De-initialize. - * - * Note, we call this method when init() fails too, so this method should deal with - * any partially-initialized states. + * Load {@link Resources} from an APK, with cache. */ - public static void reset() { - if (RAVENWOOD_VERBOSE_LOGGING) { - Log.v(TAG, "reset() called here", new RuntimeException("STACKTRACE")); - } - if (sRunner == null) { - throw new RavenwoodRuntimeException("Internal error: reset() already called"); + private static Resources loadResources(@Nullable File apkPath) { + var cached = sCachedResources.get(apkPath); + if (cached != null) { + return cached; } - var config = sRunner.mState.getConfig(); - sRunner = null; - if (ENABLE_TIMEOUT_STACKS) { - sPendingTimeout.cancel(false); - } + var fileToLoad = apkPath != null ? apkPath : new File(RAVENWOOD_EMPTY_RESOURCES_APK); - RavenwoodSystemServer.reset(config); + assertTrue("File " + fileToLoad + " doesn't exist.", fileToLoad.isFile()); - InstrumentationRegistry.registerInstance(null, Bundle.EMPTY); - config.mInstrumentation = null; - if (config.mInstContext != null) { - ((RavenwoodContext) config.mInstContext).cleanUp(); - config.mInstContext = null; - } - if (config.mTargetContext != null) { - ((RavenwoodContext) config.mTargetContext).cleanUp(); - config.mTargetContext = null; - } - if (config.mState.mSystemServerContext != null) { - config.mState.mSystemServerContext.cleanUp(); - } + final String path = fileToLoad.getAbsolutePath(); + final var emptyPaths = new String[0]; - if (Looper.getMainLooper() != null) { - Looper.getMainLooper().quit(); - } - Looper.clearMainLooperForTest(); + ResourcesManager.getInstance().initializeApplicationPaths(path, emptyPaths); - ActivityManager.reset$ravenwood(); + final var ret = ResourcesManager.getInstance().getResources(null, path, + emptyPaths, emptyPaths, emptyPaths, + emptyPaths, null, null, + new DisplayAdjustments().getCompatibilityInfo(), + RavenwoodRuntimeEnvironmentController.class.getClassLoader(), null); - LocalServices.removeAllServicesForTest(); - ServiceManager.reset$ravenwood(); + assertNotNull(ret); - setSystemProperties(null); - if (sOriginalIdentityToken != -1) { - Binder.restoreCallingIdentity(sOriginalIdentityToken); - } - RavenwoodRuntimeState.reset(); - Process_ravenwood.reset(); - DeviceConfig_host.reset(); + sCachedResources.put(apkPath, ret); + return ret; + } - try { - ResourcesManager.setInstance(null); // Better structure needed. - } catch (Exception e) { - // AOSP-CHANGE: AOSP doesn't support resources yet. + /** + * A callback when a test class finishes its execution, mostly only for debugging. + */ + public static void exitTestClass() { + if (ENABLE_TIMEOUT_STACKS) { + sPendingTimeout.cancel(false); } - if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) { maybeThrowPendingUncaughtException(true); } @@ -524,7 +487,7 @@ public class RavenwoodRuntimeEnvironmentController { /** * Set the current configuration to the actual SystemProperties. */ - private static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) { + public static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) { SystemProperties.clearChangeCallbacksForTest(); RavenwoodRuntimeNative.clearSystemProperties(); if (systemProperties == null) systemProperties = new RavenwoodSystemProperties(); @@ -558,28 +521,28 @@ public class RavenwoodRuntimeEnvironmentController { // TODO: use the real UiAutomation class instead of a mock private static UiAutomation createMockUiAutomation() { - final Set[] adoptedPermission = { Collections.emptySet() }; + sAdoptedPermissions = Collections.emptySet(); var mock = mock(UiAutomation.class, inv -> { HostTestUtils.onThrowMethodCalled(); return null; }); doAnswer(inv -> { - adoptedPermission[0] = UiAutomation.ALL_PERMISSIONS; + sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; return null; }).when(mock).adoptShellPermissionIdentity(); doAnswer(inv -> { if (inv.getArgument(0) == null) { - adoptedPermission[0] = UiAutomation.ALL_PERMISSIONS; + sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; } else { - adoptedPermission[0] = Set.of(inv.getArguments()); + sAdoptedPermissions = (Set) Set.of(inv.getArguments()); } return null; }).when(mock).adoptShellPermissionIdentity(any()); doAnswer(inv -> { - adoptedPermission[0] = Collections.emptySet(); + sAdoptedPermissions = Collections.emptySet(); return null; }).when(mock).dropShellPermissionIdentity(); - doAnswer(inv -> adoptedPermission[0]).when(mock).getAdoptedShellPermissions(); + doAnswer(inv -> sAdoptedPermissions).when(mock).getAdoptedShellPermissions(); return mock; } diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java index 438a2bfa7a14..3346635b7f7b 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java @@ -33,7 +33,7 @@ import com.android.server.compat.PlatformCompat; import com.android.server.compat.PlatformCompatNative; import com.android.server.utils.TimingsTraceAndSlog; -import java.util.List; +import java.util.Collection; import java.util.Set; public class RavenwoodSystemServer { @@ -68,27 +68,24 @@ public class RavenwoodSystemServer { private static TimingsTraceAndSlog sTimings; private static SystemServiceManager sServiceManager; - public static void init(RavenwoodConfig config) { + public static void init(Context systemServerContext) { // Always start PlatformCompat, regardless of the requested services. // PlatformCompat is not really a SystemService, so it won't receive boot phases / etc. // This initialization code is copied from SystemServer.java. - PlatformCompat platformCompat = new PlatformCompat(config.mState.mSystemServerContext); + PlatformCompat platformCompat = new PlatformCompat(systemServerContext); ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat); ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE, new PlatformCompatNative(platformCompat)); - // Avoid overhead if no services required - if (config.mServicesRequired.isEmpty()) return; - sStartedServices = new ArraySet<>(); sTimings = new TimingsTraceAndSlog(); - sServiceManager = new SystemServiceManager(config.mState.mSystemServerContext); + sServiceManager = new SystemServiceManager(systemServerContext); sServiceManager.setStartInfo(false, SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); LocalServices.addService(SystemServiceManager.class, sServiceManager); - startServices(config.mServicesRequired); + startServices(sKnownServices.keySet()); sServiceManager.sealStartedServices(); // TODO: expand to include additional boot phases when relevant @@ -96,7 +93,7 @@ public class RavenwoodSystemServer { sServiceManager.startBootPhase(sTimings, SystemService.PHASE_BOOT_COMPLETED); } - public static void reset(RavenwoodConfig config) { + public static void reset() { // TODO: consider introducing shutdown boot phases LocalServices.removeServiceForTest(SystemServiceManager.class); @@ -105,7 +102,7 @@ public class RavenwoodSystemServer { sStartedServices = null; } - private static void startServices(List<Class<?>> serviceClasses) { + private static void startServices(Collection<Class<?>> serviceClasses) { for (Class<?> serviceClass : serviceClasses) { // Quietly ignore duplicate requests if service already started if (sStartedServices.contains(serviceClass)) continue; diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java index 7ca9239d2062..3ed0f50434fb 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java @@ -15,21 +15,13 @@ */ package android.platform.test.ravenwood; -import static android.os.Process.FIRST_APPLICATION_UID; -import static android.os.UserHandle.SYSTEM; - import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.Instrumentation; -import android.content.Context; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; /** * @deprecated This class will be removed. Reach out to g/ravenwood if you need any features in it. @@ -45,37 +37,10 @@ public final class RavenwoodConfig { public @interface Config { } - private static final int NOBODY_UID = 9999; - - private static final AtomicInteger sNextPid = new AtomicInteger(100); - - int mCurrentUser = SYSTEM.getIdentifier(); - - /** - * Unless the test author requests differently, run as "nobody", and give each collection of - * tests its own unique PID. - */ - int mUid = FIRST_APPLICATION_UID; - int mPid = sNextPid.getAndIncrement(); - - String mTestPackageName; - String mTargetPackageName; - - int mTargetSdkLevel; - - final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties(); - - final List<Class<?>> mServicesRequired = new ArrayList<>(); - - volatile Context mInstContext; - volatile Context mTargetContext; - volatile Instrumentation mInstrumentation; - /** * Stores internal states / methods associated with this config that's only needed in * junit-impl. */ - final RavenwoodConfigState mState = new RavenwoodConfigState(this); private RavenwoodConfig() { } @@ -159,34 +124,11 @@ public final class RavenwoodConfig { return this; } - Builder setSystemPropertyImmutableReal(@NonNull String key, - @Nullable Object value) { - mConfig.mSystemProperties.setValue(key, value); - mConfig.mSystemProperties.setAccessReadOnly(key); - return this; - } - - Builder setSystemPropertyMutableReal(@NonNull String key, - @Nullable Object value) { - mConfig.mSystemProperties.setValue(key, value); - mConfig.mSystemProperties.setAccessReadWrite(key); - return this; - } - /** - * Configure the set of system services that are required for this test to operate. - * - * For example, passing {@code android.hardware.SerialManager.class} as an argument will - * ensure that the underlying service is created, initialized, and ready to use for the - * duration of the test. The {@code SerialManager} instance can be obtained via - * {@code RavenwoodRule.getContext()} and {@code Context.getSystemService()}, and - * {@code SerialManagerInternal} can be obtained via {@code LocalServices.getService()}. + * @deprecated no longer used. All supported services are available. */ + @Deprecated public Builder setServicesRequired(@NonNull Class<?>... services) { - mConfig.mServicesRequired.clear(); - for (Class<?> service : services) { - mConfig.mServicesRequired.add(service); - } return this; } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index 5681a9040f63..6262ad160c0f 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -92,19 +92,11 @@ public final class RavenwoodRule implements TestRule { } } - private final RavenwoodConfig mConfiguration; - - public RavenwoodRule() { - mConfiguration = new RavenwoodConfig.Builder().build(); - } - - private RavenwoodRule(RavenwoodConfig config) { - mConfiguration = config; - } + final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties(); public static class Builder { - private final RavenwoodConfig.Builder mBuilder = - new RavenwoodConfig.Builder(); + + private final RavenwoodRule mRule = new RavenwoodRule(); public Builder() { } @@ -152,7 +144,8 @@ public final class RavenwoodRule implements TestRule { * Has no effect on non-Ravenwood environments. */ public Builder setSystemPropertyImmutable(@NonNull String key, @Nullable Object value) { - mBuilder.setSystemPropertyImmutableReal(key, value); + mRule.mSystemProperties.setValue(key, value); + mRule.mSystemProperties.setAccessReadOnly(key); return this; } @@ -167,26 +160,21 @@ public final class RavenwoodRule implements TestRule { * Has no effect on non-Ravenwood environments. */ public Builder setSystemPropertyMutable(@NonNull String key, @Nullable Object value) { - mBuilder.setSystemPropertyMutableReal(key, value); + mRule.mSystemProperties.setValue(key, value); + mRule.mSystemProperties.setAccessReadWrite(key); return this; } /** - * Configure the set of system services that are required for this test to operate. - * - * For example, passing {@code android.hardware.SerialManager.class} as an argument will - * ensure that the underlying service is created, initialized, and ready to use for the - * duration of the test. The {@code SerialManager} instance can be obtained via - * {@code RavenwoodRule.getContext()} and {@code Context.getSystemService()}, and - * {@code SerialManagerInternal} can be obtained via {@code LocalServices.getService()}. + * @deprecated no longer used. All supported services are available. */ + @Deprecated public Builder setServicesRequired(@NonNull Class<?>... services) { - mBuilder.setServicesRequired(services); return this; } public RavenwoodRule build() { - return new RavenwoodRule(mBuilder.build()); + return mRule; } } @@ -227,7 +215,7 @@ public final class RavenwoodRule implements TestRule { @Override public Statement apply(Statement base, Description description) { - if (!RavenwoodConfig.isOnRavenwood()) { + if (!IS_ON_RAVENWOOD) { return base; } return new Statement() { @@ -296,8 +284,4 @@ public final class RavenwoodRule implements TestRule { public static RavenwoodPrivate private$ravenwood() { return sRavenwoodPrivate; } - - RavenwoodConfig getConfiguration() { - return mConfiguration; - } } diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java deleted file mode 100644 index c25d2b4cbc4d..000000000000 --- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.ravenwoodtest.bivalenttest; - -import android.platform.test.ravenwood.RavenwoodConfig; -import android.platform.test.ravenwood.RavenwoodRule; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.junit.Assume; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; - -/** - * Make sure having multiple RavenwoodRule's is detected. - * (But only when running on ravenwod. Otherwise it'll be ignored.) - */ -@RunWith(AndroidJUnit4.class) -public class RavenwoodMultipleRuleTest { - - @Rule(order = Integer.MIN_VALUE) - public final ExpectedException mExpectedException = ExpectedException.none(); - - @Rule - public final RavenwoodRule mRavenwood1 = new RavenwoodRule(); - - @Rule - public final RavenwoodRule mRavenwood2 = new RavenwoodRule(); - - public RavenwoodMultipleRuleTest() { - // We can't call it within the test method because the exception happens before - // calling the method, so set it up here. - if (RavenwoodConfig.isOnRavenwood()) { - mExpectedException.expectMessage("Multiple nesting RavenwoodRule"); - } - } - - @Test - public void testMultipleRulesNotAllowed() { - Assume.assumeTrue(RavenwoodConfig.isOnRavenwood()); - } -} diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java new file mode 100644 index 000000000000..f9e73db23740 --- /dev/null +++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java @@ -0,0 +1,103 @@ +/* + * 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.runnercallbacktests; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.SystemProperties; +import android.platform.test.annotations.NoRavenizer; +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; + +/** + * Test for RavenwoodRule. + */ +@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner. +public class RavenwoodRuleValidationTest extends RavenwoodRunnerTestBase { + + public static class RuleInBaseClass { + static String PROPERTY_KEY = "debug.ravenwood.prop.in.base"; + static String PROPERTY_VAL = "ravenwood"; + @Rule + public final RavenwoodRule mRavenwood1 = new RavenwoodRule.Builder() + .setSystemPropertyImmutable(PROPERTY_KEY, PROPERTY_VAL).build(); + } + + /** + * Make sure that RavenwoodRule in a base class takes effect. + */ + @RunWith(AndroidJUnit4.class) + // CHECKSTYLE:OFF + @Expected(""" + testRunStarted: classes + testSuiteStarted: classes + testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest + testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest) + testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest) + testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest + testSuiteFinished: classes + testRunFinished: 1,0,0,0 + """) + // CHECKSTYLE:ON + public static class RuleInBaseClassSuccessTest extends RuleInBaseClass { + + @Test + public void testRuleInBaseClass() { + assertThat(SystemProperties.get(PROPERTY_KEY)).isEqualTo(PROPERTY_VAL); + } + } + + /** + * Same as {@link RuleInBaseClass}, but the type of the rule field is not {@link RavenwoodRule}. + */ + public abstract static class RuleWithDifferentTypeInBaseClass { + static String PROPERTY_KEY = "debug.ravenwood.prop.in.base.different.type"; + static String PROPERTY_VAL = "ravenwood"; + @Rule + public final TestRule mRavenwood1 = new RavenwoodRule.Builder() + .setSystemPropertyImmutable(PROPERTY_KEY, PROPERTY_VAL).build(); + } + + /** + * Make sure that RavenwoodRule in a base class takes effect, even if the field type is not + */ + @RunWith(AndroidJUnit4.class) + // CHECKSTYLE:OFF + @Expected(""" + testRunStarted: classes + testSuiteStarted: classes + testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest + testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest) + testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest) + testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest + testSuiteFinished: classes + testRunFinished: 1,0,0,0 + """) + // CHECKSTYLE:ON + public static class RuleWithDifferentTypeInBaseClassSuccessTest extends RuleWithDifferentTypeInBaseClass { + + @Test + public void testRuleInBaseClass() { + assertThat(SystemProperties.get(PROPERTY_KEY)).isEqualTo(PROPERTY_VAL); + } + } +} diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java deleted file mode 100644 index f94b98bc1fb8..000000000000 --- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java +++ /dev/null @@ -1,484 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.ravenwoodtest.runnercallbacktests; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.NoRavenizer; -import android.platform.test.ravenwood.RavenwoodConfig; -import android.platform.test.ravenwood.RavenwoodRule; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; -import org.junit.runner.RunWith; - - -/** - * Test for @Config field extraction and validation. - * - * TODO(b/377765941) Most of the tests here will be obsolete and deleted with b/377765941, but - * some of the tests may need to be re-implemented one way or another. (e.g. the package name - * test.) Until that happens, we'll keep all tests here but add an {@code @Ignore} instead. - */ -@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner. -public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase { - public abstract static class ConfigInBaseClass { - static String PACKAGE_NAME = "com.ConfigInBaseClass"; - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder() - .setPackageName(PACKAGE_NAME).build(); - } - - /** - * Make sure a config in the base class is detected. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest - testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest) - testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest - testSuiteFinished: classes - testRunFinished: 1,0,0,0 - """) - // CHECKSTYLE:ON - @Ignore // Package name is no longer set via config. - public static class ConfigInBaseClassTest extends ConfigInBaseClass { - @Test - public void test() { - assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName()) - .isEqualTo(PACKAGE_NAME); - } - } - - /** - * Make sure a config in the base class is detected. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest - testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest) - testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest - testSuiteFinished: classes - testRunFinished: 1,0,0,0 - """) - // CHECKSTYLE:ON - @Ignore // Package name is no longer set via config. - public static class ConfigOverridingTest extends ConfigInBaseClass { - static String PACKAGE_NAME_OVERRIDE = "com.ConfigOverridingTest"; - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder() - .setPackageName(PACKAGE_NAME_OVERRIDE).build(); - - @Test - public void test() { - assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName()) - .isEqualTo(PACKAGE_NAME_OVERRIDE); - } - } - - /** - * Test to make sure that if a test has a config error, the failure would be reported from - * each test method. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) - testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class ErrorMustBeReportedFromEachTest { - @RavenwoodConfig.Config - private static RavenwoodConfig sConfig = // Invalid because it's private. - new RavenwoodConfig.Builder().build(); - - @Test - public void testMethod1() { - } - - @Test - public void testMethod2() { - } - - @Test - public void testMethod3() { - } - } - - /** - * Invalid because there are two @Config's. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest) - testFailure: Class com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.DuplicateConfigTest has multiple fields with @RavenwoodConfig.Config - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class DuplicateConfigTest { - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig1 = - new RavenwoodConfig.Builder().build(); - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig2 = - new RavenwoodConfig.Builder().build(); - - @Test - public void testConfig() { - } - } - - /** - * @Config's must be static. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest) - testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest.sConfig expected to be public static - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class NonStaticConfigTest { - - @RavenwoodConfig.Config - public RavenwoodConfig sConfig = - new RavenwoodConfig.Builder().build(); - - @Test - public void testConfig() { - } - } - - /** - * @Config's must be public. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest) - testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest.sConfig expected to be public static - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class NonPublicConfigTest { - - @RavenwoodConfig.Config - RavenwoodConfig sConfig = - new RavenwoodConfig.Builder().build(); - - @Test - public void testConfig() { - } - } - - /** - * @Config's must be of type RavenwoodConfig. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest) - testFailure: Field com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.WrongTypeConfigTest.sConfig has @RavenwoodConfig.Config but type is not RavenwoodConfig - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class WrongTypeConfigTest { - - @RavenwoodConfig.Config - public static Object sConfig = - new RavenwoodConfig.Builder().build(); - - @Test - public void testConfig() { - } - - } - - /** - * @Rule must be of type RavenwoodRule. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest - testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest) - testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it. - testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class WrongTypeRuleTest { - - @Rule - public TestRule mRule = new RavenwoodRule.Builder().build(); - - @Test - public void testConfig() { - } - - } - - /** - * Config can't be used with a (instance) Rule. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest) - testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig. - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class WithInstanceRuleTest { - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = - new RavenwoodConfig.Builder().build(); - - @Rule - public RavenwoodRule mRule = new RavenwoodRule.Builder().build(); - - @Test - public void testConfig() { - } - } - - /** - * Config can't be used with a (static) Rule. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest) - testFailure: Failed to instantiate class androidx.test.ext.junit.runners.AndroidJUnit4 - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class WithStaticRuleTest { - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = - new RavenwoodConfig.Builder().build(); - - @Rule - public static RavenwoodRule sRule = new RavenwoodRule.Builder().build(); - - @Test - public void testConfig() { - } - } - - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest - testStarted: testMultipleRulesNotAllowed(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest) - testFailure: Multiple nesting RavenwoodRule's are detected in the same class, which is not supported. - testFinished: testMultipleRulesNotAllowed(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class DuplicateRulesTest { - - @Rule - public final RavenwoodRule mRavenwood1 = new RavenwoodRule(); - - @Rule - public final RavenwoodRule mRavenwood2 = new RavenwoodRule(); - - @Test - public void testMultipleRulesNotAllowed() { - } - } - - public static class RuleInBaseClass { - static String PACKAGE_NAME = "com.RuleInBaseClass"; - @Rule - public final RavenwoodRule mRavenwood1 = new RavenwoodRule.Builder() - .setPackageName(PACKAGE_NAME).build(); - } - - /** - * Make sure that RavenwoodRule in a base class takes effect. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest - testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest) - testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest - testSuiteFinished: classes - testRunFinished: 1,0,0,0 - """) - // CHECKSTYLE:ON - @Ignore // Package name is no longer set via config. - public static class RuleInBaseClassSuccessTest extends RuleInBaseClass { - - @Test - public void testRuleInBaseClass() { - assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName()) - .isEqualTo(PACKAGE_NAME); - } - } - - /** - * Make sure that having a config and a rule in a base class should fail. - * RavenwoodRule. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest) - testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig. - testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest) - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class ConfigWithRuleInBaseClassTest extends RuleInBaseClass { - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder().build(); - - @Test - public void test() { - } - } - - /** - * Same as {@link RuleInBaseClass}, but the type of the rule field is not {@link RavenwoodRule}. - */ - public abstract static class RuleWithDifferentTypeInBaseClass { - static String PACKAGE_NAME = "com.RuleWithDifferentTypeInBaseClass"; - @Rule - public final TestRule mRavenwood1 = new RavenwoodRule.Builder() - .setPackageName(PACKAGE_NAME).build(); - } - - /** - * Make sure that RavenwoodRule in a base class takes effect, even if the field type is not - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest - testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest) - testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it. - testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - @Ignore // Package name is no longer set via config. - public static class RuleWithDifferentTypeInBaseClassSuccessTest extends RuleWithDifferentTypeInBaseClass { - - @Test - public void testRuleInBaseClass() { - assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName()) - .isEqualTo(PACKAGE_NAME); - } - } - - /** - * Make sure that having a config and a rule in a base class should fail, even if the field type is not - * RavenwoodRule. - */ - @RunWith(AndroidJUnit4.class) - // CHECKSTYLE:OFF - @Expected(""" - testRunStarted: classes - testSuiteStarted: classes - testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest - testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest) - testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it. - testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest) - testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest - testSuiteFinished: classes - testRunFinished: 1,1,0,0 - """) - // CHECKSTYLE:ON - public static class ConfigWithRuleWithDifferentTypeInBaseClassTest extends RuleWithDifferentTypeInBaseClass { - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder().build(); - - @Test - public void test() { - } - } -} diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java index 8e04b698c9d9..271c27f6ae93 100644 --- a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java @@ -15,7 +15,6 @@ */ package com.android.ravenwoodtest.runtimetest; -import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static android.os.Process.FIRST_APPLICATION_UID; import static org.junit.Assert.assertEquals; @@ -23,7 +22,6 @@ import static org.junit.Assert.assertEquals; import android.os.Binder; import android.os.Build; import android.os.Process; -import android.platform.test.ravenwood.RavenwoodConfig; import android.system.Os; import com.android.ravenwood.RavenwoodRuntimeState; @@ -34,13 +32,6 @@ import org.junit.Test; public class IdentityTest { - @RavenwoodConfig.Config - public static final RavenwoodConfig sConfig = - new RavenwoodConfig.Builder() - .setTargetSdkLevel(UPSIDE_DOWN_CAKE) - .setProcessApp() - .build(); - @Test public void testUid() { assertEquals(FIRST_APPLICATION_UID, RavenwoodRuntimeState.sUid); @@ -60,7 +51,7 @@ public class IdentityTest { @Test public void testTargetSdkLevel() { assertEquals(Build.VERSION_CODES.CUR_DEVELOPMENT, RavenwoodRuntimeState.CUR_DEVELOPMENT); - assertEquals(UPSIDE_DOWN_CAKE, RavenwoodRuntimeState.sTargetSdkLevel); - assertEquals(UPSIDE_DOWN_CAKE, VMRuntime.getRuntime().getTargetSdkVersion()); + assertEquals(RavenwoodRuntimeState.sTargetSdkLevel, + VMRuntime.getRuntime().getTargetSdkVersion()); } } diff --git a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java index 4aae1e11b72e..e83a247bd769 100644 --- a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java +++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java @@ -24,8 +24,6 @@ import static org.junit.Assert.fail; import android.content.Context; import android.hardware.SerialManager; import android.hardware.SerialManagerInternal; -import android.platform.test.ravenwood.RavenwoodConfig; -import android.platform.test.ravenwood.RavenwoodConfig.Config; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; @@ -42,12 +40,6 @@ import org.junit.runner.RunWith; public class RavenwoodServicesTest { private static final String TEST_VIRTUAL_PORT = "virtual:example"; - @Config - public static final RavenwoodConfig sRavenwood = new RavenwoodConfig.Builder() - .setProcessSystem() - .setServicesRequired(SerialManager.class) - .build(); - private Context mContext; @Before diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp index 1570549ec27d..483c4be7633b 100644 --- a/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp @@ -156,6 +156,7 @@ python_test_host { ], data: [ "golden-output/*.txt", + "golden-output.RELEASE_TARGET_JAVA_21/*.txt", ], java_data: [ "hoststubgen-test-tiny-framework-orig-dump", diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt new file mode 100644 index 000000000000..5e5ca62b49f0 --- /dev/null +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt @@ -0,0 +1,3697 @@ +## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class + Compiled from "HostSideTestClassLoadHook.java" +public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestClassLoadHook.java" +RuntimeVisibleAnnotations: + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestIgnore.class + Compiled from "HostSideTestIgnore.java" +public interface android.hosttest.annotation.HostSideTestIgnore extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestIgnore + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestIgnore.java" +RuntimeVisibleAnnotations: + x: #x(#x=[e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestKeep.class + Compiled from "HostSideTestKeep.java" +public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestKeep.java" +RuntimeVisibleAnnotations: + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRedirect.class + Compiled from "HostSideTestRedirect.java" +public interface android.hosttest.annotation.HostSideTestRedirect extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRedirect + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestRedirect.java" +RuntimeVisibleAnnotations: + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRedirectionClass.class + Compiled from "HostSideTestRedirectionClass.java" +public interface android.hosttest.annotation.HostSideTestRedirectionClass extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRedirectionClass + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestRedirectionClass.java" +RuntimeVisibleAnnotations: + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRemove.class + Compiled from "HostSideTestRemove.java" +public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRemove + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestRemove.java" +RuntimeVisibleAnnotations: + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestStaticInitializerKeep.class + Compiled from "HostSideTestStaticInitializerKeep.java" +public interface android.hosttest.annotation.HostSideTestStaticInitializerKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestStaticInitializerKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestStaticInitializerKeep.java" +RuntimeVisibleAnnotations: + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestSubstitute.class + Compiled from "HostSideTestSubstitute.java" +public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestSubstitute + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String suffix(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT +} +SourceFile: "HostSideTestSubstitute.java" +RuntimeVisibleAnnotations: + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestThrow.class + Compiled from "HostSideTestThrow.java" +public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestThrow + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestThrow.java" +RuntimeVisibleAnnotations: + x: #x(#x=[e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class + Compiled from "HostSideTestWholeClassKeep.java" +public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestWholeClassKeep.java" +RuntimeVisibleAnnotations: + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/tests/HostSideTestSuppress.class + Compiled from "HostSideTestSuppress.java" +public interface android.hosttest.annotation.tests.HostSideTestSuppress extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/tests/HostSideTestSuppress + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestSuppress.java" +RuntimeVisibleAnnotations: + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD] + ) +## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class + Compiled from "IPretendingAidl.java" +public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 3 + public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy(); + 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/IPretendingAidl$Stub$Proxy; + + public static int addTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: iload_0 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 a I +} +SourceFile: "IPretendingAidl.java" +NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl +InnerClasses: + public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl + public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub +## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class + Compiled from "IPretendingAidl.java" +public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 3 + public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub(); + 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/IPretendingAidl$Stub; + + public static int addOne(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: iload_0 + x: iconst_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 a I +} +SourceFile: "IPretendingAidl.java" +NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl +InnerClasses: + public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl + public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub +## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class + Compiled from "IPretendingAidl.java" +public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 3 +} +SourceFile: "IPretendingAidl.java" +NestMembers: + com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub + com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy +InnerClasses: + public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl + public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub +## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R$Nested + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 3 + public static int[] ARRAY; + descriptor: [I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + + public com.android.hoststubgen.test.tinyframework.R$Nested(); + 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/R$Nested; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: iconst_1 + x: newarray int + x: dup + x: iconst_0 + x: iconst_1 + x: iastore + x: putstatic #x // Field ARRAY:[I + x: return + LineNumberTable: +} +SourceFile: "R.java" +NestHost: class com/android/hoststubgen/test/tinyframework/R +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/R.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 3 + public com.android.hoststubgen.test.tinyframework.R(); + 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/R; +} +SourceFile: "R.java" +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/TinyFrameworkAnnotations.class + Compiled from "TinyFrameworkAnnotations.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 9, attributes: 2 + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations(); + 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 keep:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + 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/TinyFrameworkAnnotations; + 0 4 1 value I + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + 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/TinyFrameworkAnnotations; + 0 8 1 foo Ljava/lang/String; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRemove + + 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/TinyFrameworkAnnotations; + 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/TinyFrameworkAnnotations; + 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(#x=s#x) + android.hosttest.annotation.HostSideTestSubstitute( + suffix="_host" + ) + + private static int nativeAddThree_host(int); + descriptor: (I)I + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: iload_0 + x: iconst_3 + x: iadd + 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/TinyFrameworkAnnotations; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public int toBeIgnored(); + descriptor: ()I + 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 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/TinyFrameworkAnnotations; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestIgnore +} +SourceFile: "TinyFrameworkAnnotations.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) +## 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: 65 + 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: 2 + 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<*>;>; + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + 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/TinyFrameworkClassLoadHook; + + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: getstatic #x // Field sLoadedClasses:Ljava/util/Set; + x: aload_0 + x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z + x: pop + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class; + LocalVariableTypeTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class<*>; + Signature: #x // (Ljava/lang/Class<*>;)V + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: new #x // class java/util/HashSet + x: dup + x: invokespecial #x // Method java/util/HashSet."<init>":()V + x: putstatic #x // Field sLoadedClasses:Ljava/util/Set; + x: return + LineNumberTable: +} +SourceFile: "TinyFrameworkClassLoadHook.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## 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: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 6, attributes: 2 + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRemove + + 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 keep: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 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/TinyFrameworkClassWideAnnotations; + 0 8 1 foo Ljava/lang/String; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRemove + + 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 +} +SourceFile: "TinyFrameworkClassWideAnnotations.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class + Compiled from "TinyFrameworkClassWithInitializerDefault.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 2, attributes: 2 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.lang.Object sObject; + descriptor: Ljava/lang/Object; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault(); + 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/TinyFrameworkClassWithInitializerDefault; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: iconst_1 + x: putstatic #x // Field sInitialized:Z + x: new #x // class java/lang/Object + x: dup + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: putstatic #x // Field sObject:Ljava/lang/Object; + x: return + LineNumberTable: +} +SourceFile: "TinyFrameworkClassWithInitializerDefault.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class + Compiled from "TinyFrameworkClassWithInitializerStub.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 2, attributes: 2 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.lang.Object sObject; + descriptor: Ljava/lang/Object; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub(); + 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/TinyFrameworkClassWithInitializerStub; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: iconst_1 + x: putstatic #x // Field sInitialized:Z + x: new #x // class java/lang/Object + x: dup + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: putstatic #x // Field sObject:Ljava/lang/Object; + x: return + LineNumberTable: +} +SourceFile: "TinyFrameworkClassWithInitializerStub.java" +RuntimeInvisibleAnnotations: + x: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x() + android.hosttest.annotation.HostSideTestStaticInitializerKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class + Compiled from "TinyFrameworkEnumComplex.java" +public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex> + minor version: 0 + major version: 65 + flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + super_class: #x // java/lang/Enum + interfaces: 0, fields: 6, methods: 7, attributes: 3 + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private final java.lang.String mLongName; + descriptor: Ljava/lang/String; + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private final java.lang.String mShortName; + descriptor: Ljava/lang/String; + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES; + descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object; + x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;" + x: areturn + LineNumberTable: + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String); + descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: aload_0 + x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 name Ljava/lang/String; + MethodParameters: + Name Flags + <no name> mandated + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String); + descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + flags: (0x0002) ACC_PRIVATE + Code: + stack=3, locals=5, args_size=5 + x: aload_0 + x: aload_1 + x: iload_2 + x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V + x: aload_0 + x: aload_3 + x: putfield #x // Field mLongName:Ljava/lang/String; + x: aload_0 + x: aload 4 + x: putfield #x // Field mShortName:Ljava/lang/String; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 18 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + 0 18 3 longName Ljava/lang/String; + 0 18 4 shortName Ljava/lang/String; + MethodParameters: + Name Flags + <no name> synthetic + <no name> synthetic + <no name> + <no name> + Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public java.lang.String getLongName(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: getfield #x // Field mLongName:Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public java.lang.String getShortName(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: getfield #x // Field mShortName:Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: iconst_3 + x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: iconst_0 + x: getstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: aastore + x: dup + x: iconst_1 + x: getstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: aastore + x: dup + x: iconst_2 + x: getstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: aastore + x: areturn + LineNumberTable: + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=6, locals=0, args_size=0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: ldc #x // String RED + x: iconst_0 + x: ldc #x // String Red + x: ldc #x // String R + x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + x: putstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: ldc #x // String GREEN + x: iconst_1 + x: ldc #x // String Green + x: ldc #x // String G + x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + x: putstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: ldc #x // String BLUE + x: iconst_2 + x: ldc #x // String Blue + x: ldc #x // String B + x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + x: putstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: return + LineNumberTable: +} +Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>; +SourceFile: "TinyFrameworkEnumComplex.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class + Compiled from "TinyFrameworkEnumSimple.java" +public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple> + minor version: 0 + major version: 65 + flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + super_class: #x // java/lang/Enum + interfaces: 0, fields: 3, methods: 5, attributes: 3 + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES; + descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object; + x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;" + x: areturn + LineNumberTable: + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String); + descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: aload_0 + x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 name Ljava/lang/String; + MethodParameters: + Name Flags + <no name> mandated + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple(); + descriptor: (Ljava/lang/String;I)V + flags: (0x0002) ACC_PRIVATE + Code: + stack=3, locals=3, args_size=3 + x: aload_0 + x: aload_1 + x: iload_2 + x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 7 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + MethodParameters: + Name Flags + <no name> synthetic + <no name> synthetic + Signature: #x // ()V + + private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: iconst_2 + x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: dup + x: iconst_0 + x: getstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: aastore + x: dup + x: iconst_1 + x: getstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: aastore + x: areturn + LineNumberTable: + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: dup + x: ldc #x // String CAT + x: iconst_0 + x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V + x: putstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: dup + x: ldc #x // String DOG + x: iconst_1 + x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V + x: putstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: return + LineNumberTable: +} +Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>; +SourceFile: "TinyFrameworkEnumSimple.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class + Compiled from "TinyFrameworkExceptionTester.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 2 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester(); + 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/TinyFrameworkExceptionTester; + + public static int testException(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=0 + x: new #x // class java/lang/IllegalStateException + x: dup + x: ldc #x // String Inner exception + x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V + x: athrow + x: astore_0 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Outer exception + x: aload_0 + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V + x: athrow + Exception table: + from to target type + 0 10 10 Class java/lang/Exception + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 11 0 e Ljava/lang/Exception; + StackMapTable: number_of_entries = 1 + frame_type = 74 /* same_locals_1_stack_item */ + stack = [ class java/lang/Exception ] +} +SourceFile: "TinyFrameworkExceptionTester.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class + Compiled from "TinyFrameworkForTextPolicy.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 17, attributes: 1 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public int remove; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy(); + 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/TinyFrameworkForTextPolicy; + + 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/TinyFrameworkForTextPolicy; + 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/TinyFrameworkForTextPolicy; + 0 8 1 foo Ljava/lang/String; + + public java.lang.String toBeIgnoredObj(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 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/TinyFrameworkForTextPolicy; + + public void toBeIgnoredV(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 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/TinyFrameworkForTextPolicy; + + public boolean toBeIgnoredZ(); + descriptor: ()Z + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 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/TinyFrameworkForTextPolicy; + + public byte toBeIgnoredB(); + descriptor: ()B + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 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/TinyFrameworkForTextPolicy; + + public char toBeIgnoredC(); + descriptor: ()C + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 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/TinyFrameworkForTextPolicy; + + public short toBeIgnoredS(); + descriptor: ()S + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 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/TinyFrameworkForTextPolicy; + + public int toBeIgnoredI(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 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/TinyFrameworkForTextPolicy; + + public float toBeIgnoredF(); + descriptor: ()F + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 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/TinyFrameworkForTextPolicy; + + public double toBeIgnoredD(); + descriptor: ()D + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 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/TinyFrameworkForTextPolicy; + + 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/TinyFrameworkForTextPolicy; + 0 10 1 value I + + 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/TinyFrameworkForTextPolicy; + 0 4 1 value I + + public static native int nativeAddThree(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static int addThree_host(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: iload_0 + x: iconst_3 + x: iadd + 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/TinyFrameworkForTextPolicy; +} +SourceFile: "TinyFrameworkForTextPolicy.java" +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class + Compiled from "TinyFrameworkLambdas.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 8, attributes: 5 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested(); + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static java.lang.Integer lambda$getSupplier_static$3(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: bipush 8 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + + private static java.lang.Integer lambda$getSupplier$2(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: bipush 7 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + + private static java.lang.Integer lambda$static$1(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: bipush 6 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + + private static java.lang.Integer lambda$new$0(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: iconst_5 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: +} +SourceFile: "TinyFrameworkLambdas.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x() + android.hosttest.annotation.HostSideTestStaticInitializerKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas +BootstrapMethods: + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class + Compiled from "TinyFrameworkLambdas.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 8, attributes: 5 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas(); + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static java.lang.Integer lambda$getSupplier_static$3(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: iconst_4 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + + private static java.lang.Integer lambda$getSupplier$2(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: iconst_3 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + + private static java.lang.Integer lambda$static$1(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: iconst_2 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + + private static java.lang.Integer lambda$new$0(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: iconst_1 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: +} +SourceFile: "TinyFrameworkLambdas.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x() + android.hosttest.annotation.HostSideTestStaticInitializerKeep +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested +BootstrapMethods: + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class + Compiled from "TinyFrameworkMethodCallReplace.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo(); + 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/TinyFrameworkMethodCallReplace$ReplaceTo; + + public static void startThread(java.lang.Thread); + descriptor: (Ljava/lang/Thread;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: iconst_1 + x: invokevirtual #x // Method java/lang/Thread.setDaemon:(Z)V + x: aload_0 + x: invokevirtual #x // Method java/lang/Thread.start:()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 thread Ljava/lang/Thread; + + public static int add(int, int); + descriptor: (II)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=2, args_size=2 + x: iload_0 + x: iload_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 a I + 0 4 1 b I +} +SourceFile: "TinyFrameworkMethodCallReplace.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace +InnerClasses: + public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class + Compiled from "TinyFrameworkMethodCallReplace.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 5, attributes: 5 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace(); + 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/TinyFrameworkMethodCallReplace; + + public static boolean nonStaticMethodCallReplaceTester() throws java.lang.Exception; + descriptor: ()Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=2, args_size=0 + x: new #x // class java/util/concurrent/atomic/AtomicBoolean + x: dup + x: iconst_0 + x: invokespecial #x // Method java/util/concurrent/atomic/AtomicBoolean."<init>":(Z)V + x: astore_0 + x: new #x // class java/lang/Thread + x: dup + x: aload_0 + x: invokedynamic #x, 0 // InvokeDynamic #x:run:(Ljava/util/concurrent/atomic/AtomicBoolean;)Ljava/lang/Runnable; + x: invokespecial #x // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V + x: astore_1 + x: aload_1 + x: invokevirtual #x // Method java/lang/Thread.start:()V + x: aload_1 + x: invokevirtual #x // Method java/lang/Thread.join:()V + x: aload_0 + x: invokevirtual #x // Method java/util/concurrent/atomic/AtomicBoolean.get:()Z + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 9 27 0 ab Ljava/util/concurrent/atomic/AtomicBoolean; + 23 13 1 th Ljava/lang/Thread; + Exceptions: + throws java.lang.Exception + + public static int staticMethodCallReplaceTester(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: iconst_1 + x: iconst_2 + x: invokestatic #x // Method originalAdd:(II)I + x: ireturn + LineNumberTable: + + private static int originalAdd(int, int); + descriptor: (II)I + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=2, args_size=2 + x: iload_0 + x: iload_1 + x: iadd + x: iconst_1 + x: isub + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 a I + 0 6 1 b I + + private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean); + descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread; + x: invokevirtual #x // Method java/lang/Thread.isDaemon:()Z + x: invokevirtual #x // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 ab Ljava/util/concurrent/atomic/AtomicBoolean; +} +SourceFile: "TinyFrameworkMethodCallReplace.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo +BootstrapMethods: + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()V + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V + #x ()V +InnerClasses: + public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace + public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class + Compiled from "TinyFrameworkNative.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 14, attributes: 2 + int value; + descriptor: I + flags: (0x0000) + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative(); + 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/TinyFrameworkNative; + + public static native int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public static int nativeAddTwo_should_be_like_this(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=1, args_size=1 + x: iload_0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 arg I + + public static native long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public static long nativeLongPlus_should_be_like_this(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + x: lload_0 + x: lload_2 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + x: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 arg1 J + 0 6 2 arg2 J + + public void setValue(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: iload_1 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + 0 6 1 v I + + public native int nativeNonStaticAddToValue(int); + descriptor: (I)I + flags: (0x0101) ACC_PUBLIC, ACC_NATIVE + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public int nativeNonStaticAddToValue_should_be_like_this(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: iload_1 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + 0 6 1 arg I + + public static native void nativeStillNotSupported(); + descriptor: ()V + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public static native void nativeStillKeep(); + descriptor: ()V + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + + public static void nativeStillNotSupported_should_be_like_this(); + descriptor: ()V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: new #x // class java/lang/RuntimeException + x: dup + x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + x: athrow + LineNumberTable: + + public static native byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public void notNativeRedirected(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 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/TinyFrameworkNative; + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public static void notNativeStaticRedirected(); + descriptor: ()V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: new #x // class java/lang/RuntimeException + x: dup + x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + x: athrow + LineNumberTable: + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect +} +SourceFile: "TinyFrameworkNative.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep + x: #x(#x=s#x) + android.hosttest.annotation.HostSideTestRedirectionClass( + value="TinyFrameworkNative_host" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class + Compiled from "TinyFrameworkNative_host.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 7, attributes: 2 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host(); + 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/TinyFrameworkNative_host; + + public static int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: iload_0 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 arg I + + public static long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + x: lload_0 + x: lload_2 + x: ladd + x: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 arg1 J + 0 4 2 arg2 J + + public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: getfield #x // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I + x: iload_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + 0 7 1 arg I + + public static byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=2, args_size=2 + x: iload_0 + x: iload_1 + x: iadd + x: i2b + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 arg1 B + 0 5 1 arg2 B + + public static void notNativeRedirected(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=0, locals=1, args_size=1 + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 1 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + + public static void notNativeStaticRedirected(); + descriptor: ()V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=0, locals=0, args_size=0 + x: return + LineNumberTable: +} +SourceFile: "TinyFrameworkNative_host.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 5 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=1, locals=2, args_size=2 + 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/TinyFrameworkNestedClasses$1; + 0 5 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + MethodParameters: + Name Flags + <no name> final mandated + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_1 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 5 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2(); + descriptor: ()V + flags: (0x0000) + 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/TinyFrameworkNestedClasses$2; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_2 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 5 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=1, locals=2, args_size=2 + 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/TinyFrameworkNestedClasses$3; + 0 5 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + MethodParameters: + Name Flags + <no name> final mandated + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_3 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 5 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4(); + descriptor: ()V + flags: (0x0000) + 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/TinyFrameworkNestedClasses$4; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_4 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + 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$BaseClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iload_1 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass; + 0 10 1 x I +} +SourceFile: "TinyFrameworkNestedClasses.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass + minor version: 0 + major version: 65 + 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: 1, methods: 1, attributes: 3 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_5 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass; + 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + MethodParameters: + Name Flags + <no name> final mandated +} +SourceFile: "TinyFrameworkNestedClasses.java" +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 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 5 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1(); + descriptor: ()V + flags: (0x0000) + 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/TinyFrameworkNestedClasses$StaticNestedClass$1; + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: bipush 7 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; +} +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static +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 +## 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: 65 + 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 + minor version: 0 + major version: 65 + 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: 3 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass(); + 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 6 + 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; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; +} +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 + #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 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + interfaces: 0, fields: 0, methods: 1, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: iload_1 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass; + 0 6 1 x I +} +SourceFile: "TinyFrameworkNestedClasses.java" +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +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; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 4, attributes: 4 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + x: dup + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + x: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + x: dup + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 9 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V + x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: +} +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +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 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + 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" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 2 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect(); + 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/TinyFrameworkPackageRedirect; + + public static int foo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class com/unsupported/UnsupportedClass + x: dup + x: iload_0 + x: invokespecial #x // Method com/unsupported/UnsupportedClass."<init>":(I)V + x: invokevirtual #x // Method com/unsupported/UnsupportedClass.getValue:()I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 12 0 value I +} +SourceFile: "TinyFrameworkPackageRedirect.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class + Compiled from "TinyFrameworkRenamedClassCaller.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 2 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller(); + 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/TinyFrameworkRenamedClassCaller; + + public static int foo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed + x: dup + x: iload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed."<init>":(I)V + x: invokevirtual #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 12 0 value I +} +SourceFile: "TinyFrameworkRenamedClassCaller.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class + Compiled from "TinyFrameworkToBeRenamed.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 2 + private final int mValue; + descriptor: I + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iload_1 + x: putfield #x // Field mValue:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed; + 0 10 1 value I + + public int getValue(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: getfield #x // Field mValue:I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed; +} +SourceFile: "TinyFrameworkToBeRenamed.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.A + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.packagetest.A(); + 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/packagetest/A; +} +SourceFile: "A.java" +## Class: com/android/hoststubgen/test/tinyframework/packagetest/B.class + Compiled from "B.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.B + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/B + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.packagetest.B(); + 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/packagetest/B; +} +SourceFile: "B.java" +## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.packagetest.sub.A(); + 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/packagetest/sub/A; +} +SourceFile: "A.java" +## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/B.class + Compiled from "B.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.sub.B + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/B + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.packagetest.sub.B(); + 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/packagetest/sub/B; +} +SourceFile: "B.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class + Compiled from "C1.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.C1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1 + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.C1(); + 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/subclasstest/C1; +} +SourceFile: "C1.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class + Compiled from "C2.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1 + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.C2(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C1."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/C2; +} +SourceFile: "C2.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class + Compiled from "C3.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2 + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.C3(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C2."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/C3; +} +SourceFile: "C3.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class + Compiled from "CA.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.CA + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.CA(); + 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/subclasstest/CA; +} +SourceFile: "CA.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class + Compiled from "CB.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.CB + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.CB(); + 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/subclasstest/CB; +} +SourceFile: "CB.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class + Compiled from "Class_C1.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1 + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C1."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C1; +} +SourceFile: "Class_C1.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class + Compiled from "Class_C2.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2 + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C2."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C2; +} +SourceFile: "Class_C2.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class + Compiled from "Class_C3.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3 + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C3."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C3; +} +SourceFile: "Class_C3.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.class + Compiled from "Class_CA.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA extends com.android.hoststubgen.test.tinyframework.subclasstest.CA + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/CA."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CA; +} +SourceFile: "Class_CA.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.class + Compiled from "Class_CB.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB extends com.android.hoststubgen.test.tinyframework.subclasstest.CB + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/CB."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CB; +} +SourceFile: "Class_CB.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.class + Compiled from "Class_CB_IA.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA extends com.android.hoststubgen.test.tinyframework.subclasstest.CB implements com.android.hoststubgen.test.tinyframework.subclasstest.IA + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB + interfaces: 1, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/CB."<init>":()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA; +} +SourceFile: "Class_CB_IA.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class + Compiled from "Class_I1.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1(); + 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/subclasstest/Class_I1; +} +SourceFile: "Class_I1.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class + Compiled from "Class_I1_IA.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA + super_class: #x // java/lang/Object + interfaces: 2, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA(); + 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/subclasstest/Class_I1_IA; +} +SourceFile: "Class_I1_IA.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class + Compiled from "Class_I2.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2(); + 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/subclasstest/Class_I2; +} +SourceFile: "Class_I2.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class + Compiled from "Class_I3.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3(); + 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/subclasstest/Class_I3; +} +SourceFile: "Class_I3.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.class + Compiled from "Class_I3_IA.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I3,com.android.hoststubgen.test.tinyframework.subclasstest.IA + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA + super_class: #x // java/lang/Object + interfaces: 2, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA(); + 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/subclasstest/Class_I3_IA; +} +SourceFile: "Class_I3_IA.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.class + Compiled from "Class_IA.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.IA + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA(); + 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/subclasstest/Class_IA; +} +SourceFile: "Class_IA.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.class + Compiled from "Class_IA_I1.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.IA,com.android.hoststubgen.test.tinyframework.subclasstest.I1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1 + super_class: #x // java/lang/Object + interfaces: 2, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1(); + 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/subclasstest/Class_IA_I1; +} +SourceFile: "Class_IA_I1.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.class + Compiled from "Class_IA_I3.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.IA,com.android.hoststubgen.test.tinyframework.subclasstest.I3 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3 + super_class: #x // java/lang/Object + interfaces: 2, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3(); + 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/subclasstest/Class_IA_I3; +} +SourceFile: "Class_IA_I3.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.class + Compiled from "Class_IB.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB implements com.android.hoststubgen.test.tinyframework.subclasstest.IB + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB(); + 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/subclasstest/Class_IB; +} +SourceFile: "Class_IB.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.class + Compiled from "Class_IB_IA.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.IB,com.android.hoststubgen.test.tinyframework.subclasstest.IA + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA + super_class: #x // java/lang/Object + interfaces: 2, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA(); + 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/subclasstest/Class_IB_IA; +} +SourceFile: "Class_IB_IA.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.class + Compiled from "Class_None.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_None + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_None + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 1 + public com.android.hoststubgen.test.tinyframework.subclasstest.Class_None(); + 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/subclasstest/Class_None; +} +SourceFile: "Class_None.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class + Compiled from "I1.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1 + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1 + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 1 +} +SourceFile: "I1.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class + Compiled from "I2.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1 + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 1 +} +SourceFile: "I2.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class + Compiled from "I3.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2 + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 1 +} +SourceFile: "I3.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class + Compiled from "IA.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 1 +} +SourceFile: "IA.java" +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class + Compiled from "IB.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 1 +} +SourceFile: "IB.java" +## Class: com/supported/UnsupportedClass.class + Compiled from "UnsupportedClass.java" +public class com.supported.UnsupportedClass + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/supported/UnsupportedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 2 + private final int mValue; + descriptor: I + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + + public com.supported.UnsupportedClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iload_1 + x: putfield #x // Field mValue:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/supported/UnsupportedClass; + 0 10 1 value I + + public int getValue(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: getfield #x // Field mValue:I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/supported/UnsupportedClass; +} +SourceFile: "UnsupportedClass.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/unsupported/UnsupportedClass.class + Compiled from "UnsupportedClass.java" +public class com.unsupported.UnsupportedClass + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/unsupported/UnsupportedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 2 + public com.unsupported.UnsupportedClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String This class is not supported + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 14 0 this Lcom/unsupported/UnsupportedClass; + 0 14 1 value I + + public int getValue(); + descriptor: ()I + 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 This class is not supported + 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/unsupported/UnsupportedClass; +} +SourceFile: "UnsupportedClass.java" +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt new file mode 100644 index 000000000000..84a8373008d7 --- /dev/null +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/03-hoststubgen-test-tiny-framework-host-dump.txt @@ -0,0 +1,3727 @@ +## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class + Compiled from "HostSideTestClassLoadHook.java" +public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "HostSideTestClassLoadHook.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestKeep.class + Compiled from "HostSideTestKeep.java" +public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestKeep.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRedirect.class + Compiled from "HostSideTestRedirect.java" +public interface android.hosttest.annotation.HostSideTestRedirect extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRedirect + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestRedirect.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRedirectionClass.class + Compiled from "HostSideTestRedirectionClass.java" +public interface android.hosttest.annotation.HostSideTestRedirectionClass extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRedirectionClass + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "HostSideTestRedirectionClass.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRemove.class + Compiled from "HostSideTestRemove.java" +public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRemove + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestRemove.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestStaticInitializerKeep.class + Compiled from "HostSideTestStaticInitializerKeep.java" +public interface android.hosttest.annotation.HostSideTestStaticInitializerKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestStaticInitializerKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestStaticInitializerKeep.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestSubstitute.class + Compiled from "HostSideTestSubstitute.java" +public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestSubstitute + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + public abstract java.lang.String suffix(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "HostSideTestSubstitute.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestThrow.class + Compiled from "HostSideTestThrow.java" +public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestThrow + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestThrow.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class + Compiled from "HostSideTestWholeClassKeep.java" +public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "HostSideTestWholeClassKeep.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class + Compiled from "IPretendingAidl.java" +public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 4 + public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy(); + 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/IPretendingAidl$Stub$Proxy; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int addTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: iload_0 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 a I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl + public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub +SourceFile: "IPretendingAidl.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl +## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class + Compiled from "IPretendingAidl.java" +public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 4 + public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub(); + 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/IPretendingAidl$Stub; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int addOne(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: iload_0 + x: iconst_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 a I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl + public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub +SourceFile: "IPretendingAidl.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl +## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class + Compiled from "IPretendingAidl.java" +public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 4 +} +InnerClasses: + public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl + public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub +SourceFile: "IPretendingAidl.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestMembers: + com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub + com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy +## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R$Nested + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 4 + public static int[] ARRAY; + descriptor: [I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public com.android.hoststubgen.test.tinyframework.R$Nested(); + 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/R$Nested; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: iconst_1 + x: newarray int + x: dup + x: iconst_0 + x: iconst_1 + x: iastore + x: putstatic #x // Field ARRAY:[I + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/R +## Class: com/android/hoststubgen/test/tinyframework/R.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 4 + public com.android.hoststubgen.test.tinyframework.R(); + 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/R; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestMembers: + com/android/hoststubgen/test/tinyframework/R$Nested +## 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: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 7, attributes: 3 + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + 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/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.TinyFrameworkAnnotations(); + 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 keep:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + 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/TinyFrameworkAnnotations; + 0 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + 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/TinyFrameworkAnnotations; + 0 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: iload_0 + x: iconst_3 + x: iadd + 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.HostStubGenProcessedAsKeep + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 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.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public int toBeIgnored(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestIgnore +} +SourceFile: "TinyFrameworkAnnotations.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) +## 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: 65 + 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.HostStubGenProcessedAsKeep + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + 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/TinyFrameworkClassLoadHook; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: getstatic #x // Field sLoadedClasses:Ljava/util/Set; + x: aload_0 + x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z + x: pop + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class; + LocalVariableTypeTable: + Start Length Slot Name Signature + 0 11 0 clazz Ljava/lang/Class<*>; + Signature: #x // (Ljava/lang/Class<*>;)V + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: new #x // class java/util/HashSet + x: dup + x: invokespecial #x // Method java/util/HashSet."<init>":()V + x: putstatic #x // Field sLoadedClasses:Ljava/util/Set; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkClassLoadHook.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## 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: 65 + 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: 4, attributes: 3 + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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 keep: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.HostStubGenProcessedAsKeep + + 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.HostStubGenProcessedAsKeep + + 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.HostStubGenProcessedAsKeep + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 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.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow +} +SourceFile: "TinyFrameworkClassWideAnnotations.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class + Compiled from "TinyFrameworkClassWithInitializerDefault.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 0, attributes: 3 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.lang.Object sObject; + descriptor: Ljava/lang/Object; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + +} +SourceFile: "TinyFrameworkClassWithInitializerDefault.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class + Compiled from "TinyFrameworkClassWithInitializerStub.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 1, attributes: 3 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.lang.Object sObject; + descriptor: Ljava/lang/Object; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub + 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: iconst_1 + x: putstatic #x // Field sInitialized:Z + x: new #x // class java/lang/Object + x: dup + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: putstatic #x // Field sObject:Ljava/lang/Object; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkClassWithInitializerStub.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x() + android.hosttest.annotation.HostSideTestStaticInitializerKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class + Compiled from "TinyFrameworkEnumComplex.java" +public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex> + minor version: 0 + major version: 65 + flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + super_class: #x // java/lang/Enum + interfaces: 0, fields: 6, methods: 7, attributes: 4 + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private final java.lang.String mLongName; + descriptor: Ljava/lang/String; + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private final java.lang.String mShortName; + descriptor: Ljava/lang/String; + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES; + descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object; + x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;" + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String); + descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: aload_0 + x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 name Ljava/lang/String; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> mandated + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String); + descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + flags: (0x0002) ACC_PRIVATE + Code: + stack=3, locals=5, args_size=5 + x: aload_0 + x: aload_1 + x: iload_2 + x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V + x: aload_0 + x: aload_3 + x: putfield #x // Field mLongName:Ljava/lang/String; + x: aload_0 + x: aload 4 + x: putfield #x // Field mShortName:Ljava/lang/String; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 18 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + 0 18 3 longName Ljava/lang/String; + 0 18 4 shortName Ljava/lang/String; + Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + MethodParameters: + Name Flags + <no name> synthetic + <no name> synthetic + <no name> + <no name> + + public java.lang.String getLongName(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: getfield #x // Field mLongName:Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public java.lang.String getShortName(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: getfield #x // Field mShortName:Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: iconst_3 + x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: iconst_0 + x: getstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: aastore + x: dup + x: iconst_1 + x: getstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: aastore + x: dup + x: iconst_2 + x: getstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: aastore + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=6, locals=0, args_size=0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: ldc #x // String RED + x: iconst_0 + x: ldc #x // String Red + x: ldc #x // String R + x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + x: putstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: ldc #x // String GREEN + x: iconst_1 + x: ldc #x // String Green + x: ldc #x // String G + x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + x: putstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: ldc #x // String BLUE + x: iconst_2 + x: ldc #x // String Blue + x: ldc #x // String B + x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + x: putstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>; +SourceFile: "TinyFrameworkEnumComplex.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class + Compiled from "TinyFrameworkEnumSimple.java" +public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple> + minor version: 0 + major version: 65 + flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + super_class: #x // java/lang/Enum + interfaces: 0, fields: 3, methods: 5, attributes: 4 + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES; + descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object; + x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;" + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String); + descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: aload_0 + x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 name Ljava/lang/String; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> mandated + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple(); + descriptor: (Ljava/lang/String;I)V + flags: (0x0002) ACC_PRIVATE + Code: + stack=3, locals=3, args_size=3 + x: aload_0 + x: aload_1 + x: iload_2 + x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 7 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + Signature: #x // ()V + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> synthetic + <no name> synthetic + + private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: iconst_2 + x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: dup + x: iconst_0 + x: getstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: aastore + x: dup + x: iconst_1 + x: getstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: aastore + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: dup + x: ldc #x // String CAT + x: iconst_0 + x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V + x: putstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: dup + x: ldc #x // String DOG + x: iconst_1 + x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V + x: putstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>; +SourceFile: "TinyFrameworkEnumSimple.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class + Compiled from "TinyFrameworkExceptionTester.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester(); + 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/TinyFrameworkExceptionTester; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int testException(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=0 + x: new #x // class java/lang/IllegalStateException + x: dup + x: ldc #x // String Inner exception + x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V + x: athrow + x: astore_0 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Outer exception + x: aload_0 + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V + x: athrow + Exception table: + from to target type + 0 10 10 Class java/lang/Exception + StackMapTable: number_of_entries = 1 + frame_type = 74 /* same_locals_1_stack_item */ + stack = [ class java/lang/Exception ] + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 11 0 e Ljava/lang/Exception; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkExceptionTester.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class + Compiled from "TinyFrameworkForTextPolicy.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 15, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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/TinyFrameworkForTextPolicy + 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.TinyFrameworkForTextPolicy(); + 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/TinyFrameworkForTextPolicy; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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/TinyFrameworkForTextPolicy; + 0 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.String toBeIgnoredObj(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aconst_null + x: areturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public void toBeIgnoredV(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=0, locals=1, args_size=1 + x: return + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public boolean toBeIgnoredZ(); + descriptor: ()Z + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public byte toBeIgnoredB(); + descriptor: ()B + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public char toBeIgnoredC(); + descriptor: ()C + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public short toBeIgnoredS(); + descriptor: ()S + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public int toBeIgnoredI(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public float toBeIgnoredF(); + descriptor: ()F + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: fconst_0 + x: freturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public double toBeIgnoredD(); + descriptor: ()D + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + x: dconst_0 + x: dreturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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/TinyFrameworkForTextPolicy; + 0 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: iload_0 + x: iconst_3 + x: iadd + 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.HostStubGenProcessedAsKeep + + public java.lang.String unsupportedMethod(); + descriptor: ()Ljava/lang/String; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + 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.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkForTextPolicy.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class + Compiled from "TinyFrameworkLambdas.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 8, attributes: 6 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested(); + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static java.lang.Integer lambda$getSupplier_static$3(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: bipush 8 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$getSupplier$2(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: bipush 7 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$static$1(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: bipush 6 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$new$0(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: iconst_5 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles +SourceFile: "TinyFrameworkLambdas.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x() + android.hosttest.annotation.HostSideTestStaticInitializerKeep +BootstrapMethods: + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class + Compiled from "TinyFrameworkLambdas.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 8, attributes: 6 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas(); + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static java.lang.Integer lambda$getSupplier_static$3(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: iconst_4 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$getSupplier$2(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: iconst_3 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$static$1(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: iconst_2 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$new$0(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=1, locals=0, args_size=0 + x: iconst_1 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=1, locals=0, args_size=0 + x: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles +SourceFile: "TinyFrameworkLambdas.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x() + android.hosttest.annotation.HostSideTestStaticInitializerKeep +BootstrapMethods: + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class + Compiled from "TinyFrameworkMethodCallReplace.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 4 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo(); + 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/TinyFrameworkMethodCallReplace$ReplaceTo; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void startThread(java.lang.Thread); + descriptor: (Ljava/lang/Thread;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: iconst_1 + x: invokevirtual #x // Method java/lang/Thread.setDaemon:(Z)V + x: aload_0 + x: invokevirtual #x // Method java/lang/Thread.start:()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 thread Ljava/lang/Thread; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int add(int, int); + descriptor: (II)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=2, args_size=2 + x: iload_0 + x: iload_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 a I + 0 4 1 b I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace +SourceFile: "TinyFrameworkMethodCallReplace.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class + Compiled from "TinyFrameworkMethodCallReplace.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 4, attributes: 6 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace(); + 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/TinyFrameworkMethodCallReplace; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static boolean nonStaticMethodCallReplaceTester() throws java.lang.Exception; + descriptor: ()Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=2, args_size=0 + x: new #x // class java/util/concurrent/atomic/AtomicBoolean + x: dup + x: iconst_0 + x: invokespecial #x // Method java/util/concurrent/atomic/AtomicBoolean."<init>":(Z)V + x: astore_0 + x: new #x // class java/lang/Thread + x: dup + x: aload_0 + x: invokedynamic #x, 0 // InvokeDynamic #x:run:(Ljava/util/concurrent/atomic/AtomicBoolean;)Ljava/lang/Runnable; + x: invokespecial #x // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V + x: astore_1 + x: aload_1 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.startThread:(Ljava/lang/Thread;)V + x: aload_1 + x: invokevirtual #x // Method java/lang/Thread.join:()V + x: aload_0 + x: invokevirtual #x // Method java/util/concurrent/atomic/AtomicBoolean.get:()Z + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 9 27 0 ab Ljava/util/concurrent/atomic/AtomicBoolean; + 23 13 1 th Ljava/lang/Thread; + Exceptions: + throws java.lang.Exception + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int staticMethodCallReplaceTester(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: iconst_1 + x: iconst_2 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.add:(II)I + x: ireturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean); + descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=2, locals=1, args_size=1 + x: aload_0 + x: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread; + x: invokevirtual #x // Method java/lang/Thread.isDaemon:()Z + x: invokevirtual #x // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 11 0 ab Ljava/util/concurrent/atomic/AtomicBoolean; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace + public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles +SourceFile: "TinyFrameworkMethodCallReplace.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +BootstrapMethods: + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()V + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V + #x ()V +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class + Compiled from "TinyFrameworkNative.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 14, attributes: 3 + int value; + descriptor: I + flags: (0x0000) + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative(); + 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/TinyFrameworkNative; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=1, args_size=1 + x: iload_0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public static int nativeAddTwo_should_be_like_this(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=1, locals=1, args_size=1 + x: iload_0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 arg I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + x: lload_0 + x: lload_2 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + x: lreturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public static long nativeLongPlus_should_be_like_this(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + x: lload_0 + x: lload_2 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + x: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 arg1 J + 0 6 2 arg2 J + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public void setValue(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: iload_1 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + 0 6 1 v I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public int nativeNonStaticAddToValue(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: iload_1 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public int nativeNonStaticAddToValue_should_be_like_this(int); + descriptor: (I)I + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: iload_1 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + 0 6 1 arg I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void nativeStillNotSupported(); + descriptor: ()V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=0, args_size=0 + 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.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public static native void nativeStillKeep(); + descriptor: ()V + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void nativeStillNotSupported_should_be_like_this(); + descriptor: ()V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: new #x // class java/lang/RuntimeException + x: dup + x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + x: athrow + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=2, args_size=2 + x: iload_0 + x: iload_1 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public void notNativeRedirected(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.notNativeRedirected:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)V + x: return + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public static void notNativeStaticRedirected(); + descriptor: ()V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=0, locals=0, args_size=0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.notNativeStaticRedirected:()V + x: return + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect +} +SourceFile: "TinyFrameworkNative.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep + x: #x(#x=s#x) + android.hosttest.annotation.HostSideTestRedirectionClass( + value="TinyFrameworkNative_host" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class + Compiled from "TinyFrameworkNative_host.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 7, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host(); + 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/TinyFrameworkNative_host; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=1, args_size=1 + x: iload_0 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 arg I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + x: lload_0 + x: lload_2 + x: ladd + x: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 4 0 arg1 J + 0 4 2 arg2 J + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: getfield #x // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I + x: iload_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + 0 7 1 arg I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=2, args_size=2 + x: iload_0 + x: iload_1 + x: iadd + x: i2b + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 arg1 B + 0 5 1 arg2 B + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void notNativeRedirected(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=0, locals=1, args_size=1 + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 1 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void notNativeStaticRedirected(); + descriptor: ()V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=0, locals=0, args_size=0 + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkNative_host.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 6 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=1, locals=2, args_size=2 + 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/TinyFrameworkNestedClasses$1; + 0 5 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> final mandated + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_1 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 6 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2(); + descriptor: ()V + flags: (0x0000) + 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/TinyFrameworkNestedClasses$2; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_2 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 6 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=1, locals=2, args_size=2 + 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/TinyFrameworkNestedClasses$3; + 0 5 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> final mandated + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_3 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 6 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4(); + descriptor: ()V + flags: (0x0000) + 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/TinyFrameworkNestedClasses$4; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: iconst_4 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + 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.HostStubGenProcessedAsKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iload_1 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass; + 0 10 1 x I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass + minor version: 0 + major version: 65 + 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: 1, methods: 1, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iconst_5 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass; + 0 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> final mandated +} +InnerClasses: + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 3, attributes: 6 + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1(); + descriptor: ()V + flags: (0x0000) + 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/TinyFrameworkNestedClasses$StaticNestedClass$1; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: bipush 7 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: invokevirtual #x // Method get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +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 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +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: 65 + 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.HostStubGenProcessedAsKeep + + 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.HostStubGenProcessedAsKeep +} +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.HostStubGenProcessedAsKeep +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 + minor version: 0 + major version: 65 + 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 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass(); + 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 6 + 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; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +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.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## 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 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + interfaces: 0, fields: 0, methods: 1, attributes: 4 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: iload_1 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass; + 0 6 1 x I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +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; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 4, attributes: 5 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + x: dup + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + x: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + x: dup + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 9 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V + x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + 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: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +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 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class + Compiled from "TinyFrameworkPackageRedirect.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect(); + 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/TinyFrameworkPackageRedirect; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int foo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class com/supported/UnsupportedClass + x: dup + x: iload_0 + x: invokespecial #x // Method com/supported/UnsupportedClass."<init>":(I)V + x: invokevirtual #x // Method com/supported/UnsupportedClass.getValue:()I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 12 0 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkPackageRedirect.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class + Compiled from "TinyFrameworkRenamedClassCaller.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 3 + public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller(); + 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/TinyFrameworkRenamedClassCaller; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int foo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=1, args_size=1 + x: new #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed + x: dup + x: iload_0 + x: invokespecial #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed."<init>":(I)V + x: invokevirtual #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 12 0 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkRenamedClassCaller.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.A + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class + Compiled from "C1.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.C1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1 + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "C1.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class + Compiled from "C2.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1 + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "C2.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class + Compiled from "C3.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2 + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "C3.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class + Compiled from "CA.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.CA + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "CA.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class + Compiled from "CB.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.CB + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "CB.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class + Compiled from "Class_C1.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1 + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "Class_C1.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class + Compiled from "Class_C2.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2 + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "Class_C2.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class + Compiled from "Class_C3.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3 + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "Class_C3.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class + Compiled from "Class_I1.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "Class_I1.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class + Compiled from "Class_I1_IA.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA + super_class: #x // java/lang/Object + interfaces: 2, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "Class_I1_IA.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class + Compiled from "Class_I2.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "Class_I2.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class + Compiled from "Class_I3.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "Class_I3.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class + Compiled from "I1.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1 + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1 + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "I1.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class + Compiled from "I2.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1 + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "I2.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class + Compiled from "I3.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2 + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "I3.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class + Compiled from "IA.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "IA.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class + Compiled from "IB.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 0, attributes: 2 +} +SourceFile: "IB.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/supported/UnsupportedClass.class + Compiled from "UnsupportedClass.java" +public class com.supported.UnsupportedClass + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/supported/UnsupportedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 3 + private final int mValue; + descriptor: I + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public com.supported.UnsupportedClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iload_1 + x: putfield #x // Field mValue:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/supported/UnsupportedClass; + 0 10 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public int getValue(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: getfield #x // Field mValue:I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/supported/UnsupportedClass; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "UnsupportedClass.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/unsupported/UnsupportedClass.class + Compiled from "UnsupportedClass.java" +public class com.unsupported.UnsupportedClass + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/unsupported/UnsupportedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, attributes: 3 + public com.unsupported.UnsupportedClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=3, locals=2, args_size=2 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String This class is not supported + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 14 0 this Lcom/unsupported/UnsupportedClass; + 0 14 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public int getValue(); + descriptor: ()I + 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 This class is not supported + 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/unsupported/UnsupportedClass; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "UnsupportedClass.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class + Compiled from "TinyFrameworkToBeRenamed.java" +public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 3 + private final int mValue; + descriptor: I + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=2, args_size=2 + x: aload_0 + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: aload_0 + x: iload_1 + x: putfield #x // Field mValue:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed; + 0 10 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public int getValue(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + x: aload_0 + x: getfield #x // Field mValue:I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkToBeRenamed.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt new file mode 100644 index 000000000000..49769e648bbf --- /dev/null +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/13-hoststubgen-test-tiny-framework-host-ext-dump.txt @@ -0,0 +1,4896 @@ +## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class + Compiled from "HostSideTestClassLoadHook.java" +public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestClassLoadHook + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 2, attributes: 2 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class android/hosttest/annotation/HostSideTestClassLoadHook + 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 abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "HostSideTestClassLoadHook.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestKeep.class + Compiled from "HostSideTestKeep.java" +public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class android/hosttest/annotation/HostSideTestKeep + 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 +} +SourceFile: "HostSideTestKeep.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRedirect.class + Compiled from "HostSideTestRedirect.java" +public interface android.hosttest.annotation.HostSideTestRedirect extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRedirect + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class android/hosttest/annotation/HostSideTestRedirect + 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 +} +SourceFile: "HostSideTestRedirect.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRedirectionClass.class + Compiled from "HostSideTestRedirectionClass.java" +public interface android.hosttest.annotation.HostSideTestRedirectionClass extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRedirectionClass + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 2, attributes: 2 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class android/hosttest/annotation/HostSideTestRedirectionClass + 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 abstract java.lang.String value(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "HostSideTestRedirectionClass.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestRemove.class + Compiled from "HostSideTestRemove.java" +public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestRemove + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class android/hosttest/annotation/HostSideTestRemove + 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 +} +SourceFile: "HostSideTestRemove.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestStaticInitializerKeep.class + Compiled from "HostSideTestStaticInitializerKeep.java" +public interface android.hosttest.annotation.HostSideTestStaticInitializerKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestStaticInitializerKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class android/hosttest/annotation/HostSideTestStaticInitializerKeep + 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 +} +SourceFile: "HostSideTestStaticInitializerKeep.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestSubstitute.class + Compiled from "HostSideTestSubstitute.java" +public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestSubstitute + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 2, attributes: 2 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class android/hosttest/annotation/HostSideTestSubstitute + 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 abstract java.lang.String suffix(); + descriptor: ()Ljava/lang/String; + flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "HostSideTestSubstitute.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestThrow.class + Compiled from "HostSideTestThrow.java" +public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestThrow + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class android/hosttest/annotation/HostSideTestThrow + 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 +} +SourceFile: "HostSideTestThrow.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x,e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class + Compiled from "HostSideTestWholeClassKeep.java" +public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation + minor version: 0 + major version: 65 + flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION + this_class: #x // android/hosttest/annotation/HostSideTestWholeClassKeep + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class android/hosttest/annotation/HostSideTestWholeClassKeep + 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 +} +SourceFile: "HostSideTestWholeClassKeep.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + x: #x(#x=[e#x.#x]) + java.lang.annotation.Target( + value=[Ljava/lang/annotation/ElementType;.TYPE] + ) + x: #x(#x=e#x.#x) + java.lang.annotation.Retention( + value=Ljava/lang/annotation/RetentionPolicy;.CLASS + ) +## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class + Compiled from "IPretendingAidl.java" +public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, 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/IPretendingAidl$Stub$Proxy + 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.IPretendingAidl$Stub$Proxy(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy + 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/IPretendingAidl$Stub$Proxy; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int addTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy + 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_0 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 4 0 a I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl + public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub +SourceFile: "IPretendingAidl.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl +## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class + Compiled from "IPretendingAidl.java" +public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, 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/IPretendingAidl$Stub + 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.IPretendingAidl$Stub(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub + 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/IPretendingAidl$Stub; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int addOne(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub + 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_0 + x: iconst_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 4 0 a I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl + public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub +SourceFile: "IPretendingAidl.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl +## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class + Compiled from "IPretendingAidl.java" +public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/IPretendingAidl + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, 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/IPretendingAidl + 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 +} +InnerClasses: + public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl + public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub +SourceFile: "IPretendingAidl.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestMembers: + com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub + com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy +## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R$Nested + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 2, attributes: 4 + public static int[] ARRAY; + descriptor: [I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public com.android.hoststubgen.test.tinyframework.R$Nested(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested + 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/R$Nested; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested + x: ldc #x // String <clinit> + 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: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested + 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: iconst_1 + x: newarray int + x: dup + x: iconst_0 + x: iconst_1 + x: iastore + x: putstatic #x // Field ARRAY:[I + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/R +## Class: com/android/hoststubgen/test/tinyframework/R.class + Compiled from "R.java" +public class com.android.hoststubgen.test.tinyframework.R + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/R + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 2, 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/R + 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.R(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/R + 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/R; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R +SourceFile: "R.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestMembers: + com/android/hoststubgen/test/tinyframework/R$Nested +## 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: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 7, attributes: 3 + public int keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + 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/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/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.TinyFrameworkAnnotations(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + 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 + 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 keep:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + 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/TinyFrameworkAnnotations + 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/TinyFrameworkAnnotations; + 11 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + 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/TinyFrameworkAnnotations + 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/TinyFrameworkAnnotations; + 11 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + 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 + 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: 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.HostStubGenProcessedAsKeep + + 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/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: 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.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public int toBeIgnored(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations + x: ldc #x // String toBeIgnored + 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_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestIgnore +} +SourceFile: "TinyFrameworkAnnotations.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) +## 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: 65 + 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.HostStubGenProcessedAsKeep + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook(); + descriptor: ()V + flags: (0x0002) ACC_PRIVATE + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + 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/TinyFrameworkClassLoadHook; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void onClassLoaded(java.lang.Class<?>); + descriptor: (Ljava/lang/Class;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + x: ldc #x // String onClassLoaded + x: ldc #x // String (Ljava/lang/Class;)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: getstatic #x // Field sLoadedClasses:Ljava/util/Set; + x: aload_0 + x: invokeinterface #x, 2 // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z + x: pop + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 11 0 clazz Ljava/lang/Class; + LocalVariableTypeTable: + Start Length Slot Name Signature + 11 11 0 clazz Ljava/lang/Class<*>; + Signature: #x // (Ljava/lang/Class<*>;)V + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + x: ldc #x // String <clinit> + 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: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook + 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: new #x // class java/util/HashSet + x: dup + x: invokespecial #x // Method java/util/HashSet."<init>":()V + x: putstatic #x // Field sLoadedClasses:Ljava/util/Set; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkClassLoadHook.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## 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: 65 + 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 keep; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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 keep: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.HostStubGenProcessedAsKeep + + 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.HostStubGenProcessedAsKeep + + 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.HostStubGenProcessedAsKeep + + 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: 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.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow +} +SourceFile: "TinyFrameworkClassWideAnnotations.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class + Compiled from "TinyFrameworkClassWithInitializerDefault.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 0, attributes: 3 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.lang.Object sObject; + descriptor: Ljava/lang/Object; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + +} +SourceFile: "TinyFrameworkClassWithInitializerDefault.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class + Compiled from "TinyFrameworkClassWithInitializerStub.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 1, attributes: 3 + public static boolean sInitialized; + descriptor: Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.lang.Object sObject; + descriptor: Ljava/lang/Object; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub + x: ldc #x // String <clinit> + 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: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub + 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/TinyFrameworkClassWithInitializerStub + 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: iconst_1 + x: putstatic #x // Field sInitialized:Z + x: new #x // class java/lang/Object + x: dup + x: invokespecial #x // Method java/lang/Object."<init>":()V + x: putstatic #x // Field sObject:Ljava/lang/Object; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkClassWithInitializerStub.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x(#x=s#x) + android.hosttest.annotation.HostSideTestClassLoadHook( + value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded" + ) + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x() + android.hosttest.annotation.HostSideTestStaticInitializerKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class + Compiled from "TinyFrameworkEnumComplex.java" +public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex> + minor version: 0 + major version: 65 + flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + super_class: #x // java/lang/Enum + interfaces: 0, fields: 6, methods: 7, attributes: 4 + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private final java.lang.String mLongName; + descriptor: Ljava/lang/String; + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private final java.lang.String mShortName; + descriptor: Ljava/lang/String; + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES; + descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: ldc #x // String values + x: ldc #x // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + 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: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object; + x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;" + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String); + descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: ldc #x // String valueOf + x: ldc #x // String (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + 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 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: aload_0 + x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 10 0 name Ljava/lang/String; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> mandated + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String); + descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + flags: (0x0002) ACC_PRIVATE + Code: + stack=4, locals=5, args_size=5 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: ldc #x // String <init> + x: ldc #x // String (Ljava/lang/String;ILjava/lang/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: aload_0 + x: aload_1 + x: iload_2 + x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V + x: aload_0 + x: aload_3 + x: putfield #x // Field mLongName:Ljava/lang/String; + x: aload_0 + x: aload 4 + x: putfield #x // Field mShortName:Ljava/lang/String; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 18 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + 11 18 3 longName Ljava/lang/String; + 11 18 4 shortName Ljava/lang/String; + Signature: #x // (Ljava/lang/String;Ljava/lang/String;)V + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + MethodParameters: + Name Flags + <no name> synthetic + <no name> synthetic + <no name> + <no name> + + public java.lang.String getLongName(); + 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/TinyFrameworkEnumComplex + x: ldc #x // String getLongName + 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: getfield #x // Field mLongName:Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public java.lang.String getShortName(); + 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/TinyFrameworkEnumComplex + x: ldc #x // String getShortName + 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: getfield #x // Field mShortName:Ljava/lang/String; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: ldc #x // String $values + x: ldc #x // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + 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_3 + x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: iconst_0 + x: getstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: aastore + x: dup + x: iconst_1 + x: getstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: aastore + x: dup + x: iconst_2 + x: getstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: aastore + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=6, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: ldc #x // String <clinit> + 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: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + 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: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: ldc #x // String RED + x: iconst_0 + x: ldc #x // String Red + x: ldc #x // String R + x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + x: putstatic #x // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: ldc #x // String GREEN + x: iconst_1 + x: ldc #x // String Green + x: ldc #x // String G + x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + x: putstatic #x // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex + x: dup + x: ldc #x // String BLUE + x: iconst_2 + x: ldc #x // String Blue + x: ldc #x // String B + x: invokespecial #x // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V + x: putstatic #x // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>; +SourceFile: "TinyFrameworkEnumComplex.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class + Compiled from "TinyFrameworkEnumSimple.java" +public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple> + minor version: 0 + major version: 65 + flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + super_class: #x // java/lang/Enum + interfaces: 0, fields: 3, methods: 5, attributes: 4 + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG; + descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES; + descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: ldc #x // String values + x: ldc #x // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + 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: getstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: invokevirtual #x // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object; + x: checkcast #x // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;" + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String); + descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: ldc #x // String valueOf + x: ldc #x // String (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + 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 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: aload_0 + x: invokestatic #x // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; + x: checkcast #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 10 0 name Ljava/lang/String; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> mandated + + private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple(); + descriptor: (Ljava/lang/String;I)V + flags: (0x0002) ACC_PRIVATE + Code: + stack=4, locals=3, args_size=3 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: ldc #x // String <init> + x: ldc #x // String (Ljava/lang/String;I)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: aload_1 + x: iload_2 + x: invokespecial #x // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 7 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + Signature: #x // ()V + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> synthetic + <no name> synthetic + + private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values(); + descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: ldc #x // String $values + x: ldc #x // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + 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_2 + x: anewarray #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: dup + x: iconst_0 + x: getstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: aastore + x: dup + x: iconst_1 + x: getstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: aastore + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: ldc #x // String <clinit> + 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: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + 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: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: dup + x: ldc #x // String CAT + x: iconst_0 + x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V + x: putstatic #x // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple + x: dup + x: ldc #x // String DOG + x: iconst_1 + x: invokespecial #x // Method "<init>":(Ljava/lang/String;I)V + x: putstatic #x // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: invokestatic #x // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: putstatic #x // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +Signature: #x // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>; +SourceFile: "TinyFrameworkEnumSimple.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class + Compiled from "TinyFrameworkExceptionTester.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 3 + 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/TinyFrameworkExceptionTester + 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.TinyFrameworkExceptionTester(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester + 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/TinyFrameworkExceptionTester; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int testException(); + descriptor: ()I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester + x: ldc #x // String testException + 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: new #x // class java/lang/IllegalStateException + x: dup + x: ldc #x // String Inner exception + x: invokespecial #x // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V + x: athrow + x: astore_0 + x: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String Outer exception + x: aload_0 + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V + x: athrow + Exception table: + from to target type + 11 21 21 Class java/lang/Exception + StackMapTable: number_of_entries = 1 + frame_type = 85 /* same_locals_1_stack_item */ + stack = [ class java/lang/Exception ] + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 22 11 0 e Ljava/lang/Exception; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkExceptionTester.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class + Compiled from "TinyFrameworkForTextPolicy.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 15, attributes: 2 + public int stub; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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/TinyFrameworkForTextPolicy + 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/TinyFrameworkForTextPolicy + 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.TinyFrameworkForTextPolicy(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + 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/TinyFrameworkForTextPolicy; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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/TinyFrameworkForTextPolicy + 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/TinyFrameworkForTextPolicy; + 11 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.String toBeIgnoredObj(); + 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/TinyFrameworkForTextPolicy + x: ldc #x // String toBeIgnoredObj + 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: aconst_null + x: areturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public void toBeIgnoredV(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String toBeIgnoredV + 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: return + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public boolean toBeIgnoredZ(); + descriptor: ()Z + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String toBeIgnoredZ + x: ldc #x // String ()Z + 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_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public byte toBeIgnoredB(); + descriptor: ()B + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String toBeIgnoredB + x: ldc #x // String ()B + 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_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public char toBeIgnoredC(); + descriptor: ()C + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String toBeIgnoredC + x: ldc #x // String ()C + 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_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public short toBeIgnoredS(); + descriptor: ()S + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String toBeIgnoredS + x: ldc #x // String ()S + 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_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public int toBeIgnoredI(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String toBeIgnoredI + 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_0 + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public float toBeIgnoredF(); + descriptor: ()F + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String toBeIgnoredF + x: ldc #x // String ()F + 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: fconst_0 + x: freturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public double toBeIgnoredD(); + descriptor: ()D + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String toBeIgnoredD + x: ldc #x // String ()D + 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: dconst_0 + x: dreturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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/TinyFrameworkForTextPolicy + 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/TinyFrameworkForTextPolicy; + 11 4 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int nativeAddThree(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy + x: ldc #x // String nativeAddThree + 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_0 + x: iconst_3 + x: iadd + 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.HostStubGenProcessedAsKeep + + 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/TinyFrameworkForTextPolicy + 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: 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.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkForTextPolicy.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class + Compiled from "TinyFrameworkLambdas.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 8, attributes: 6 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + x: ldc #x // String getSupplier + x: ldc #x // String ()Ljava/util/function/Supplier; + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + x: ldc #x // String getSupplier_static + x: ldc #x // String ()Ljava/util/function/Supplier; + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static java.lang.Integer lambda$getSupplier_static$3(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + x: ldc #x // String lambda$getSupplier_static$3 + x: ldc #x // String ()Ljava/lang/Integer; + 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: bipush 8 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$getSupplier$2(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + x: ldc #x // String lambda$getSupplier$2 + x: ldc #x // String ()Ljava/lang/Integer; + 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: bipush 7 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$static$1(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + x: ldc #x // String lambda$static$1 + x: ldc #x // String ()Ljava/lang/Integer; + 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: bipush 6 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$new$0(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + x: ldc #x // String lambda$new$0 + x: ldc #x // String ()Ljava/lang/Integer; + 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_5 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + x: ldc #x // String <clinit> + 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: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles +SourceFile: "TinyFrameworkLambdas.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x() + android.hosttest.annotation.HostSideTestStaticInitializerKeep +BootstrapMethods: + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class + Compiled from "TinyFrameworkLambdas.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 8, attributes: 6 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 14 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + x: ldc #x // String getSupplier + x: ldc #x // String ()Ljava/util/function/Supplier; + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + x: ldc #x // String getSupplier_static + x: ldc #x // String ()Ljava/util/function/Supplier; + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + + private static java.lang.Integer lambda$getSupplier_static$3(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + x: ldc #x // String lambda$getSupplier_static$3 + x: ldc #x // String ()Ljava/lang/Integer; + 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_4 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$getSupplier$2(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + x: ldc #x // String lambda$getSupplier$2 + x: ldc #x // String ()Ljava/lang/Integer; + 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_3 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$static$1(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + x: ldc #x // String lambda$static$1 + x: ldc #x // String ()Ljava/lang/Integer; + 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_2 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static java.lang.Integer lambda$new$0(); + descriptor: ()Ljava/lang/Integer; + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + x: ldc #x // String lambda$new$0 + x: ldc #x // String ()Ljava/lang/Integer; + 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: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + x: ldc #x // String <clinit> + 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: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + 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: invokedynamic #x, 0 // InvokeDynamic #x:get:()Ljava/util/function/Supplier; + x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas + public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles +SourceFile: "TinyFrameworkLambdas.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestKeep + x: #x() + android.hosttest.annotation.HostSideTestStaticInitializerKeep +BootstrapMethods: + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()Ljava/lang/Object; + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer; + #x ()Ljava/lang/Integer; +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class + Compiled from "TinyFrameworkMethodCallReplace.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo + 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/TinyFrameworkMethodCallReplace$ReplaceTo + 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.TinyFrameworkMethodCallReplace$ReplaceTo(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo + 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/TinyFrameworkMethodCallReplace$ReplaceTo; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void startThread(java.lang.Thread); + descriptor: (Ljava/lang/Thread;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo + x: ldc #x // String startThread + x: ldc #x // String (Ljava/lang/Thread;)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: iconst_1 + x: invokevirtual #x // Method java/lang/Thread.setDaemon:(Z)V + x: aload_0 + x: invokevirtual #x // Method java/lang/Thread.start:()V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 10 0 thread Ljava/lang/Thread; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int add(int, int); + descriptor: (II)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo + x: ldc #x // String add + x: ldc #x // String (II)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: iload_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 4 0 a I + 11 4 1 b I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace +SourceFile: "TinyFrameworkMethodCallReplace.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class + Compiled from "TinyFrameworkMethodCallReplace.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 5, attributes: 6 + 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/TinyFrameworkMethodCallReplace + 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.TinyFrameworkMethodCallReplace(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace + 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/TinyFrameworkMethodCallReplace; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static boolean nonStaticMethodCallReplaceTester() throws java.lang.Exception; + descriptor: ()Z + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=2, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace + x: ldc #x // String nonStaticMethodCallReplaceTester + x: ldc #x // String ()Z + 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/util/concurrent/atomic/AtomicBoolean + x: dup + x: iconst_0 + x: invokespecial #x // Method java/util/concurrent/atomic/AtomicBoolean."<init>":(Z)V + x: astore_0 + x: new #x // class java/lang/Thread + x: dup + x: aload_0 + x: invokedynamic #x, 0 // InvokeDynamic #x:run:(Ljava/util/concurrent/atomic/AtomicBoolean;)Ljava/lang/Runnable; + x: invokespecial #x // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V + x: astore_1 + x: aload_1 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.startThread:(Ljava/lang/Thread;)V + x: aload_1 + x: invokevirtual #x // Method java/lang/Thread.join:()V + x: aload_0 + x: invokevirtual #x // Method java/util/concurrent/atomic/AtomicBoolean.get:()Z + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 20 27 0 ab Ljava/util/concurrent/atomic/AtomicBoolean; + 34 13 1 th Ljava/lang/Thread; + Exceptions: + throws java.lang.Exception + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int staticMethodCallReplaceTester(); + 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/TinyFrameworkMethodCallReplace + x: ldc #x // String staticMethodCallReplaceTester + 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: iconst_2 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.add:(II)I + x: ireturn + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean); + descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V + flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace + x: ldc #x // String lambda$nonStaticMethodCallReplaceTester$0 + x: ldc #x // String (Ljava/util/concurrent/atomic/AtomicBoolean;)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: invokestatic #x // Method java/lang/Thread.currentThread:()Ljava/lang/Thread; + x: invokevirtual #x // Method java/lang/Thread.isDaemon:()Z + x: invokevirtual #x // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 11 0 ab Ljava/util/concurrent/atomic/AtomicBoolean; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace + public static final #x= #x of #x; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles +SourceFile: "TinyFrameworkMethodCallReplace.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +BootstrapMethods: + x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + Method arguments: + #x ()V + #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V + #x ()V +NestMembers: + com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class + Compiled from "TinyFrameworkNative.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 15, attributes: 3 + int value; + descriptor: I + flags: (0x0000) + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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/TinyFrameworkNative + 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.TinyFrameworkNative(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + 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/TinyFrameworkNative; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + x: ldc #x // String nativeAddTwo + 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_0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public static int nativeAddTwo_should_be_like_this(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + x: ldc #x // String nativeAddTwo_should_be_like_this + 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_0 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 arg I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + x: ldc #x // String nativeLongPlus + x: ldc #x // String (JJ)J + 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: lload_0 + x: lload_2 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + x: lreturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public static long nativeLongPlus_should_be_like_this(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + x: ldc #x // String nativeLongPlus_should_be_like_this + x: ldc #x // String (JJ)J + 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: lload_0 + x: lload_2 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J + x: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 6 0 arg1 J + 11 6 2 arg2 J + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public void setValue(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + x: ldc #x // String setValue + x: ldc #x // String (I)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: iload_1 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + 11 6 1 v I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public int nativeNonStaticAddToValue(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/TinyFrameworkNative + x: ldc #x // String nativeNonStaticAddToValue + 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: aload_0 + x: iload_1 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public int nativeNonStaticAddToValue_should_be_like_this(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/TinyFrameworkNative + x: ldc #x // String nativeNonStaticAddToValue_should_be_like_this + 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: aload_0 + x: iload_1 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + 11 6 1 arg I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void nativeStillNotSupported(); + descriptor: ()V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + x: ldc #x // String nativeStillNotSupported + 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: 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.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestThrow + + public static native void nativeStillKeep(); + descriptor: ()V + flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void nativeStillNotSupported_should_be_like_this(); + descriptor: ()V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + x: ldc #x // String nativeStillNotSupported_should_be_like_this + 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: new #x // class java/lang/RuntimeException + x: dup + x: invokespecial #x // Method java/lang/RuntimeException."<init>":()V + x: athrow + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + x: ldc #x // String nativeBytePlus + x: ldc #x // String (BB)B + 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: iload_1 + x: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B + x: ireturn + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public void notNativeRedirected(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + x: ldc #x // String notNativeRedirected + 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: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.notNativeRedirected:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)V + x: return + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect + + public static void notNativeStaticRedirected(); + descriptor: ()V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative + x: ldc #x // String notNativeStaticRedirected + 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: invokestatic #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.notNativeStaticRedirected:()V + x: return + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestRedirect +} +SourceFile: "TinyFrameworkNative.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep + x: #x(#x=s#x) + android.hosttest.annotation.HostSideTestRedirectionClass( + value="TinyFrameworkNative_host" + ) +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class + Compiled from "TinyFrameworkNative_host.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 8, attributes: 3 + 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/TinyFrameworkNative_host + 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.TinyFrameworkNative_host(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + 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/TinyFrameworkNative_host; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int nativeAddTwo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String nativeAddTwo + 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_0 + x: iconst_2 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 4 0 arg I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static long nativeLongPlus(long, long); + descriptor: (JJ)J + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=4, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String nativeLongPlus + x: ldc #x // String (JJ)J + 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: lload_0 + x: lload_2 + x: ladd + x: lreturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 4 0 arg1 J + 11 4 2 arg2 J + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String nativeNonStaticAddToValue + x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;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: aload_0 + x: getfield #x // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I + x: iload_1 + x: iadd + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 7 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + 11 7 1 arg I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static byte nativeBytePlus(byte, byte); + descriptor: (BB)B + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String nativeBytePlus + x: ldc #x // String (BB)B + 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: iload_1 + x: iadd + x: i2b + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 arg1 B + 11 5 1 arg2 B + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void notNativeRedirected(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String notNativeRedirected + x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;)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: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 1 0 source Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static void notNativeStaticRedirected(); + descriptor: ()V + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host + x: ldc #x // String notNativeStaticRedirected + 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: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkNative_host.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 4, attributes: 6 + 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$1 + 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 + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + x: ldc #x // String <init> + x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)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/TinyFrameworkNestedClasses$1; + 11 5 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> final mandated + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Integer; + 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: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Object; + 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 get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 4, attributes: 6 + 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$2 + 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 + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2(); + descriptor: ()V + flags: (0x0000) + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + 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/TinyFrameworkNestedClasses$2; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Integer; + 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_2 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Object; + 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 get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 4, attributes: 6 + 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$3 + 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 + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0000) + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + x: ldc #x // String <init> + x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)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/TinyFrameworkNestedClasses$3; + 11 5 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> final mandated + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Integer; + 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_3 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Object; + 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 get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 4, attributes: 6 + 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$4 + 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 + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4(); + descriptor: ()V + flags: (0x0000) + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + 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/TinyFrameworkNestedClasses$4; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Integer; + 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_4 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Object; + 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 get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + 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.HostStubGenProcessedAsKeep + + 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$BaseClass + 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$BaseClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + x: ldc #x // String <init> + x: ldc #x // String (I)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: iload_1 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass; + 11 10 1 x I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + public static #x= #x of #x; // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass + minor version: 0 + major version: 65 + 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: 1, methods: 2, attributes: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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$InnerClass + 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$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses); + descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass + x: ldc #x // String <init> + x: ldc #x // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)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_5 + x: putfield #x // Field value:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass; + 11 10 1 this$0 Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + MethodParameters: + Name Flags + <no name> final mandated +} +InnerClasses: + public #x= #x of #x; // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class + Compiled from "TinyFrameworkNestedClasses.java" +class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer> + minor version: 0 + major version: 65 + flags: (0x0020) ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 4, attributes: 6 + 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$1 + 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 + + com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1(); + descriptor: ()V + flags: (0x0000) + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + 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/TinyFrameworkNestedClasses$StaticNestedClass$1; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Integer get(); + descriptor: ()Ljava/lang/Integer; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Integer; + 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: bipush 7 + x: invokestatic #x // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.lang.Object get(); + descriptor: ()Ljava/lang/Object; + flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + x: ldc #x // String get + x: ldc #x // String ()Ljava/lang/Object; + 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 get:()Ljava/lang/Integer; + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +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 +EnclosingMethod: #x.#x // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static +Signature: #x // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>; +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +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: 65 + 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.HostStubGenProcessedAsKeep + + 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.HostStubGenProcessedAsKeep +} +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.HostStubGenProcessedAsKeep +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 + minor version: 0 + major version: 65 + 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: 4 + public int value; + descriptor: I + flags: (0x0001) ACC_PUBLIC + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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 + 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(); + 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 + 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 6 + 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; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass + x: ldc #x // String getSupplier_static + x: ldc #x // String ()Ljava/util/function/Supplier; + 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 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +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.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## 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 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + super_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass + interfaces: 0, fields: 0, methods: 2, 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/TinyFrameworkNestedClasses$SubClass + 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$SubClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass + x: ldc #x // String <init> + x: ldc #x // String (I)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: iload_1 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 6 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass; + 11 6 1 x I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +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; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +SourceFile: "TinyFrameworkNestedClasses.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class + Compiled from "TinyFrameworkNestedClasses.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + super_class: #x // java/lang/Object + interfaces: 0, fields: 2, methods: 4, attributes: 5 + public final java.util.function.Supplier<java.lang.Integer> mSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0011) ACC_PUBLIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static final java.util.function.Supplier<java.lang.Integer> sSupplier; + descriptor: Ljava/util/function/Supplier; + flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL + Signature: #x // Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + 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: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + x: dup + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + x: putfield #x // Field mSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 17 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public java.util.function.Supplier<java.lang.Integer> getSupplier(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + x: ldc #x // String getSupplier + x: ldc #x // String ()Ljava/util/function/Supplier; + 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 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + x: dup + x: aload_0 + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V + x: areturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 9 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses; + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static java.util.function.Supplier<java.lang.Integer> getSupplier_static(); + descriptor: ()Ljava/util/function/Supplier; + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + x: ldc #x // String getSupplier_static + x: ldc #x // String ()Ljava/util/function/Supplier; + 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 com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V + x: areturn + LineNumberTable: + Signature: #x // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + static {}; + descriptor: ()V + flags: (0x0008) ACC_STATIC + Code: + stack=4, locals=0, args_size=0 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + x: ldc #x // String <clinit> + 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: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + 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: new #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + x: dup + x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V + x: putstatic #x // Field sSupplier:Ljava/util/function/Supplier; + x: return + LineNumberTable: + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +InnerClasses: + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4 + #x; // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + public static #x= #x of #x; // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses + 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: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +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 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2 + com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1 +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class + Compiled from "TinyFrameworkPackageRedirect.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 3 + 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/TinyFrameworkPackageRedirect + 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.TinyFrameworkPackageRedirect(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect + 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/TinyFrameworkPackageRedirect; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int foo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect + x: ldc #x // String foo + 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: new #x // class com/supported/UnsupportedClass + x: dup + x: iload_0 + x: invokespecial #x // Method com/supported/UnsupportedClass."<init>":(I)V + x: invokevirtual #x // Method com/supported/UnsupportedClass.getValue:()I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 12 0 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkPackageRedirect.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class + Compiled from "TinyFrameworkRenamedClassCaller.java" +public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 3 + 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/TinyFrameworkRenamedClassCaller + 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.TinyFrameworkRenamedClassCaller(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller + 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/TinyFrameworkRenamedClassCaller; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public static int foo(int); + descriptor: (I)I + flags: (0x0009) ACC_PUBLIC, ACC_STATIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller + x: ldc #x // String foo + 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: new #x // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed + x: dup + x: iload_0 + x: invokespecial #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed."<init>":(I)V + x: invokevirtual #x // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 12 0 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkRenamedClassCaller.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.A + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/packagetest/A + 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 +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class + Compiled from "A.java" +public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/packagetest/sub/A + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/packagetest/sub/A + 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 +} +SourceFile: "A.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class + Compiled from "C1.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.C1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1 + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/C1 + 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 +} +SourceFile: "C1.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class + Compiled from "C2.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1 + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/C2 + 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 +} +SourceFile: "C2.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class + Compiled from "C3.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2 + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/C3 + 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 +} +SourceFile: "C3.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class + Compiled from "CA.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.CA + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/CA + 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 +} +SourceFile: "CA.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class + Compiled from "CB.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.CB + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/CB + 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 +} +SourceFile: "CB.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class + Compiled from "Class_C1.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1 + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/Class_C1 + 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 +} +SourceFile: "Class_C1.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class + Compiled from "Class_C2.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2 + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/Class_C2 + 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 +} +SourceFile: "Class_C2.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class + Compiled from "Class_C3.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3 + super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3 + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/Class_C3 + 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 +} +SourceFile: "Class_C3.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class + Compiled from "Class_I1.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/Class_I1 + 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 +} +SourceFile: "Class_I1.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class + Compiled from "Class_I1_IA.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA + super_class: #x // java/lang/Object + interfaces: 2, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/Class_I1_IA + 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 +} +SourceFile: "Class_I1_IA.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class + Compiled from "Class_I2.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/Class_I2 + 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 +} +SourceFile: "Class_I2.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class + Compiled from "Class_I3.java" +public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3 + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/Class_I3 + 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 +} +SourceFile: "Class_I3.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class + Compiled from "I1.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1 + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1 + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/I1 + 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 +} +SourceFile: "I1.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class + Compiled from "I2.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1 + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/I2 + 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 +} +SourceFile: "I2.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class + Compiled from "I3.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2 + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3 + super_class: #x // java/lang/Object + interfaces: 1, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/I3 + 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 +} +SourceFile: "I3.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class + Compiled from "IA.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/IA + 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 +} +SourceFile: "IA.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class + Compiled from "IB.java" +public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB + minor version: 0 + major version: 65 + flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT + this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 1, attributes: 2 + 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/subclasstest/IB + 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 +} +SourceFile: "IB.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +## Class: com/supported/UnsupportedClass.class + Compiled from "UnsupportedClass.java" +public class com.supported.UnsupportedClass + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/supported/UnsupportedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 3 + private final int mValue; + descriptor: I + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/supported/UnsupportedClass + 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.supported.UnsupportedClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/supported/UnsupportedClass + x: ldc #x // String <init> + x: ldc #x // String (I)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: iload_1 + x: putfield #x // Field mValue:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 10 0 this Lcom/supported/UnsupportedClass; + 11 10 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public int getValue(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/supported/UnsupportedClass + x: ldc #x // String getValue + 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: getfield #x // Field mValue:I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/supported/UnsupportedClass; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "UnsupportedClass.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: com/unsupported/UnsupportedClass.class + Compiled from "UnsupportedClass.java" +public class com.unsupported.UnsupportedClass + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // com/unsupported/UnsupportedClass + super_class: #x // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 3 + private static {}; + descriptor: ()V + flags: (0x000a) ACC_PRIVATE, ACC_STATIC + Code: + stack=2, locals=0, args_size=0 + x: ldc #x // class com/unsupported/UnsupportedClass + 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.unsupported.UnsupportedClass(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/unsupported/UnsupportedClass + x: ldc #x // String <init> + x: ldc #x // String (I)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: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String This class is not supported + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 14 0 this Lcom/unsupported/UnsupportedClass; + 11 14 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public int getValue(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/unsupported/UnsupportedClass + x: ldc #x // String getValue + 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: new #x // class java/lang/RuntimeException + x: dup + x: ldc #x // String This class is not supported + x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V + x: athrow + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 10 0 this Lcom/unsupported/UnsupportedClass; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "UnsupportedClass.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep +## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class + Compiled from "TinyFrameworkToBeRenamed.java" +public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed + minor version: 0 + major version: 65 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #x // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed + super_class: #x // java/lang/Object + interfaces: 0, fields: 1, methods: 3, attributes: 3 + private final int mValue; + descriptor: I + flags: (0x0012) ACC_PRIVATE, ACC_FINAL + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + 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/TinyFrameworkToBeRenamed + 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 rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int); + descriptor: (I)V + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=2, args_size=2 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed + x: ldc #x // String <init> + x: ldc #x // String (I)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: iload_1 + x: putfield #x // Field mValue:I + x: return + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 10 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed; + 11 10 1 value I + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep + + public int getValue(); + descriptor: ()I + flags: (0x0001) ACC_PUBLIC + Code: + stack=4, locals=1, args_size=1 + x: ldc #x // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed + x: ldc #x // String getValue + 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: getfield #x // Field mValue:I + x: ireturn + LineNumberTable: + LocalVariableTable: + Start Length Slot Name Signature + 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed; + RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +} +SourceFile: "TinyFrameworkToBeRenamed.java" +RuntimeVisibleAnnotations: + x: #x() + com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep +RuntimeInvisibleAnnotations: + x: #x() + android.hosttest.annotation.HostSideTestWholeClassKeep diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py index 7a7de3553829..88fa492addb8 100755 --- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py @@ -20,18 +20,23 @@ import os import unittest import subprocess -GOLDEN_DIR = 'golden-output' +GOLDEN_DIRS = [ + 'golden-output', + 'golden-output.RELEASE_TARGET_JAVA_21', +] + # Run diff. def run_diff(file1, file2): - command = ['diff', '-u', '--ignore-blank-lines', '--ignore-space-change', file1, file2] + command = ['diff', '-u', '--ignore-blank-lines', + '--ignore-space-change', file1, file2] print(' '.join(command)) - result = subprocess.run(command, stderr = sys.stdout) + result = subprocess.run(command, stderr=sys.stdout) success = result.returncode == 0 if success: - print(f'No diff found.') + print('No diff found.') else: print(f'Fail: {file1} and {file2} are different.') @@ -39,27 +44,42 @@ def run_diff(file1, file2): # Check one golden file. -def check_one_file(filename): +def check_one_file(golden_dir, filename): print(f'= Checking file: {filename}') - return run_diff(os.path.join(GOLDEN_DIR, filename), filename) + return run_diff(os.path.join(golden_dir, filename), filename) + class TestWithGoldenOutput(unittest.TestCase): # Test to check the generated jar files to the golden output. + # Depending on build flags, the golden output may differ in expected ways. + # So only expect the files to match one of the possible golden outputs. def test_compare_to_golden(self): - self.skipTest("test cannot handle multiple images (see b/378470825)") - files = os.listdir(GOLDEN_DIR) + success = False + + for golden_dir in GOLDEN_DIRS: + if self.matches_golden(golden_dir): + success = True + print(f"Test passes for dir: {golden_dir}") + break + + if not success: + self.fail('Some files are different. ' + + 'See stdout log for more details.') + + def matches_golden(self, golden_dir): + files = os.listdir(golden_dir) files.sort() - print(f"Golden files: {files}") - success = True + print(f"Golden files for {golden_dir}: {files}") + match_success = True for file in files: - if not check_one_file(file): - success = False + if not check_one_file(golden_dir, file): + match_success = False + + return match_success - if not success: - self.fail('Some files are different. See stdout log for more details.') if __name__ == "__main__": unittest.main(verbosity=2) diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 2808056f72c9..a0b989b44f4f 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -55,16 +55,6 @@ flag { } flag { - name: "compute_window_changes_on_a11y_v2" - namespace: "accessibility" - description: "Computes accessibility window changes in accessibility instead of wm package." - bug: "322444245" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "deprecate_package_list_observer" namespace: "accessibility" description: "Stops using the deprecated PackageListObserver." diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index d6fc6e461edc..7ef6aace1538 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -1180,18 +1180,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } private boolean anyServiceWantsGenericMotionEvent(MotionEvent event) { - if (Flags.alwaysAllowObservingTouchEvents()) { - final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN); - if (isTouchEvent && !canShareGenericTouchEvent()) { - return false; - } - final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; - return (mCombinedGenericMotionEventSources & eventSourceWithoutClass) != 0; - } - // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing - // touch exploration. - if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) - && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { + final boolean isTouchEvent = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN); + if (isTouchEvent && !canShareGenericTouchEvent()) { return false; } final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; @@ -1199,21 +1189,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } private boolean anyServiceWantsToObserveMotionEvent(MotionEvent event) { - if (Flags.alwaysAllowObservingTouchEvents()) { - final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; - return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0; - } - // Disable SOURCE_TOUCHSCREEN generic event interception if any service is performing - // touch exploration. - if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN) - && (mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) { - return false; - } final int eventSourceWithoutClass = event.getSource() & ~InputDevice.SOURCE_CLASS_MASK; - return (mCombinedGenericMotionEventSources - & mCombinedMotionEventObservedSources - & eventSourceWithoutClass) - != 0; + return (mCombinedMotionEventObservedSources & eventSourceWithoutClass) != 0; } private boolean canShareGenericTouchEvent() { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index c210e726fc12..71a0fc453a95 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3797,13 +3797,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub || (Flags.enableMagnificationMultipleFingerMultipleTapGesture() && userState.isMagnificationTwoFingerTripleTapEnabledLocked())); - final boolean createConnectionForCurrentCapability = - com.android.window.flags.Flags.alwaysDrawMagnificationFullscreenBorder() - || (userState.getMagnificationCapabilitiesLocked() - != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - - final boolean connect = (shortcutEnabled && createConnectionForCurrentCapability) - || userHasMagnificationServicesLocked(userState); + final boolean connect = shortcutEnabled || userHasMagnificationServicesLocked(userState); getMagnificationConnectionManager().requestConnection(connect); } @@ -4852,8 +4846,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub getMagnificationConnectionManager().setConnection(connection); - if (com.android.window.flags.Flags.alwaysDrawMagnificationFullscreenBorder() - && connection == null + if (connection == null && mMagnificationController.isFullScreenMagnificationControllerInitialized()) { // Since the connection does not exist, the system ui cannot provide the border // implementation for fullscreen magnification. So we call reset to deactivate the @@ -6548,8 +6541,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // Only continue setting up the packages if the service has been initialized. // See: b/340927041 - if (Flags.skipPackageChangeBeforeUserSwitch() - && !mManagerService.isServiceInitializedLocked()) { + if (!mManagerService.isServiceInitializedLocked()) { Slog.w(LOG_TAG, "onSomePackagesChanged: service not initialized, skip the callback."); return; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 9a81aa6cc506..5cbe0c4fe4d2 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -433,22 +433,10 @@ public class AccessibilityWindowManager { return Collections.emptyList(); } - /** - * Callbacks from window manager when there's an accessibility change in windows. - * - * @param forceSend Send the windows for accessibility even if they haven't changed. - * @param topFocusedDisplayId The display Id which has the top focused window. - * @param topFocusedWindowToken The window token of top focused window. - * @param windows The windows for accessibility. - */ - @Override - public void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, + private void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows) { + // TODO(b/322444245): no longer need to get a lock. synchronized (mLock) { - if (!Flags.computeWindowChangesOnA11yV2()) { - // If the flag is enabled, it's already done in #createWindowInfoListLocked. - updateWindowsByWindowAttributesLocked(windows); - } if (DEBUG) { Slogf.i(LOG_TAG, "mDisplayId=%d, topFocusedDisplayId=%d, currentUserId=%d, " + "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId, @@ -490,9 +478,7 @@ public class AccessibilityWindowManager { } /** - * Called when the windows for accessibility changed. This is called if - * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2} is - * true. + * Called when the windows for accessibility changed. * * @param forceSend Send the windows for accessibility even if they haven't * changed. @@ -655,16 +641,6 @@ public class AccessibilityWindowManager { return true; } - private void updateWindowsByWindowAttributesLocked(List<WindowInfo> windows) { - for (int i = windows.size() - 1; i >= 0; i--) { - final WindowInfo windowInfo = windows.get(i); - final IBinder token = windowInfo.token; - final int windowId = findWindowIdLocked( - mAccessibilityUserManager.getCurrentUserIdLocked(), token); - updateWindowWithWindowAttributes(windowInfo, mWindowAttributes.get(windowId)); - } - } - private void updateWindowWithWindowAttributes(@NonNull WindowInfo windowInfo, @Nullable AccessibilityWindowAttributes attributes) { if (attributes == null) { @@ -990,19 +966,6 @@ public class AccessibilityWindowManager { private AccessibilityWindowInfo populateReportedWindowLocked(int userId, WindowInfo window, SparseArray<AccessibilityWindowInfo> oldWindowsById) { final int windowId = findWindowIdLocked(userId, window.token); - - // With the flag enabled, createWindowInfoListLocked() already removes invalid windows. - if (!Flags.computeWindowChangesOnA11yV2()) { - if (windowId < 0) { - return null; - } - - // Don't need to add the embedded hierarchy windows into the a11y windows list. - if (isEmbeddedHierarchyWindowsLocked(windowId)) { - return null; - } - } - final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain(); reportedWindow.setId(windowId); diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index d3d80e12313f..11b8ccb70dfb 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -699,10 +699,9 @@ public class FullScreenMagnificationController implements if (!mRegistered) { return false; } - // If the border implementation is on system ui side but the connection is not + // The border implementation is on system ui side but the connection is not // established, the fullscreen magnification should not work. - if (com.android.window.flags.Flags.alwaysDrawMagnificationFullscreenBorder() - && !mMagnificationConnectionStateSupplier.get()) { + if (!mMagnificationConnectionStateSupplier.get()) { return false; } if (DEBUG) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 51c4305061f8..058b2be5f4b3 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -51,7 +51,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.wm.WindowManagerInternal; -import com.android.window.flags.Flags; import java.util.concurrent.Executor; @@ -634,10 +633,8 @@ public class MagnificationController implements MagnificationConnectionManager.C @Override public void onFullScreenMagnificationActivationState(int displayId, boolean activated) { - if (Flags.alwaysDrawMagnificationFullscreenBorder()) { - getMagnificationConnectionManager() - .onFullscreenMagnificationActivationChanged(displayId, activated); - } + getMagnificationConnectionManager() + .onFullscreenMagnificationActivationChanged(displayId, activated); if (activated) { synchronized (mLock) { diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig index 1803424ae7d7..5d2ef770b96b 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -44,6 +44,16 @@ flag { } flag { + name: "test_flag" + namespace: "autofill" + description: "Test flag " + bug: "377868687" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "add_session_id_to_client_state" namespace: "autofill" description: "Include the session id into the FillEventHistory events as part of ClientState" diff --git a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java index 5e4bab15952d..02f186557ca4 100644 --- a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java +++ b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java @@ -41,6 +41,7 @@ import android.os.UserHandle; import android.util.ArraySet; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.backup.internal.LifecycleOperationStorage; @@ -71,8 +72,10 @@ public class BackupAgentConnectionManager { // Activity Manager; use this lock object to signal when a requested binding has // completed. private final Object mAgentConnectLock = new Object(); - private IBackupAgent mConnectedAgent; - private volatile boolean mConnecting; + @GuardedBy("mAgentConnectLock") + @Nullable + private BackupAgentConnection mCurrentConnection; + private final ArraySet<String> mRestoreNoRestrictedModePackages = new ArraySet<>(); private final ArraySet<String> mBackupNoRestrictedModePackages = new ArraySet<>(); @@ -96,8 +99,18 @@ public class BackupAgentConnectionManager { mUserIdMsg = "[UserID:" + userId + "] "; } + private static final class BackupAgentConnection { + public final ApplicationInfo appInfo; + public IBackupAgent backupAgent; + public boolean connecting = true; // Assume we are trying to connect on creation. + + private BackupAgentConnection(ApplicationInfo appInfo) { + this.appInfo = appInfo; + } + } + /** - * Fires off a backup agent, blocking until it attaches (and ActivityManager will call + * Fires off a backup agent, blocking until it attaches (i.e. ActivityManager calls * {@link #agentConnected(String, IBinder)}) or until this operation times out. * * @param mode a {@code BACKUP_MODE} from {@link android.app.ApplicationThreadConstants}. @@ -105,48 +118,56 @@ public class BackupAgentConnectionManager { @Nullable public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode, @BackupAnnotations.BackupDestination int backupDestination) { - IBackupAgent agent = null; synchronized (mAgentConnectLock) { - mConnecting = true; - mConnectedAgent = null; boolean useRestrictedMode = shouldUseRestrictedBackupModeForPackage(mode, app.packageName); + if (mCurrentConnection != null) { + Slog.e(TAG, mUserIdMsg + "binding to new agent before unbinding from old one: " + + mCurrentConnection.appInfo.packageName); + } + mCurrentConnection = new BackupAgentConnection(app); + + // bindBackupAgent() is an async API. It will kick off the app's process and call + // agentConnected() when it receives the agent from the app. + boolean startedBindSuccessfully = false; try { - if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId, - backupDestination, useRestrictedMode)) { - Slog.d(TAG, mUserIdMsg + "awaiting agent for " + app); - - // success; wait for the agent to arrive - // only wait 10 seconds for the bind to happen - long timeoutMark = System.currentTimeMillis() + 10 * 1000; - while (mConnecting && mConnectedAgent == null && (System.currentTimeMillis() - < timeoutMark)) { - try { - mAgentConnectLock.wait(5000); - } catch (InterruptedException e) { - // just bail - Slog.w(TAG, mUserIdMsg + "Interrupted: " + e); - mConnecting = false; - mConnectedAgent = null; - } - } + startedBindSuccessfully = mActivityManager.bindBackupAgent(app.packageName, mode, + mUserId, backupDestination, useRestrictedMode); + } catch (RemoteException e) { + // can't happen - ActivityManager is local + } - // if we timed out with no connect, abort and move on - if (mConnecting) { - Slog.w(TAG, mUserIdMsg + "Timeout waiting for agent " + app); - mConnectedAgent = null; + if (!startedBindSuccessfully) { + Slog.w(TAG, mUserIdMsg + "bind request failed for " + app.packageName); + mCurrentConnection = null; + } else { + Slog.d(TAG, mUserIdMsg + "awaiting agent for " + app.packageName); + + // Wait 10 seconds for the agent and then time out if we still haven't bound to it. + long timeoutMark = System.currentTimeMillis() + 10 * 1000; + while (mCurrentConnection != null && mCurrentConnection.connecting && ( + System.currentTimeMillis() < timeoutMark)) { + try { + mAgentConnectLock.wait(5000); + } catch (InterruptedException e) { + Slog.w(TAG, mUserIdMsg + "Interrupted: " + e); + mCurrentConnection = null; } - Slog.i(TAG, mUserIdMsg + "got agent " + mConnectedAgent); - agent = mConnectedAgent; } - } catch (RemoteException e) { - // can't happen - ActivityManager is local } - } - if (agent == null) { + + if (mCurrentConnection != null) { + if (!mCurrentConnection.connecting) { + return mCurrentConnection.backupAgent; + } + // If we are still connecting, we've timed out. + Slog.w(TAG, mUserIdMsg + "Timeout waiting for agent " + app); + mCurrentConnection = null; + } + mActivityManagerInternal.clearPendingBackup(mUserId); + return null; } - return agent; } /** @@ -154,30 +175,49 @@ public class BackupAgentConnectionManager { * It will tell the app to destroy the agent. */ public void unbindAgent(ApplicationInfo app) { - try { - mActivityManager.unbindBackupAgent(app); - } catch (RemoteException e) { - // Can't happen - activity manager is local + synchronized (mAgentConnectLock) { + if (mCurrentConnection == null) { + Slog.w(TAG, mUserIdMsg + "unbindAgent but no current connection"); + } else if (!mCurrentConnection.appInfo.packageName.equals(app.packageName)) { + Slog.w(TAG, mUserIdMsg + "unbindAgent for unexpected package: " + app.packageName + + " expected: " + mCurrentConnection.appInfo.packageName); + } else { + mCurrentConnection = null; + } + + // Even if we weren't expecting to be bound to this agent, we should still call + // ActivityManager just in case. It will ignore the call if it also wasn't expecting it. + try { + mActivityManager.unbindBackupAgent(app); + } catch (RemoteException e) { + // Can't happen - activity manager is local + } } } /** * Callback: a requested backup agent has been instantiated. This should only be called from - * the - * {@link ActivityManager} when it's telling us that an agent is ready after a call to + * the {@link ActivityManager} when it's telling us that an agent is ready after a call to * {@link #bindToAgentSynchronous(ApplicationInfo, int, int)}. */ public void agentConnected(String packageName, IBinder agentBinder) { synchronized (mAgentConnectLock) { - if (getCallingUid() == android.os.Process.SYSTEM_UID) { - Slog.d(TAG, - mUserIdMsg + "agentConnected pkg=" + packageName + " agent=" + agentBinder); - mConnectedAgent = IBackupAgent.Stub.asInterface(agentBinder); - mConnecting = false; - } else { + if (getCallingUid() != Process.SYSTEM_UID) { Slog.w(TAG, mUserIdMsg + "Non-system process uid=" + getCallingUid() + " claiming agent connected"); + return; } + + Slog.d(TAG, mUserIdMsg + "agentConnected pkg=" + packageName + " agent=" + agentBinder); + if (mCurrentConnection == null) { + Slog.w(TAG, mUserIdMsg + "was not expecting connection"); + } else if (!mCurrentConnection.appInfo.packageName.equals(packageName)) { + Slog.w(TAG, mUserIdMsg + "got agent for unexpected package=" + packageName); + } else { + mCurrentConnection.backupAgent = IBackupAgent.Stub.asInterface(agentBinder); + mCurrentConnection.connecting = false; + } + mAgentConnectLock.notifyAll(); } } @@ -189,16 +229,22 @@ public class BackupAgentConnectionManager { */ public void agentDisconnected(String packageName) { synchronized (mAgentConnectLock) { - if (getCallingUid() == Process.SYSTEM_UID) { - mConnectedAgent = null; - mConnecting = false; - } else { + if (getCallingUid() != Process.SYSTEM_UID) { Slog.w(TAG, mUserIdMsg + "Non-system process uid=" + getCallingUid() + " claiming agent disconnected"); + return; } + Slog.w(TAG, mUserIdMsg + "agentDisconnected: the backup agent for " + packageName + " died: cancel current operations"); + // Only abort the current connection if the agent we were expecting or already + // connected to has disconnected. + if (mCurrentConnection != null && mCurrentConnection.appInfo.packageName.equals( + packageName)) { + mCurrentConnection = null; + } + // Offload operation cancellation off the main thread as the cancellation callbacks // might call out to BackupTransport. Other operations started on the same package // before the cancellation callback has executed will also be cancelled by the callback. diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 8b5b93e96494..d4beb019e049 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -44,7 +44,6 @@ import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.app.compat.CompatChanges; import android.companion.AssociationInfo; -import android.companion.AssociationRequest; import android.companion.virtual.ActivityPolicyExemption; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceActivityListener; @@ -156,9 +155,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private static final String PERSISTENT_ID_PREFIX_CDM_ASSOCIATION = "companion:"; - private static final List<String> DEVICE_PROFILES_ALLOWING_MIRROR_DISPLAYS = List.of( - AssociationRequest.DEVICE_PROFILE_APP_STREAMING); - /** * Timeout until {@link #launchPendingIntent} stops waiting for an activity to be launched. */ @@ -1352,11 +1348,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return hasCustomAudioInputSupportInternal(); } - @Override - public boolean canCreateMirrorDisplays() { - return DEVICE_PROFILES_ALLOWING_MIRROR_DISPLAYS.contains(getDeviceProfile()); - } - private boolean hasCustomAudioInputSupportInternal() { if (!Flags.vdmPublicApis()) { return false; diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 3dcca1433dec..4cf17ae3984d 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -69,6 +69,7 @@ import android.service.battery.BatteryServiceDumpProto; import android.sysprop.PowerProperties; import android.util.EventLog; import android.util.Slog; +import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; @@ -303,6 +304,17 @@ public final class BatteryService extends SystemService { */ @VisibleForTesting public long mLastBroadcastVoltageUpdateTime; + /** + * Time when the max charging current was updated last by HAL and we sent the + * {@link Intent#ACTION_BATTERY_CHANGED} broadcast. + * Note: This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast + * so it is possible that max current was updated but we did not send the broadcast so in that + * case we do not update the time. + */ + @VisibleForTesting + public long mLastBroadcastMaxChargingCurrentUpdateTime; + + private boolean mIsFirstBatteryChangedUpdate = true; private Led mLed; @@ -350,16 +362,21 @@ public final class BatteryService extends SystemService { private static final int ABSOLUTE_DECI_CELSIUS_DIFF_FOR_TEMP_UPDATE = 10; /** * This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We - * only send the broadcast if the last voltage was updated at least 20s seconds back and has a + * only send the broadcast if the last voltage was updated at least 20 seconds back and has a * fluctuation of at least 1%. */ private static final int TIME_DIFF_FOR_VOLTAGE_UPDATE_MS = 20000; /** * The value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We - * only send the broadcast if the last voltage was updated at least 20s seconds back and has a + * only send the broadcast if the last voltage was updated at least 20 seconds back and has a * fluctuation of at least 1%. */ private static final float BASE_POINT_DIFF_FOR_VOLTAGE_UPDATE = 0.01f; + /** + * This value is used to rate limit the {@link Intent#ACTION_BATTERY_CHANGED} broadcast. We + * only send the broadcast if the last max charging current was updated at least 5 seconds back. + */ + private static final int TIME_DIFF_FOR_MAX_CHARGING_CURRENT_UPDATE_MS = 5000; private final Handler.Callback mLocalCallback = msg -> { switch (msg.what) { @@ -1252,8 +1269,10 @@ public final class BatteryService extends SystemService { if (!com.android.server.flags.Flags.rateLimitBatteryChangedBroadcast()) { return false; } - if (mLastBroadcastBatteryVoltage == 0 || mLastBroadcastBatteryTemperature == 0) { + if (mIsFirstBatteryChangedUpdate) { mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime(); + mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime(); + mIsFirstBatteryChangedUpdate = false; return false; } @@ -1261,13 +1280,14 @@ public final class BatteryService extends SystemService { mLastBroadcastBatteryVoltage != mHealthInfo.batteryVoltageMillivolts; final boolean temperatureUpdated = mLastBroadcastBatteryTemperature != mHealthInfo.batteryTemperatureTenthsCelsius; + final boolean maxChargingCurrentUpdated = + mLastBroadcastMaxChargingCurrent != mHealthInfo.maxChargingCurrentMicroamps; final boolean otherStatesUpdated = forceUpdate || mHealthInfo.batteryStatus != mLastBroadcastBatteryStatus || mHealthInfo.batteryHealth != mLastBroadcastBatteryHealth || mHealthInfo.batteryPresent != mLastBroadcastBatteryPresent || mHealthInfo.batteryLevel != mLastBroadcastBatteryLevel || mPlugType != mLastBroadcastPlugType - || mHealthInfo.maxChargingCurrentMicroamps != mLastBroadcastMaxChargingCurrent || mHealthInfo.maxChargingVoltageMicrovolts != mLastBroadcastMaxChargingVoltage || mInvalidCharger != mLastBroadcastInvalidCharger || mHealthInfo.batteryCycleCount != mLastBroadcastBatteryCycleCount @@ -1280,6 +1300,9 @@ public final class BatteryService extends SystemService { if (voltageUpdated) { mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime(); } + if (maxChargingCurrentUpdated) { + mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime(); + } return false; } @@ -1295,6 +1318,9 @@ public final class BatteryService extends SystemService { >= TIME_DIFF_FOR_VOLTAGE_UPDATE_MS) { mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime(); + if (maxChargingCurrentUpdated) { + mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime(); + } return false; } @@ -1307,6 +1333,20 @@ public final class BatteryService extends SystemService { if (voltageUpdated) { mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime(); } + if (maxChargingCurrentUpdated) { + mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime(); + } + return false; + } + + if (maxChargingCurrentUpdated + && SystemClock.elapsedRealtime() - mLastBroadcastMaxChargingCurrentUpdateTime + >= TIME_DIFF_FOR_MAX_CHARGING_CURRENT_UPDATE_MS) { + mLastBroadcastMaxChargingCurrentUpdateTime = SystemClock.elapsedRealtime(); + + if (voltageUpdated) { + mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime(); + } return false; } @@ -1615,6 +1655,9 @@ public final class BatteryService extends SystemService { pw.println(" Wireless powered: " + mHealthInfo.chargerWirelessOnline); pw.println(" Dock powered: " + mHealthInfo.chargerDockOnline); pw.println(" Max charging current: " + mHealthInfo.maxChargingCurrentMicroamps); + pw.println(" Time when the latest updated value of the Max charging current was" + + " sent via battery changed broadcast: " + + TimeUtils.formatDuration(mLastBroadcastMaxChargingCurrentUpdateTime)); pw.println(" Max charging voltage: " + mHealthInfo.maxChargingVoltageMicrovolts); pw.println(" Charge counter: " + mHealthInfo.batteryChargeCounterUah); pw.println(" status: " + mHealthInfo.batteryStatus); @@ -1624,7 +1667,8 @@ public final class BatteryService extends SystemService { pw.println(" scale: " + BATTERY_SCALE); pw.println(" voltage: " + mHealthInfo.batteryVoltageMillivolts); pw.println(" Time when the latest updated value of the voltage was sent via " - + "battery changed broadcast: " + mLastBroadcastVoltageUpdateTime); + + "battery changed broadcast: " + + TimeUtils.formatDuration(mLastBroadcastVoltageUpdateTime)); pw.println(" The last voltage value sent via the battery changed broadcast: " + mLastBroadcastBatteryVoltage); pw.println(" temperature: " + mHealthInfo.batteryTemperatureTenthsCelsius); diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index 485bf31dfb64..0286f7b0b73d 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -65,6 +65,7 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -347,7 +348,8 @@ public class BinaryTransparencyService extends SystemService { + " packages after considering preloads"); } - if (CompatChanges.isChangeEnabled(LOG_MBA_INFO)) { + if (!android.app.Flags.backgroundInstallControlCallbackApi() + && CompatChanges.isChangeEnabled(LOG_MBA_INFO)) { // lastly measure all newly installed MBAs List<IBinaryTransparencyService.AppInfo> allMbaInfo = collectAllSilentInstalledMbaInfo(packagesMeasured); @@ -1158,6 +1160,49 @@ public class BinaryTransparencyService extends SystemService { } /** + * Receive callbacks from BIC to write silently installed apps to log + * + * TODO: Add a host test for testing registration and callback of BicCallbackHandler + * b/380002484 + */ + static class BicCallbackHandler extends IRemoteCallback.Stub { + private static final String BIC_CALLBACK_HANDLER_TAG = + "BTS.BicCallbackHandler"; + private final BinaryTransparencyServiceImpl mServiceImpl; + static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; + + BicCallbackHandler(BinaryTransparencyServiceImpl impl) { + mServiceImpl = impl; + } + + @Override + public void sendResult(Bundle data) { + String packageName = data.getString(FLAGGED_PACKAGE_NAME_KEY); + if (packageName == null) return; + if (DEBUG) { + Slog.d(BIC_CALLBACK_HANDLER_TAG, "background install event detected for " + + packageName); + } + + PackageState packageState = LocalServices.getService(PackageManagerInternal.class) + .getPackageStateInternal(packageName); + if (packageState == null) { + Slog.w(TAG, "Package state is unavailable, ignoring the package " + + packageName); + return; + } + if (packageState.isUpdatedSystemApp()) { + return; + } + List<IBinaryTransparencyService.AppInfo> mbaInfo = mServiceImpl.collectAppInfo( + packageState, MBA_STATUS_NEW_INSTALL); + for (IBinaryTransparencyService.AppInfo appInfo : mbaInfo) { + mServiceImpl.writeAppInfoToLog(appInfo); + } + } + }; + + /** * Called when the system service should publish a binder service using * {@link #publishBinderService(String, IBinder).} */ @@ -1534,6 +1579,29 @@ public class BinaryTransparencyService extends SystemService { } } + private void registerBicCallback() { + if(!com.android.server.flags.Flags.optionalBackgroundInstallControl()) { + Slog.d(TAG, "BICS is disabled for this device, skipping registration."); + return; + } + IBackgroundInstallControlService iBics = + IBackgroundInstallControlService.Stub.asInterface( + ServiceManager.getService( + Context.BACKGROUND_INSTALL_CONTROL_SERVICE)); + if(iBics == null) { + Slog.e(TAG, "Failed to register BackgroundInstallControl callback, either " + + "background install control service does not exist or disabled on this " + + "build."); + return; + } + try { + iBics.registerBackgroundInstallCallback( + new BicCallbackHandler(mServiceImpl)); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to register BackgroundInstallControl callback."); + } + } + private boolean isPackagePreloaded(String packageName) { PackageManager pm = mContext.getPackageManager(); try { @@ -1596,6 +1664,10 @@ public class BinaryTransparencyService extends SystemService { private void registerAllPackageUpdateObservers() { registerApkAndNonStagedApexUpdateListener(); registerStagedApexUpdateObserver(); + if (android.app.Flags.backgroundInstallControlCallbackApi() + && CompatChanges.isChangeEnabled(LOG_MBA_INFO)) { + registerBicCallback(); + } } private String translateContentDigestAlgorithmIdToString(int algorithmId) { diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING index ce66dc3c76cb..8da835896bd3 100644 --- a/services/core/java/com/android/server/TEST_MAPPING +++ b/services/core/java/com/android/server/TEST_MAPPING @@ -176,6 +176,10 @@ "include-filter": "com.android.server.wm.BackgroundActivityStart*" } ] + }, + { + "name": "FrameworksMockingServicesTests_service_batteryServiceTest", + "file_patterns": ["BatteryService\\.java"] } ] } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index ab3ab159ba12..71cbc10074d6 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -9319,6 +9319,46 @@ public final class ActiveServices { Slog.e(TAG, "stopForegroundServiceDelegateLocked delegate does not exist"); } } + /** + * Handles notifications from MediaSessionService about active media service. + * This method evaluates the provided information and transitions corresponding service to + * foreground state. + * + * @param packageName The package name of the app running the service. + * @param userId The user ID associated with the service. + * @param notificationId The ID of the media notification associated with the service. + */ + void notifyActiveMediaForegroundServiceLocked(@NonNull String packageName, + @UserIdInt int userId, int notificationId) { + if (!enableNotifyingActivityManagerWithMediaSessionStatusChange()) { + return; + } + + final ServiceMap smap = mServiceMap.get(userId); + if (smap == null) { + return; + } + final int serviceSize = smap.mServicesByInstanceName.size(); + for (int i = 0; i < serviceSize; i++) { + final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i); + if (sr.appInfo.packageName.equals(packageName) && !sr.isForeground) { + // foregroundServiceType is cleared when media session is user-disengaged + // and calls notifyInactiveMediaForegroundService->setServiceForegroundInnerLocked. + if (sr.foregroundServiceType + == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE + && sr.foregroundId == notificationId) { + if (DEBUG_FOREGROUND_SERVICE) { + Slog.d(TAG, "Moving media service to foreground for package " + + packageName); + } + setServiceForegroundInnerLocked(sr, sr.foregroundId, + sr.foregroundNoti, /* flags */ 0, + ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, + /* callingUidStart */ 0); + } + } + } + } /** * Handles notifications from MediaSessionService about inactive media foreground services. diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5c5361bacf5a..053ec824e1a7 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -131,6 +131,9 @@ import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES; import static android.provider.Settings.Global.DEBUG_APP; import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER; import static android.security.Flags.preventIntentRedirect; +import static android.security.Flags.preventIntentRedirectCollectNestedKeysOnServerIfNotCollected; +import static android.security.Flags.preventIntentRedirectShowToast; +import static android.security.Flags.preventIntentRedirectThrowExceptionIfNestedKeysNotCollected; import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS; import static android.view.Display.INVALID_DISPLAY; @@ -387,6 +390,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.view.autofill.AutofillManagerInternal; +import android.widget.Toast; import com.android.internal.annotations.CompositeRWLock; import com.android.internal.annotations.GuardedBy; @@ -437,6 +441,7 @@ import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.ThreadPriorityBooster; +import com.android.server.UiThread; import com.android.server.Watchdog; import com.android.server.am.LowMemDetector.MemFactor; import com.android.server.appop.AppOpsService; @@ -478,6 +483,7 @@ import com.android.server.wm.WindowProcessController; import dalvik.annotation.optimization.NeverCompile; import dalvik.system.VMRuntime; + import libcore.util.EmptyArray; import java.io.File; @@ -17923,6 +17929,15 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public void notifyActiveMediaForegroundService(@NonNull String packageName, + @UserIdInt int userId, int notificationId) { + synchronized (ActivityManagerService.this) { + mServices.notifyActiveMediaForegroundServiceLocked(packageName, userId, + notificationId); + } + } + + @Override public void notifyInactiveMediaForegroundService(@NonNull String packageName, @UserIdInt int userId, int notificationId) { synchronized (ActivityManagerService.this) { @@ -19304,9 +19319,32 @@ public class ActivityManagerService extends IActivityManager.Stub */ public void addCreatorToken(@Nullable Intent intent, String creatorPackage) { if (!preventIntentRedirect()) return; - if (intent == null) return; + if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED) == 0) { + Slog.wtf(TAG, + "[IntentRedirect] The intent does not have its nested keys collected as a " + + "preparation for creating intent creator tokens. Intent: " + + intent + "; creatorPackage: " + creatorPackage); + if (preventIntentRedirectShowToast()) { + UiThread.getHandler().post( + () -> Toast.makeText(mContext, + "Nested keys not collected. go/report-bug-intentRedir to report a" + + " bug", Toast.LENGTH_LONG).show()); + } + if (preventIntentRedirectThrowExceptionIfNestedKeysNotCollected()) { + // this flag will be internal only, not ramped to public. + throw new SecurityException( + "The intent does not have its nested keys collected as a preparation for " + + "creating intent creator tokens. Intent: " + + intent + "; creatorPackage: " + creatorPackage); + } + if (preventIntentRedirectCollectNestedKeysOnServerIfNotCollected()) { + // this flag will be ramped to public. + intent.collectExtraIntentKeys(); + } + } + String targetPackage = intent.getComponent() != null ? intent.getComponent().getPackageName() : intent.getPackage(); diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index d3d3fc968ae6..37d058b06b99 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -471,14 +471,14 @@ final class ActivityManagerShellCommand extends ShellCommand { } int userId = UserHandle.USER_CURRENT; final String cmd = getNextArgRequired(); - if ("inactive".equals(cmd)) { + if ("inactive".equals(cmd) || "active".equals(cmd)) { String opt; while ((opt = getNextOption()) != null) { if (opt.equals("--user")) { userId = UserHandle.parseUserArg(getNextArgRequired()); if (userId == UserHandle.USER_ALL) { err.println( - "Error: Can't set media fgs inactive with user 'all'"); + "Error: Can't set media fgs with user 'all'"); return -1; } } else { @@ -492,8 +492,13 @@ final class ActivityManagerShellCommand extends ShellCommand { err.println("Error: notification id cannot be zero"); return -1; } - mInternal.mInternal.notifyInactiveMediaForegroundService(pkgName, - userId, notificationId); + if ("inactive".equals(cmd)) { + mInternal.mInternal.notifyInactiveMediaForegroundService(pkgName, + userId, notificationId); + } else { + mInternal.mInternal.notifyActiveMediaForegroundService(pkgName, + userId, notificationId); + } return 0; } err.println("Error: Unknown set-media-foreground-service command: " + cmd); @@ -852,6 +857,7 @@ final class ActivityManagerShellCommand extends ShellCommand { } options.setDismissKeyguardIfInsecure(); } + intent.collectExtraIntentKeys(); if (mWaitOption) { result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent, mimeType, null, null, 0, mStartFlags, profilerInfo, @@ -970,6 +976,7 @@ final class ActivityManagerShellCommand extends ShellCommand { } pw.println("Starting service: " + intent); pw.flush(); + intent.collectExtraIntentKeys(); ComponentName cn = mInterface.startService(null, intent, intent.getType(), asForeground, SHELL_PACKAGE_NAME, null, mUserId); if (cn == null) { @@ -1002,6 +1009,7 @@ final class ActivityManagerShellCommand extends ShellCommand { } pw.println("Stopping service: " + intent); pw.flush(); + intent.collectExtraIntentKeys(); int result = mInterface.stopService(null, intent, intent.getType(), mUserId); if (result == 0) { err.println("Service not stopped: was not running."); @@ -1399,6 +1407,12 @@ final class ActivityManagerShellCommand extends ShellCommand { heapFile = "/data/local/tmp/heapdump-" + logNameTimeString + ".prof"; } + String argAfterHeapFile = getNextArg(); + if (argAfterHeapFile != null) { + err.println("Error: Arguments cannot be placed after the heap file"); + return -1; + } + // Writes an error message to stderr on failure ParcelFileDescriptor fd = openFileForSystem(heapFile, "w"); if (fd == null) { @@ -4683,9 +4697,9 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" --protobuf: format output using protobuffer"); pw.println(" set-app-zygote-preload-timeout <TIMEOUT_IN_MS>"); pw.println(" Set the timeout for preloading code in the app-zygote"); - pw.println(" set-media-foreground-service inactive [--user USER_ID]" - + " <PACKAGE> <NOTIFICATION_ID>"); - pw.println(" Set an app's media foreground service inactive."); + pw.println(" set-media-foreground-service inactive|active [--user USER_ID] <PACKAGE>" + + " <NOTIFICATION_ID>"); + pw.println(" Set an app's media service inactive or active."); Intent.printIntentArgsHelp(pw, ""); } } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 415f78aa3ee5..b7a5f3e4099a 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -598,7 +598,7 @@ class AppErrors { } if (r != null) { - mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(), + mPackageWatchdog.notifyPackageFailure(r.getPackageListWithVersionCode(), PackageWatchdog.FAILURE_REASON_APP_CRASH); synchronized (mService) { @@ -1142,7 +1142,7 @@ class AppErrors { } // Notify PackageWatchdog without the lock held if (packageList != null) { - mPackageWatchdog.onPackageFailure(packageList, + mPackageWatchdog.notifyPackageFailure(packageList, PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING); } } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 2eb9f3cb600f..400ebfde1741 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -1126,20 +1126,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub break; } case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL: - if (Flags.disableCompositeBatteryUsageStatsAtoms()) { - return StatsManager.PULL_SKIP; - } + return StatsManager.PULL_SKIP; - final BatteryUsageStatsQuery queryPowerProfile = - new BatteryUsageStatsQuery.Builder() - .setMaxStatsAgeMs(0) - .includeProcessStateData() - .includeVirtualUids() - .powerProfileModeledOnly() - .includePowerModels() - .build(); - bus = getBatteryUsageStats(List.of(queryPowerProfile)).get(0); - break; case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET: { if (Flags.disableCompositeBatteryUsageStatsAtoms()) { return StatsManager.PULL_SKIP; @@ -1262,6 +1250,17 @@ public final class BatteryStatsService extends IBatteryStats.Stub mFrameworkStatsLogger = frameworkStatsLogger; } + private static float clampPowerMah(double powerMah, String consumer) { + float resultPowerMah = 0; + if (powerMah <= Float.MAX_VALUE && powerMah >= Float.MIN_VALUE) { + resultPowerMah = (float) powerMah; + } else { + // Handle overflow appropriately + Slog.wtfStack(TAG, consumer + " reported powerMah float overflow: " + powerMah); + } + return resultPowerMah; + } + /** * Generates StatsEvents for the supplied battery usage stats and adds them to * the supplied list. @@ -1282,7 +1281,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub bus.getAggregateBatteryConsumer( BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE); - final float totalDeviceConsumedPowerMah = (float) deviceConsumer.getConsumedPower(); + final float totalDeviceConsumedPowerMah = + clampPowerMah(deviceConsumer.getConsumedPower(), "AggregateBatteryConsumer"); for (@BatteryConsumer.PowerComponentId int powerComponentId : deviceConsumer.getPowerComponentIds()) { @@ -1314,7 +1314,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub // Log single atom for BatteryUsageStats per uid/process_state/component/etc. for (UidBatteryConsumer uidConsumer : uidConsumers) { final int uid = uidConsumer.getUid(); - final float totalConsumedPowerMah = (float) uidConsumer.getConsumedPower(); + + final float totalConsumedPowerMah = + clampPowerMah(uidConsumer.getConsumedPower(), "uidConsumer-" + uid); for (@BatteryConsumer.PowerComponentId int powerComponentId : uidConsumer.getPowerComponentIds()) { @@ -1358,7 +1360,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub } final String powerComponentName = batteryConsumer.getPowerComponentName(componentId); - final float powerMah = (float) batteryConsumer.getConsumedPower(key); + final double consumedPowerMah = batteryConsumer.getConsumedPower(key); + float powerMah = + clampPowerMah( + consumedPowerMah, "uidConsumer-" + uid + "-" + powerComponentName); final long powerComponentDurationMillis = batteryConsumer.getUsageDurationMillis(key); if (powerMah == 0 && powerComponentDurationMillis == 0) { @@ -3167,7 +3172,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } - private void dumpUsageStats(FileDescriptor fd, PrintWriter pw, int model, + private void dumpUsageStats(FileDescriptor fd, PrintWriter pw, boolean proto, boolean accumulated) { awaitCompletion(); syncStats("dump", BatteryExternalStatsWorker.UPDATE_ALL); @@ -3179,9 +3184,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub if (Flags.batteryUsageStatsByPowerAndScreenState()) { builder.includeScreenStateData().includePowerStateData(); } - if (model == BatteryConsumer.POWER_MODEL_POWER_PROFILE) { - builder.powerProfileModeledOnly(); - } if (accumulated) { builder.accumulated(); } @@ -3376,7 +3378,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub dumpPowerProfile(pw); return; } else if ("--usage".equals(arg)) { - int model = BatteryConsumer.POWER_MODEL_UNDEFINED; boolean proto = false; boolean accumulated = false; for (int j = i + 1; j < args.length; j++) { @@ -3384,29 +3385,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub case "--proto": proto = true; break; - case "--model": { - if (j + 1 < args.length) { - j++; - if ("power-profile".equals(args[j])) { - model = BatteryConsumer.POWER_MODEL_POWER_PROFILE; - } else { - pw.println("Unknown power model: " + args[j]); - dumpHelp(pw); - return; - } - } else { - pw.println("--model without a value"); - dumpHelp(pw); - return; - } - break; - } case "--accumulated": accumulated = true; break; } } - dumpUsageStats(fd, pw, model, proto, accumulated); + dumpUsageStats(fd, pw, proto, accumulated); return; } else if ("--wakeups".equals(arg)) { mCpuWakeupStats.dump(new IndentingPrintWriter(pw, " "), diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index da5b1fd1c079..2f5362f53361 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -2122,7 +2122,7 @@ public class CachedAppOptimizer { Slog.d(TAG_AM, "Performing native compaction for pid=" + pid + " type=" + compactProfile.name()); - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactSystem"); + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "compactNative"); try { mProcessDependencies.performCompaction(compactProfile, pid); } catch (Exception e) { diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS index ab7cd5f29660..1a6051b6ac9c 100644 --- a/services/core/java/com/android/server/am/OWNERS +++ b/services/core/java/com/android/server/am/OWNERS @@ -66,6 +66,9 @@ per-file CarUserSwitchingDialog.java = file:platform/packages/services/Car:/OWNE # Activity Security per-file ActivityManager* = file:/ACTIVITY_SECURITY_OWNERS +# Aconfig Flags +per-file flags.aconfig = yamasani@google.com, bills@google.com, nalini@google.com + # Londoners michaelwr@google.com #{LAST_RESORT_SUGGESTION} narayan@google.com #{LAST_RESORT_SUGGESTION} diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index b84bf6b90711..f42641ece09b 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1376,9 +1376,11 @@ public class OomAdjuster { ProcessRecord app = lruList.get(i); final ProcessStateRecord state = app.mState; if (!app.isKilledByAm() && app.getThread() != null) { - // We don't need to apply the update for the process which didn't get computed - if (state.getCompletedAdjSeq() == mAdjSeq) { - applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, true); + if (!Flags.fixApplyOomadjOrder()) { + // We don't need to apply the update for the process which didn't get computed + if (state.getCompletedAdjSeq() == mAdjSeq) { + applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, true); + } } if (app.isPendingFinishAttach()) { @@ -1480,6 +1482,19 @@ public class OomAdjuster { } } + if (Flags.fixApplyOomadjOrder()) { + // We need to apply the update starting from the least recently used. + // Otherwise, they won't be in the correct LRU order in LMKD. + for (int i = 0; i < numLru; i++) { + ProcessRecord app = lruList.get(i); + // We don't need to apply the update for the process which didn't get computed + if (!app.isKilledByAm() && app.getThread() != null + && app.mState.getCompletedAdjSeq() == mAdjSeq) { + applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, true); + } + } + } + if (!mProcsToOomAdj.isEmpty()) { mInjector.batchSetOomAdj(mProcsToOomAdj); mProcsToOomAdj.clear(); diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 78c4f74f3afa..7660c154efd5 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -140,6 +140,8 @@ public class SettingsToPropertiesMapper { // The list is sorted. @VisibleForTesting static final String[] sDeviceConfigAconfigScopes = new String[] { + "aaos_audio_triage", + "aaos_power_triage", "aaos_sdv", "accessibility", "android_core_networking", @@ -533,9 +535,8 @@ public class SettingsToPropertiesMapper { * @param packageName the package of the flag * @param flagName the name of the flag * @param immediate if true, clear immediately; otherwise, clear on next reboot - * - * @hide */ + @VisibleForTesting public static void writeFlagOverrideRemovalRequest( ProtoOutputStream proto, String packageName, String flagName, boolean immediate) { long msgsToken = proto.start(StorageRequestMessages.MSGS); @@ -591,7 +592,8 @@ public class SettingsToPropertiesMapper { * apply flag local override in aconfig new storage * @param props */ - static void setLocalOverridesInNewStorage(DeviceConfig.Properties props) { + @VisibleForTesting + public static void setLocalOverridesInNewStorage(DeviceConfig.Properties props) { int num_requests = 0; ProtoOutputStream requests = new ProtoOutputStream(); for (String flagName : props.getKeyset()) { diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index 6d247d227774..89e4a8d82676 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -245,6 +245,14 @@ flag { } flag { + name: "fix_apply_oomadj_order" + namespace: "backstage_power" + is_fixed_read_only: true + description: "Fix the iteration direction of process LRU list when applying oom adj" + bug: "378580264" +} + +flag { name: "phantom_processes_fix" namespace: "backstage_power" description: "Make sure setProcessGroupForPhantomProcessOfApp deals with phantom processes properly" diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index 8c5152fdb0d6..6f8dc105850d 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -100,6 +100,7 @@ import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.SystemService.TargetUser; +import com.android.server.utils.LazyJniRegistrar; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.CompatScaleProvider; @@ -158,6 +159,10 @@ public final class GameManagerService extends IGameManagerService.Stub { private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME = "game_mode_intervention.list"; + static { + LazyJniRegistrar.registerGameManagerService(); + } + private final Context mContext; private final Object mLock = new Object(); private final Object mDeviceConfigLock = new Object(); diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java index ed41f2e881f8..fa2e674d37c7 100644 --- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java +++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java @@ -31,7 +31,6 @@ import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; -import static android.app.AppOpsManager.OP_TAKE_AUDIO_FOCUS; import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE; import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED; import static android.app.AppOpsManager.UID_STATE_NONEXISTENT; @@ -177,8 +176,6 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker { case OP_RECORD_AUDIO: case OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO: return PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; - case OP_TAKE_AUDIO_FOCUS: - return PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; default: return PROCESS_CAPABILITY_NONE; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 40d5f86f8094..c6317bd29824 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -4954,22 +4954,24 @@ public class AudioService extends IAudioService.Stub } final Set<Integer> deviceTypes = getDeviceSetForStreamDirect(streamType); - final Set<Integer> absVolumeMultiModeCaseDevices = - AudioSystem.intersectionAudioDeviceTypes( - mAbsVolumeMultiModeCaseDevices, deviceTypes); - if (absVolumeMultiModeCaseDevices.isEmpty()) { + Set<Integer> absVolumeDeviceTypes = new ArraySet<>( + AudioSystem.DEVICE_OUT_ALL_A2DP_SET); + absVolumeDeviceTypes.addAll(mAbsVolumeMultiModeCaseDevices); + + final Set<Integer> absVolumeDevices = + AudioSystem.intersectionAudioDeviceTypes(absVolumeDeviceTypes, deviceTypes); + if (absVolumeDevices.isEmpty()) { return; } - if (absVolumeMultiModeCaseDevices.size() > 1) { + if (absVolumeDevices.size() > 1) { Log.w(TAG, "onUpdateContextualVolumes too many active devices: " - + absVolumeMultiModeCaseDevices.stream().map(AudioSystem::getOutputDeviceName) + + absVolumeDevices.stream().map(AudioSystem::getOutputDeviceName) .collect(Collectors.joining(",")) + ", for stream: " + streamType); return; } - final int device = absVolumeMultiModeCaseDevices.toArray(new Integer[0])[0].intValue(); - + final int device = absVolumeDevices.toArray(new Integer[0])[0].intValue(); final int index = getStreamVolume(streamType, device); if (DEBUG_VOL) { @@ -4983,6 +4985,8 @@ public class AudioService extends IAudioService.Stub getVssForStreamOrDefault(streamType).getMaxIndex(), streamType); } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) { mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType); + } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)) { + mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index); } else { return; } @@ -11425,6 +11429,10 @@ public class AudioService extends IAudioService.Stub return mSpatializerHelper.canBeSpatialized(attributes, format); } + public @NonNull List<Integer> getSpatializedChannelMasks() { + return mSpatializerHelper.getSpatializedChannelMasks(); + } + /** @see Spatializer.SpatializerInfoDispatcherStub */ public void registerSpatializerCallback( @NonNull ISpatializerCallback cb) { diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index a62ac82f27eb..1c01fb9f19e0 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -17,13 +17,14 @@ package com.android.server.audio; import static android.media.AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_MUTE; -import static android.media.AudioPlaybackConfiguration.MUTED_BY_APP_OPS; +import static android.media.AudioPlaybackConfiguration.MUTED_BY_OP_PLAY_AUDIO; import static android.media.AudioPlaybackConfiguration.MUTED_BY_CLIENT_VOLUME; import static android.media.AudioPlaybackConfiguration.MUTED_BY_MASTER; import static android.media.AudioPlaybackConfiguration.MUTED_BY_PORT_VOLUME; import static android.media.AudioPlaybackConfiguration.MUTED_BY_STREAM_MUTED; import static android.media.AudioPlaybackConfiguration.MUTED_BY_STREAM_VOLUME; import static android.media.AudioPlaybackConfiguration.MUTED_BY_VOLUME_SHAPER; +import static android.media.AudioPlaybackConfiguration.MUTED_BY_OP_CONTROL_AUDIO; import static android.media.AudioPlaybackConfiguration.PLAYER_PIID_INVALID; import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED; @@ -1376,8 +1377,8 @@ public final class PlaybackActivityMonitor if ((eventValue & MUTED_BY_STREAM_MUTED) != 0) { builder.append("streamMute "); } - if ((eventValue & MUTED_BY_APP_OPS) != 0) { - builder.append("appOps "); + if ((eventValue & MUTED_BY_OP_PLAY_AUDIO) != 0) { + builder.append("opPlayAudio "); } if ((eventValue & MUTED_BY_CLIENT_VOLUME) != 0) { builder.append("clientVolume "); @@ -1388,6 +1389,10 @@ public final class PlaybackActivityMonitor if ((eventValue & MUTED_BY_PORT_VOLUME) != 0) { builder.append("portVolume "); } + if ((eventValue & MUTED_BY_OP_CONTROL_AUDIO) != 0) { + builder.append("opControlAudio "); + } + } return builder.toString(); default: diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index 9265ff2d9b2d..afa90d5869e3 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -59,6 +59,8 @@ import com.android.server.utils.EventLogger; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.UUID; @@ -1100,6 +1102,23 @@ public class SpatializerHelper { return able; } + synchronized @NonNull List<Integer> getSpatializedChannelMasks() { + if (!checkSpatializer("getSpatializedChannelMasks")) { + return Collections.emptyList(); + } + try { + final int[] nativeMasks = new int[0]; // FIXME mSpat query goes here + for (int i = 0; i < nativeMasks.length; i++) { + nativeMasks[i] = AudioFormat.convertNativeChannelMaskToOutMask(nativeMasks[i]); + } + final List<Integer> masks = Arrays.stream(nativeMasks).boxed().toList(); + return masks; + } catch (Exception e) { // just catch Exception in case nativeMasks is null + Log.e(TAG, "Error calling getSpatializedChannelMasks", e); + return Collections.emptyList(); + } + } + //------------------------------------------------------ // head tracking final RemoteCallbackList<ISpatializerHeadTrackingModeCallback> mHeadTrackingModeCallbacks = diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 97a88542f0a4..b365ef7ff61c 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -434,21 +434,12 @@ public class BiometricService extends SystemService { public boolean getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(int userId) { if (!mMandatoryBiometricsEnabled.containsKey(userId)) { - Slog.d(TAG, "update mb toggle for user " + userId); updateMandatoryBiometricsForAllProfiles(userId); } if (!mMandatoryBiometricsRequirementsSatisfied.containsKey(userId)) { - Slog.d(TAG, "update mb reqs for user " + userId); updateMandatoryBiometricsRequirementsForAllProfiles(userId); } - Slog.d(TAG, mMandatoryBiometricsEnabled.getOrDefault(userId, - DEFAULT_MANDATORY_BIOMETRICS_STATUS) - + " " + mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId, - DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS) - + " " + getEnabledForApps(userId) - + " " + (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */) - || mFaceEnrolledForUser.getOrDefault(userId, false /* default */))); return mMandatoryBiometricsEnabled.getOrDefault(userId, DEFAULT_MANDATORY_BIOMETRICS_STATUS) && mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId, diff --git a/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java b/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java index eed2bddb0312..958ab8ee0b6a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java +++ b/services/core/java/com/android/server/biometrics/sensors/PerformanceTracker.java @@ -18,6 +18,8 @@ package com.android.server.biometrics.sensors; import android.util.SparseArray; +import java.util.concurrent.ConcurrentHashMap; + /** * Tracks biometric performance across sensors and users. */ @@ -25,17 +27,12 @@ public class PerformanceTracker { private static final String TAG = "PerformanceTracker"; // Keyed by SensorId - private static SparseArray<PerformanceTracker> sTrackers; + private static final ConcurrentHashMap<Integer, PerformanceTracker> sTrackers = + new ConcurrentHashMap<>(); public static PerformanceTracker getInstanceForSensorId(int sensorId) { - if (sTrackers == null) { - sTrackers = new SparseArray<>(); - } - - if (!sTrackers.contains(sensorId)) { - sTrackers.put(sensorId, new PerformanceTracker()); - } - return sTrackers.get(sensorId); + PerformanceTracker tracker = sTrackers.putIfAbsent(sensorId, new PerformanceTracker()); + return tracker != null ? tracker : sTrackers.get(sensorId); } private static class Info { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 36e4a7e6a7ef..944e85cc970e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -851,13 +851,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { for (int i = 0; i < mFaceSensors.size(); i++) { final Sensor sensor = mFaceSensors.valueAt(i); final int sensorId = mFaceSensors.keyAt(i); - final PerformanceTracker performanceTracker = PerformanceTracker.getInstanceForSensorId( - sensorId); - if (performanceTracker != null) { - performanceTracker.incrementHALDeathCount(); - } else { - Slog.w(getTag(), "Performance tracker is null. Not counting HAL death."); - } + PerformanceTracker.getInstanceForSensorId(sensorId) + .incrementHALDeathCount(); sensor.onBinderDied(); } }); diff --git a/services/core/java/com/android/server/crashrecovery/CrashRecoveryHelper.java b/services/core/java/com/android/server/crashrecovery/CrashRecoveryHelper.java index 8e725465ddd6..eef2b15c3387 100644 --- a/services/core/java/com/android/server/crashrecovery/CrashRecoveryHelper.java +++ b/services/core/java/com/android/server/crashrecovery/CrashRecoveryHelper.java @@ -93,7 +93,8 @@ public final class CrashRecoveryHelper { return; } final List<VersionedPackage> pkgList = Collections.singletonList(pkg); - PackageWatchdog.getInstance(mContext).onPackageFailure(pkgList, PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); + PackageWatchdog.getInstance(mContext).notifyPackageFailure(pkgList, + PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); }); } diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index d2c044fdbb5e..1c1bdad01034 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -311,6 +311,11 @@ final class DisplayDeviceInfo { */ public FrameRateCategoryRate frameRateCategoryRate; /** + * All the refresh rates supported for the default display mode. + */ + public float[] supportedRefreshRates = new float[0]; + + /** * The default mode of the display. */ public int defaultModeId; @@ -562,7 +567,8 @@ final class DisplayDeviceInfo { || installOrientation != other.installOrientation || !Objects.equals(displayShape, other.displayShape) || hasArrSupport != other.hasArrSupport - || !Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate)) { + || !Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate) + || !Arrays.equals(supportedRefreshRates, other.supportedRefreshRates)) { diff |= DIFF_OTHER; } return diff; @@ -582,6 +588,7 @@ final class DisplayDeviceInfo { renderFrameRate = other.renderFrameRate; hasArrSupport = other.hasArrSupport; frameRateCategoryRate = other.frameRateCategoryRate; + supportedRefreshRates = other.supportedRefreshRates; defaultModeId = other.defaultModeId; userPreferredModeId = other.userPreferredModeId; supportedModes = other.supportedModes; @@ -628,6 +635,7 @@ final class DisplayDeviceInfo { sb.append(", renderFrameRate ").append(renderFrameRate); sb.append(", hasArrSupport ").append(hasArrSupport); sb.append(", frameRateCategoryRate ").append(frameRateCategoryRate); + sb.append(", supportedRefreshRates ").append(Arrays.toString(supportedRefreshRates)); sb.append(", defaultModeId ").append(defaultModeId); sb.append(", userPreferredModeId ").append(userPreferredModeId); sb.append(", supportedModes ").append(Arrays.toString(supportedModes)); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index ae74dbecc5f8..3871f2a57f76 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -17,6 +17,7 @@ package com.android.server.display; import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY; +import static android.Manifest.permission.ADD_MIRROR_DISPLAY; import static android.Manifest.permission.ADD_TRUSTED_DISPLAY; import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT; import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT; @@ -1677,15 +1678,10 @@ public final class DisplayManagerService extends SystemService { } private boolean canCreateMirrorDisplays(IVirtualDevice virtualDevice) { - if (virtualDevice == null) { - return false; - } - try { - return virtualDevice.canCreateMirrorDisplays(); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to query virtual device for permissions", e); - return false; + if (android.companion.virtualdevice.flags.Flags.enableLimitedVdmRole()) { + return checkCallingPermission(ADD_MIRROR_DISPLAY, "canCreateMirrorDisplays"); } + return virtualDevice != null; } private boolean canProjectVideo(IMediaProjection projection) { @@ -1818,7 +1814,7 @@ public final class DisplayManagerService extends SystemService { // display. if (!canProjectVideo(projection) && !canCreateMirrorDisplays(virtualDevice) && !hasVideoOutputPermission("createVirtualDisplayInternal")) { - throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or " + throw new SecurityException("Requires ADD_MIRROR_DISPLAY, CAPTURE_VIDEO_OUTPUT or " + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate " + "MediaProjection token in order to create a screen sharing virtual " + "display. In order to create a virtual display that does not perform " diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index a4bb8c348d3f..0b8f7d5ef2cf 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -249,6 +249,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private int mActiveColorMode; private boolean mHasArrSupport; private FrameRateCategoryRate mFrameRateCategoryRate; + private float[] mSupportedRefreshRates = new float[0]; private Display.HdrCapabilities mHdrCapabilities; private boolean mAllmSupported; private boolean mGameContentTypeSupported; @@ -316,6 +317,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { changed |= updateGameContentTypeSupport(dynamicInfo.gameContentTypeSupported); changed |= updateHasArrSupportLocked(dynamicInfo.hasArrSupport); changed |= updateFrameRateCategoryRatesLocked(dynamicInfo.frameRateCategoryRate); + changed |= updateSupportedRefreshatesLocked(dynamicInfo.supportedRefreshRates); if (changed) { mHavePendingChanges = true; @@ -624,6 +626,20 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + private boolean updateSupportedRefreshatesLocked(float[] supportedRefreshRates) { + if (!getFeatureFlags().enableGetSupportedRefreshRates()) { + return false; + } + if (supportedRefreshRates == null) { + return false; + } + if (Arrays.equals(mSupportedRefreshRates, supportedRefreshRates)) { + return false; + } + mSupportedRefreshRates = supportedRefreshRates; + return true; + } + private boolean updateAllmSupport(boolean supported) { if (mAllmSupported == supported) { return false; @@ -708,6 +724,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.hdrCapabilities = mHdrCapabilities; mInfo.hasArrSupport = mHasArrSupport; mInfo.frameRateCategoryRate = mFrameRateCategoryRate; + mInfo.supportedRefreshRates = mSupportedRefreshRates; mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos; mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos; mInfo.state = mState; @@ -1299,6 +1316,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mDefaultModeId=" + mDefaultModeId); pw.println("mUserPreferredModeId=" + mUserPreferredModeId); pw.println("mHasArrSupport=" + mHasArrSupport); + pw.println("mSupportedRefreshRates=" + Arrays.toString(mSupportedRefreshRates)); pw.println("mState=" + Display.stateToString(mState)); pw.println("mCommittedState=" + Display.stateToString(mCommittedState)); pw.println("mBrightnessState=" + mBrightnessState); diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 7cfdcafcb610..85465981c473 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -508,6 +508,8 @@ final class LogicalDisplay { mBaseDisplayInfo.renderFrameRate = deviceInfo.renderFrameRate; mBaseDisplayInfo.hasArrSupport = deviceInfo.hasArrSupport; mBaseDisplayInfo.frameRateCategoryRate = deviceInfo.frameRateCategoryRate; + mBaseDisplayInfo.supportedRefreshRates = Arrays.copyOf( + deviceInfo.supportedRefreshRates, deviceInfo.supportedRefreshRates.length); mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId; mBaseDisplayInfo.userPreferredModeId = deviceInfo.userPreferredModeId; mBaseDisplayInfo.supportedModes = Arrays.copyOf( diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index 7f46bbbf0507..45106f54cb9f 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -243,6 +243,11 @@ public class DisplayManagerFlags { Flags::autoBrightnessModeBedtimeWear ); + private final FlagState mGetSupportedRefreshRatesFlagState = new FlagState( + Flags.FLAG_ENABLE_GET_SUPPORTED_REFRESH_RATES, + Flags::enableGetSupportedRefreshRates + ); + private final FlagState mEnablePluginManagerFlagState = new FlagState( Flags.FLAG_ENABLE_PLUGIN_MANAGER, Flags::enablePluginManager @@ -252,6 +257,11 @@ public class DisplayManagerFlags { Flags::displayListenerPerformanceImprovements ); + private final FlagState mSubscribeGranularDisplayEvents = new FlagState( + Flags.FLAG_SUBSCRIBE_GRANULAR_DISPLAY_EVENTS, + Flags::subscribeGranularDisplayEvents + ); + /** * @return {@code true} if 'port' is allowed in display layout configuration file. */ @@ -528,6 +538,13 @@ public class DisplayManagerFlags { return mAutoBrightnessModeBedtimeWearFlagState.isEnabled(); } + /** + * @return {@code true} if supported refresh rate api is enabled. + */ + public boolean enableGetSupportedRefreshRates() { + return mGetSupportedRefreshRatesFlagState.isEnabled(); + } + public boolean isPluginManagerEnabled() { return mEnablePluginManagerFlagState.isEnabled(); } @@ -540,6 +557,13 @@ public class DisplayManagerFlags { } /** + * @return {@code true} if the flag for subscribing to granular display events is enabled + */ + public boolean isSubscribeGranularDisplayEventsEnabled() { + return mSubscribeGranularDisplayEvents.isEnabled(); + } + + /** * dumps all flagstates * @param pw printWriter */ @@ -590,8 +614,10 @@ public class DisplayManagerFlags { pw.println(" " + mIsUserRefreshRateForExternalDisplayEnabled); pw.println(" " + mHasArrSupport); pw.println(" " + mAutoBrightnessModeBedtimeWearFlagState); + pw.println(" " + mGetSupportedRefreshRatesFlagState); pw.println(" " + mEnablePluginManagerFlagState); pw.println(" " + mDisplayListenerPerformanceImprovementsFlagState); + pw.println(" " + mSubscribeGranularDisplayEvents); } private static class FlagState { diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index e7ea868ca04f..3976d01d806d 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -478,3 +478,14 @@ flag { bug: "378385869" is_fixed_read_only: true } + +flag { + name: "subscribe_granular_display_events" + namespace: "display_manager" + description: "Enable subscription to granular display change events." + bug: "379250634" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index 0e7d2b631833..a06f9ef634d1 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -524,6 +524,13 @@ final class Constants { static final String PROPERTY_DISABLE_CEC_ON_STANDBY_IN_LOW_ENERGY_MODE = "persist.sys.hdmi.property_disable_cec_on_standby_in_low_energy_mode"; + /** + * Property that checks if CEC was manually enabled by the user in offline mode. With the help + * of this property we avoid turning off CEC when the device goes to sleep and if the device + * is in low energy mode. + */ + static final String PROPERTY_USER_ACTION_KEEP_CEC_ENABLED_IN_OFFLINE_MODE = + "persist.sys.hdmi.property_user_action_keep_cec_enabled_in_offline_mode"; static final int RECORDING_TYPE_DIGITAL_RF = 1; static final int RECORDING_TYPE_ANALOGUE_RF = 2; static final int RECORDING_TYPE_EXTERNAL_PHYSICAL_ADDRESS = 3; diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 0c5069f81774..6e98bff8dda5 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -4027,7 +4027,8 @@ public class HdmiControlService extends SystemService { return; } if (isTvDevice() && getDisableCecOnStandbyByLowEnergyMode() - && mPowerManager.isLowPowerStandbyEnabled()) { + && mPowerManager.isLowPowerStandbyEnabled() + && !userEnabledCecInOfflineMode()) { Slog.w(TAG, "Disable CEC on standby due to low power energy mode."); setWasCecDisabledOnStandbyByLowEnergyMode(true); getHdmiCecConfig().setIntValue( @@ -5225,4 +5226,14 @@ public class HdmiControlService extends SystemService { Constants.PROPERTY_WAS_CEC_DISABLED_ON_STANDBY_BY_LOW_ENERGY_MODE, String.valueOf(value)); } + + /** + * Reads the property that checks if CEC was enabled by the user while in offline mode such that + * it won't be disabled when going to sleep by low energy mode. + */ + @VisibleForTesting + protected boolean userEnabledCecInOfflineMode() { + return SystemProperties.getBoolean( + Constants.PROPERTY_USER_ACTION_KEEP_CEC_ENABLED_IN_OFFLINE_MODE, false); + } } diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java index d13244905633..da2089131b9f 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java @@ -19,13 +19,19 @@ package com.android.server.inputmethod; import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import android.annotation.NonNull; +import android.content.Context; +import android.graphics.Rect; +import android.graphics.Region; import android.os.InputConfig; import android.os.Process; +import android.util.Slog; import android.view.InputApplicationHandle; import android.view.InputChannel; import android.view.InputWindowHandle; import android.view.SurfaceControl; import android.view.WindowManager; +import android.view.WindowMetrics; +import android.view.inputmethod.Flags; import com.android.server.input.InputManagerService; @@ -39,8 +45,8 @@ final class HandwritingEventReceiverSurface { private final SurfaceControl mInputSurface; private boolean mIsIntercepting; - HandwritingEventReceiverSurface(String name, int displayId, @NonNull SurfaceControl sc, - @NonNull InputChannel inputChannel) { + HandwritingEventReceiverSurface(Context context, String name, int displayId, + @NonNull SurfaceControl sc, @NonNull InputChannel inputChannel) { mClientChannel = inputChannel; mInputSurface = sc; @@ -59,15 +65,31 @@ final class HandwritingEventReceiverSurface { | InputConfig.SPY | InputConfig.INTERCEPTS_STYLUS; - // Configure the surface to receive stylus events across the entire display. - mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); + Rect bounds = null; + if (Flags.adaptiveHandwritingBounds()) { + mWindowHandle.setTouchableRegionCrop(mInputSurface); + // Default touchable area to getMaximumWindowMetrics() + WindowMetrics windowMetrics = context.getSystemService(WindowManager.class) + .getMaximumWindowMetrics(); + bounds = windowMetrics.getBounds(); + if (DEBUG) Slog.d(TAG, "initial handwriting touchable bounds: " + bounds); + mWindowHandle.setTouchableRegion(windowMetrics.getBounds()); + } else { + // Configure the surface to receive stylus events across the entire display. + mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); + } final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); mWindowHandle.setTrustedOverlay(t, mInputSurface, true); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setLayer(mInputSurface, InputManagerService.INPUT_OVERLAY_LAYER_HANDWRITING_SURFACE); t.setPosition(mInputSurface, 0, 0); - t.setCrop(mInputSurface, null /* crop to parent surface */); + if (Flags.adaptiveHandwritingBounds()) { + // crop to parent surface if null, else bounds. + t.setCrop(mInputSurface, bounds); + } else { + t.setCrop(mInputSurface, null /* crop to parent surface */); + } t.show(mInputSurface); t.apply(); @@ -79,12 +101,23 @@ final class HandwritingEventReceiverSurface { mWindowHandle.ownerUid = imeUid; mWindowHandle.inputConfig &= ~InputConfig.SPY; + if (Flags.adaptiveHandwritingBounds()) { + // watch outside touch to finish handwriting. + mWindowHandle.inputConfig |= InputConfig.WATCH_OUTSIDE_TOUCH; + } new SurfaceControl.Transaction() .setInputWindowInfo(mInputSurface, mWindowHandle) .apply(); mIsIntercepting = true; } + void setTouchableRegion(Region touchableRegion) { + mWindowHandle.touchableRegion.set(touchableRegion); + new SurfaceControl.Transaction() + .setInputWindowInfo(mInputSurface, mWindowHandle) + .apply(); + } + void setNotTouchable(boolean notTouchable) { if (notTouchable) { mWindowHandle.inputConfig |= InputConfig.NOT_TOUCHABLE; diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java index c19cb030ef37..45d7d0464fe1 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java @@ -27,6 +27,7 @@ import android.annotation.UiThread; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManagerInternal; +import android.graphics.Region; import android.hardware.input.InputManager; import android.hardware.input.InputManagerGlobal; import android.os.Handler; @@ -139,7 +140,7 @@ final class HandwritingModeController { } mHandwritingSurface = new HandwritingEventReceiverSurface( - name, displayId, surface, channel); + mContext, name, displayId, surface, channel); // Use a dup of the input channel so that event processing can be paused by disposing the // event receiver without causing a fd hangup. @@ -163,6 +164,13 @@ final class HandwritingModeController { mHandwritingSurface.setNotTouchable(notTouchable); } + void setHandwritingTouchableRegion(Region region) { + if (!getCurrentRequestId().isPresent()) { + return; + } + mHandwritingSurface.setTouchableRegion(region); + } + boolean isStylusGestureOngoing() { if (mRecordingGestureAfterStylusUp && !mHandwritingBuffer.isEmpty()) { // If it is less than AFTER_STYLUS_UP_ALLOW_PERIOD_MS after the stylus up event, return diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index 76049ca824c2..d177d0e5f2eb 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -34,6 +34,7 @@ import android.hardware.contexthub.ErrorCode; import android.hardware.contexthub.HubEndpointInfo; import android.hardware.contexthub.IContextHubEndpoint; import android.hardware.contexthub.IContextHubEndpointCallback; +import android.hardware.contexthub.IContextHubEndpointDiscoveryCallback; import android.hardware.contexthub.MessageDeliveryStatus; import android.hardware.location.ContextHubInfo; import android.hardware.location.ContextHubMessage; @@ -793,6 +794,31 @@ public class ContextHubService extends IContextHubService.Stub { return null; } + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) + @Override + public void registerEndpointDiscoveryCallbackId( + long endpointId, IContextHubEndpointDiscoveryCallback callback) throws RemoteException { + super.registerEndpointDiscoveryCallbackId_enforcePermission(); + // TODO(b/375487784): Implement this + } + + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) + @Override + public void registerEndpointDiscoveryCallbackDescriptor( + String serviceDescriptor, IContextHubEndpointDiscoveryCallback callback) + throws RemoteException { + super.registerEndpointDiscoveryCallbackDescriptor_enforcePermission(); + // TODO(b/375487784): Implement this + } + + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) + @Override + public void unregisterEndpointDiscoveryCallback(IContextHubEndpointDiscoveryCallback callback) + throws RemoteException { + super.unregisterEndpointDiscoveryCallback_enforcePermission(); + // TODO(b/375487784): Implement this + } + /** * Creates an internal load transaction callback to be used for old API clients * diff --git a/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java index 6c74cba99bcb..05fe78114eca 100644 --- a/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java +++ b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java @@ -30,7 +30,8 @@ public class FrameworkStatsLogWrapper { int hostUid, int targetUid, int timeSinceLastActive, - int creationSource) { + int creationSource, + int stopSource) { FrameworkStatsLog.write( code, sessionId, @@ -39,7 +40,8 @@ public class FrameworkStatsLogWrapper { hostUid, targetUid, timeSinceLastActive, - creationSource); + creationSource, + stopSource); } /** Wrapper around {@link FrameworkStatsLog#write} for MediaProjectionTargetChanged atom. */ @@ -49,13 +51,23 @@ public class FrameworkStatsLogWrapper { int targetType, int hostUid, int targetUid, - int windowingMode) { + int windowingMode, + int width, + int height, + int centerX, + int centerY, + int targetChangeType) { FrameworkStatsLog.write( code, sessionId, targetType, hostUid, targetUid, - windowingMode); + windowingMode, + width, + height, + centerX, + centerY, + targetChangeType); } } diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index df5ecf872df4..c428f39fd9d0 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -51,6 +51,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.media.MediaRouter; import android.media.projection.IMediaProjection; @@ -60,6 +61,7 @@ import android.media.projection.IMediaProjectionWatcherCallback; import android.media.projection.MediaProjectionInfo; import android.media.projection.MediaProjectionManager; import android.media.projection.ReviewGrantedConsentResult; +import android.media.projection.StopReason; import android.os.Binder; import android.os.Build; import android.os.Handler; @@ -178,7 +180,7 @@ public final class MediaProjectionManagerService extends SystemService if (!mMediaProjectionStopController.isExemptFromStopping(mProjectionGrant, reason)) { Slog.d(TAG, "Content Recording: Stopping MediaProjection due to " + MediaProjectionStopController.stopReasonToString(reason)); - mProjectionGrant.stop(); + mProjectionGrant.stop(StopReason.STOP_DEVICE_LOCKED); } } } @@ -257,7 +259,7 @@ public final class MediaProjectionManagerService extends SystemService synchronized (mLock) { if (mProjectionGrant != null) { Slog.d(TAG, "Content Recording: Stopped MediaProjection due to user switching"); - mProjectionGrant.stop(); + mProjectionGrant.stop(StopReason.STOP_USER_SWITCH); } } } @@ -295,7 +297,7 @@ public final class MediaProjectionManagerService extends SystemService Slog.d(TAG, "Content Recording: Stopped MediaProjection due to foreground service change"); if (mProjectionGrant != null) { - mProjectionGrant.stop(); + mProjectionGrant.stop(StopReason.STOP_FOREGROUND_SERVICE_CHANGE); } } } @@ -304,7 +306,7 @@ public final class MediaProjectionManagerService extends SystemService if (mProjectionGrant != null) { Slog.d(TAG, "Content Recording: Stopped MediaProjection to start new " + "incoming projection"); - mProjectionGrant.stop(); + mProjectionGrant.stop(StopReason.STOP_NEW_PROJECTION); } if (mMediaRouteInfo != null) { mMediaRouter.getFallbackRoute().select(); @@ -314,7 +316,10 @@ public final class MediaProjectionManagerService extends SystemService dispatchStart(projection); } - private void stopProjectionLocked(final MediaProjection projection) { + private void stopProjectionLocked( + final MediaProjection projection, + @StopReason int stopReason + ) { Slog.d(TAG, "Content Recording: Stopped active MediaProjection and " + "dispatching stop to callbacks"); ContentRecordingSession session = projection.mSession; @@ -322,7 +327,7 @@ public final class MediaProjectionManagerService extends SystemService session != null ? session.getTargetUid() : ContentRecordingSession.TARGET_UID_UNKNOWN; - mMediaProjectionMetricsLogger.logStopped(projection.uid, targetUid); + mMediaProjectionMetricsLogger.logStopped(projection.uid, targetUid, stopReason); mProjectionToken = null; mProjectionGrant = null; dispatchStop(projection); @@ -403,7 +408,7 @@ public final class MediaProjectionManagerService extends SystemService + "ContentRecordingSession - id= " + mProjectionGrant.getVirtualDisplayId() + "type=" + projectionType); - mProjectionGrant.stop(); + mProjectionGrant.stop(StopReason.STOP_ERROR); } return false; } @@ -517,6 +522,18 @@ public final class MediaProjectionManagerService extends SystemService } } + @VisibleForTesting + void notifyCaptureBoundsChanged(int contentToRecord, int targetUid, Rect captureBounds) { + synchronized (mLock) { + if (mProjectionGrant == null) { + Slog.i(TAG, "Cannot log MediaProjectionTargetChanged atom due to null projection"); + } else { + mMediaProjectionMetricsLogger.logChangedCaptureBounds( + contentToRecord, mProjectionGrant.uid, targetUid, captureBounds); + } + } + } + /** * Handles result of dialog shown from * {@link BinderService#buildReviewGrantedConsentIntentLocked()}. @@ -557,7 +574,7 @@ public final class MediaProjectionManagerService extends SystemService Slog.w(TAG, "Content Recording: Stopped MediaProjection due to user " + "consent result of CANCEL - " + "id= " + mProjectionGrant.getVirtualDisplayId()); - mProjectionGrant.stop(); + mProjectionGrant.stop(StopReason.STOP_ERROR); } break; case RECORD_CONTENT_DISPLAY: @@ -773,14 +790,14 @@ public final class MediaProjectionManagerService extends SystemService @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @Override // Binder call - public void stopActiveProjection() { + public void stopActiveProjection(@StopReason int stopReason) { stopActiveProjection_enforcePermission(); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { if (mProjectionGrant != null) { Slog.d(TAG, "Content Recording: Stopping active projection"); - mProjectionGrant.stop(); + mProjectionGrant.stop(stopReason); } } } finally { @@ -790,8 +807,9 @@ public final class MediaProjectionManagerService extends SystemService @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @Override // Binder call - public void notifyActiveProjectionCapturedContentResized(int width, int height) { - notifyActiveProjectionCapturedContentResized_enforcePermission(); + public void notifyCaptureBoundsChanged( + int contentToRecord, int targetProcessUid, Rect newBounds) { + notifyCaptureBoundsChanged_enforcePermission(); synchronized (mLock) { if (!isCurrentProjection(mProjectionGrant)) { return; @@ -801,7 +819,13 @@ public final class MediaProjectionManagerService extends SystemService try { synchronized (mLock) { if (mProjectionGrant != null && mCallbackDelegate != null) { - mCallbackDelegate.dispatchResize(mProjectionGrant, width, height); + // log metrics for the new bounds + MediaProjectionManagerService.this.notifyCaptureBoundsChanged( + contentToRecord, targetProcessUid, newBounds); + + // update clients of the new update bounds + mCallbackDelegate.dispatchResize( + mProjectionGrant, newBounds.width(), newBounds.height()); } } } finally { @@ -1133,7 +1157,7 @@ public final class MediaProjectionManagerService extends SystemService Slog.d(TAG, "Content Recording: MediaProjection stopped by Binder death - " + "id= " + mVirtualDisplayId); mCallbackDelegate.remove(callback); - stop(); + stop(StopReason.STOP_TARGET_REMOVED); }; mToken.linkToDeath(mDeathEater, 0); } catch (RemoteException e) { @@ -1182,7 +1206,7 @@ public final class MediaProjectionManagerService extends SystemService } @Override // Binder call - public void stop() { + public void stop(@StopReason int stopReason) { synchronized (mLock) { if (!isCurrentProjection(asBinder())) { Slog.w(TAG, "Attempted to stop inactive MediaProjection " @@ -1211,7 +1235,7 @@ public final class MediaProjectionManagerService extends SystemService Slog.d(TAG, "Content Recording: handling stopping this projection token" + " createTime= " + mCreateTimeMillis + " countStarts= " + mCountStarts); - stopProjectionLocked(this); + stopProjectionLocked(this, stopReason); mToken.unlinkToDeath(mDeathEater, 0); mToken = null; unregisterCallback(mCallback); @@ -1295,7 +1319,7 @@ public final class MediaProjectionManagerService extends SystemService Slog.v(TAG, "Reusing token: Throw exception due to invalid projection."); // Tear down projection here; necessary to ensure (among other reasons) that // stop is dispatched to client and cast icon disappears from status bar. - mProjectionGrant.stop(); + mProjectionGrant.stop(StopReason.STOP_ERROR); throw new SecurityException("Don't re-use the resultData to retrieve " + "the same projection instance, and don't use a token that has " + "timed out. Don't take multiple captures by invoking " @@ -1315,10 +1339,9 @@ public final class MediaProjectionManagerService extends SystemService Slog.w(TAG, "Content Recording: MediaProjection start disallowed, aborting " + "MediaProjection"); - stop(); + stop(StopReason.STOP_DEVICE_LOCKED); return; } - mVirtualDisplayId = displayId; // If prior session was does not have a valid display id, then update the display @@ -1357,7 +1380,7 @@ public final class MediaProjectionManagerService extends SystemService if (mProjectionGrant != null) { Slog.d(TAG, "Content Recording: Stopped MediaProjection due to " + "route type of REMOTE_DISPLAY not selected"); - mProjectionGrant.stop(); + mProjectionGrant.stop(StopReason.STOP_NEW_MEDIA_ROUTE); } } } diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java index be2a25a755a5..8e5c01685275 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java @@ -19,15 +19,34 @@ package com.android.server.media.projection; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY; import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_DEVICE_LOCK; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_ERROR; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_FOREGROUND_SERVICE_CHANGE; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_HOST_APP_STOP; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_MEDIA_ROUTE; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_PROJECTION; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_QS_TILE; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_STATUS_BAR_CHIP_STOP; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_TASK_APP_CLOSE; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_USER_SWITCH; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_BOUNDS; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_POSITION; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_WINDOWING_MODE; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN; @@ -36,8 +55,12 @@ import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGE import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN; +import android.app.WindowConfiguration; import android.app.WindowConfiguration.WindowingMode; import android.content.Context; +import android.graphics.Point; +import android.graphics.Rect; +import android.media.projection.StopReason; import android.util.Log; import android.view.ContentRecordingSession.RecordContent; @@ -59,8 +82,10 @@ public class MediaProjectionMetricsLogger { private final MediaProjectionSessionIdGenerator mSessionIdGenerator; private final MediaProjectionTimestampStore mTimestampStore; - private int mPreviousState = - FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN; + private final Rect mPreviousTargetBounds = new Rect(); + private int mPreviousTargetWindowingMode = WINDOWING_MODE_UNDEFINED; + private int mPreviousProjectionState = + MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN; MediaProjectionMetricsLogger( FrameworkStatsLogWrapper frameworkStatsLogWrapper, @@ -113,7 +138,8 @@ public class MediaProjectionMetricsLogger { hostUid, TARGET_UID_UNKNOWN, timeSinceLastActiveInSeconds, - sessionCreationSource); + sessionCreationSource, + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); } /** @@ -130,7 +156,8 @@ public class MediaProjectionMetricsLogger { hostUid, TARGET_UID_UNKNOWN, TIME_SINCE_LAST_ACTIVE_UNKNOWN, - MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN); + MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN, + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); } /** @@ -141,13 +168,12 @@ public class MediaProjectionMetricsLogger { public void logProjectionPermissionRequestCancelled(int hostUid) { writeStateChanged( mSessionIdGenerator.getCurrentSessionId(), - FrameworkStatsLog - .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED, + MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED, hostUid, TARGET_UID_UNKNOWN, TIME_SINCE_LAST_ACTIVE_UNKNOWN, - FrameworkStatsLog - .MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN); + MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN, + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); } /** @@ -163,7 +189,8 @@ public class MediaProjectionMetricsLogger { hostUid, TARGET_UID_UNKNOWN, TIME_SINCE_LAST_ACTIVE_UNKNOWN, - MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN); + MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN, + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); } /** @@ -180,7 +207,8 @@ public class MediaProjectionMetricsLogger { hostUid, targetUid, TIME_SINCE_LAST_ACTIVE_UNKNOWN, - MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN); + MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN, + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); } /** @@ -196,14 +224,63 @@ public class MediaProjectionMetricsLogger { */ public void logChangedWindowingMode( int contentToRecord, int hostUid, int targetUid, int windowingMode) { - Log.d(TAG, "logChangedWindowingMode"); + Log.d(TAG, "logChangedWindowingMode: windowingMode= " + + WindowConfiguration.windowingModeToString(windowingMode)); + Log.d(TAG, "targetChangeType= changeWindowingMode"); writeTargetChanged( mSessionIdGenerator.getCurrentSessionId(), contentToRecordToTargetType(contentToRecord), hostUid, targetUid, - windowingModeToTargetWindowingMode(windowingMode)); + windowingModeToTargetWindowingMode(windowingMode), + mPreviousTargetBounds.width(), + mPreviousTargetBounds.height(), + mPreviousTargetBounds.centerX(), + mPreviousTargetBounds.centerY(), + MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_WINDOWING_MODE); + mPreviousTargetWindowingMode = windowingMode; + } + /** + * Logs that the bounds of projection's capture target has changed. + * + * @param contentToRecord ContentRecordingSession.RecordContent indicating whether it is a + * task capture or display capture - gets converted to the corresponding + * TargetType before being logged. + * @param hostUid UID of the package that initiates MediaProjection. + * @param targetUid UID of the package that is captured if selected. + * @param captureBounds Updated bounds of the captured region. + */ + public void logChangedCaptureBounds( + int contentToRecord, int hostUid, int targetUid, Rect captureBounds) { + final Point capturePosition = new Point(captureBounds.centerX(), captureBounds.centerY()); + Log.d(TAG, "logChangedCaptureBounds: captureBounds= " + captureBounds + " position= " + + capturePosition); + + writeTargetChanged( + mSessionIdGenerator.getCurrentSessionId(), + contentToRecordToTargetType(contentToRecord), + hostUid, + targetUid, + mPreviousTargetWindowingMode, + captureBounds.width(), + captureBounds.height(), + captureBounds.centerX(), + captureBounds.centerY(), + captureBoundsToTargetChangeType(captureBounds)); + mPreviousTargetBounds.set(captureBounds); + } + + private int captureBoundsToTargetChangeType(Rect captureBounds) { + final boolean hasChangedSize = captureBounds.width() != mPreviousTargetBounds.width() + && captureBounds.height() != mPreviousTargetBounds.height(); + + if (hasChangedSize) { + Log.d(TAG, "targetChangeType= changeBounds"); + return MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_BOUNDS; + } + Log.d(TAG, "targetChangeType= changePosition"); + return MEDIA_PROJECTION_TARGET_CHANGED__TARGET_CHANGE_TYPE__TARGET_CHANGE_POSITION; } @VisibleForTesting @@ -231,45 +308,85 @@ public class MediaProjectionMetricsLogger { }; } + @VisibleForTesting + public int stopReasonToSessionStopSource(@StopReason int stopReason) { + return switch (stopReason) { + case StopReason.STOP_HOST_APP -> + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_HOST_APP_STOP; + case StopReason.STOP_TARGET_REMOVED -> + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_TASK_APP_CLOSE; + case StopReason.STOP_DEVICE_LOCKED-> + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_DEVICE_LOCK; + case StopReason.STOP_PRIVACY_CHIP -> + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_STATUS_BAR_CHIP_STOP; + case StopReason.STOP_QS_TILE -> + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_QS_TILE; + case StopReason.STOP_USER_SWITCH -> + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_USER_SWITCH; + case StopReason.STOP_FOREGROUND_SERVICE_CHANGE -> + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_FOREGROUND_SERVICE_CHANGE; + case StopReason.STOP_NEW_PROJECTION -> + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_PROJECTION; + case StopReason.STOP_NEW_MEDIA_ROUTE -> + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_MEDIA_ROUTE; + case StopReason.STOP_ERROR -> + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_ERROR; + default -> + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN; + }; + } + /** * Logs that the capturing stopped, either normally or because of error. * * @param hostUid UID of the package that initiates MediaProjection. * @param targetUid UID of the package that is captured if selected. */ - public void logStopped(int hostUid, int targetUid) { + public void logStopped(int hostUid, int targetUid, int stopReason) { boolean wasCaptureInProgress = - mPreviousState + mPreviousProjectionState == MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS; - Log.d(TAG, "logStopped: wasCaptureInProgress=" + wasCaptureInProgress); + Log.d(TAG, "logStopped: wasCaptureInProgress=" + wasCaptureInProgress + + " stopReason=" + stopReason); writeStateChanged( mSessionIdGenerator.getCurrentSessionId(), MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED, hostUid, targetUid, TIME_SINCE_LAST_ACTIVE_UNKNOWN, - MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN); + MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN, + stopReasonToSessionStopSource(stopReason)); if (wasCaptureInProgress) { mTimestampStore.registerActiveSessionEnded(); } } - public void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) { - writeStateChanged(hostUid, state, sessionCreationSource); + public void notifyProjectionStateChange( + int hostUid, + int state, + int sessionCreationSource, + int sessionStopSource + ) { + writeStateChanged(hostUid, state, sessionCreationSource, sessionStopSource); } - private void writeStateChanged(int hostUid, int state, int sessionCreationSource) { + private void writeStateChanged( + int hostUid, + int state, + int sessionCreationSource, + int sessionStopSource + ) { mFrameworkStatsLogWrapper.writeStateChanged( - /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED, + /* code */ MEDIA_PROJECTION_STATE_CHANGED, /* session_id */ 123, /* state */ state, - /* previous_state */ FrameworkStatsLog - .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN, + /* previous_state */ MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN, /* host_uid */ hostUid, /* target_uid */ -1, /* time_since_last_active */ 0, - /* creation_source */ sessionCreationSource); + /* creation_source */ sessionCreationSource, + /* stop_source */ sessionStopSource); } private void writeStateChanged( @@ -278,17 +395,19 @@ public class MediaProjectionMetricsLogger { int hostUid, int targetUid, int timeSinceLastActive, - int creationSource) { + int creationSource, + int stopSource) { mFrameworkStatsLogWrapper.writeStateChanged( - /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED, + /* code */ MEDIA_PROJECTION_STATE_CHANGED, sessionId, state, - mPreviousState, + mPreviousProjectionState, hostUid, targetUid, timeSinceLastActive, - creationSource); - mPreviousState = state; + creationSource, + stopSource); + mPreviousProjectionState = state; } private void writeTargetChanged( @@ -296,13 +415,23 @@ public class MediaProjectionMetricsLogger { int targetType, int hostUid, int targetUid, - int targetWindowingMode) { + int targetWindowingMode, + int width, + int height, + int centerX, + int centerY, + int targetChangeType) { mFrameworkStatsLogWrapper.writeTargetChanged( - /* code */ FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED, + /* code */ MEDIA_PROJECTION_TARGET_CHANGED, sessionId, targetType, hostUid, targetUid, - targetWindowingMode); + targetWindowingMode, + width, + height, + centerX, + centerY, + targetChangeType); } } diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java index 7fd962003ce9..f9145514a4e0 100644 --- a/services/core/java/com/android/server/notification/GroupHelper.java +++ b/services/core/java/com/android/server/notification/GroupHelper.java @@ -755,36 +755,7 @@ public class GroupHelper { Log.i(TAG, "isGroupChildWithoutSummary OR isGroupSummaryWithoutChild" + record); } - - ArrayMap<String, NotificationAttributes> ungrouped = - mUngroupedAbuseNotifications.getOrDefault(fullAggregateGroupKey, - new ArrayMap<>()); - ungrouped.put(record.getKey(), new NotificationAttributes( - record.getFlags(), - record.getNotification().getSmallIcon(), - record.getNotification().color, - record.getNotification().visibility, - record.getNotification().getGroupAlertBehavior(), - record.getChannel().getId())); - mUngroupedAbuseNotifications.put(fullAggregateGroupKey, ungrouped); - // Create/update summary and group if >= mAutoGroupAtCount notifications - // or if aggregate group exists - boolean hasSummary = !mAggregatedNotifications.getOrDefault(fullAggregateGroupKey, - new ArrayMap<>()).isEmpty(); - if (ungrouped.size() >= mAutoGroupAtCount || hasSummary) { - if (DEBUG) { - if (ungrouped.size() >= mAutoGroupAtCount) { - Log.i(TAG, - "Found >=" + mAutoGroupAtCount - + " ungrouped notifications => force grouping"); - } else { - Log.i(TAG, "Found aggregate summary => force grouping"); - } - } - aggregateUngroupedNotifications(fullAggregateGroupKey, sbn.getKey(), - ungrouped, hasSummary, sectioner.mSummaryId); - } - + addToUngroupedAndMaybeAggregate(record, fullAggregateGroupKey, sectioner); return; } @@ -815,6 +786,38 @@ public class GroupHelper { } } + @GuardedBy("mAggregatedNotifications") + private void addToUngroupedAndMaybeAggregate(NotificationRecord record, + FullyQualifiedGroupKey fullAggregateGroupKey, NotificationSectioner sectioner) { + ArrayMap<String, NotificationAttributes> ungrouped = + mUngroupedAbuseNotifications.getOrDefault(fullAggregateGroupKey, + new ArrayMap<>()); + ungrouped.put(record.getKey(), new NotificationAttributes( + record.getFlags(), + record.getNotification().getSmallIcon(), + record.getNotification().color, + record.getNotification().visibility, + record.getNotification().getGroupAlertBehavior(), + record.getChannel().getId())); + mUngroupedAbuseNotifications.put(fullAggregateGroupKey, ungrouped); + // Create/update summary and group if >= mAutoGroupAtCount notifications + // or if aggregate group exists + boolean hasSummary = !mAggregatedNotifications.getOrDefault(fullAggregateGroupKey, + new ArrayMap<>()).isEmpty(); + if (ungrouped.size() >= mAutoGroupAtCount || hasSummary) { + if (DEBUG) { + if (ungrouped.size() >= mAutoGroupAtCount) { + Slog.i(TAG, "Found >=" + mAutoGroupAtCount + + " ungrouped notifications => force grouping"); + } else { + Slog.i(TAG, "Found aggregate summary => force grouping"); + } + } + aggregateUngroupedNotifications(fullAggregateGroupKey, record.getKey(), + ungrouped, hasSummary, sectioner.mSummaryId); + } + } + private static boolean isGroupChildBundled(final NotificationRecord record, final Map<String, NotificationRecord> summaryByGroupKey) { final StatusBarNotification sbn = record.getSbn(); @@ -897,6 +900,73 @@ public class GroupHelper { } } + /** + * Called when a child notification is removed, after some delay, so that this helper can + * trigger a forced grouping if the group has become sparse/singleton + * or only the summary is left. + * + * see also {@link #onNotificationPostedWithDelay(NotificationRecord, List, Map)} + * + * @param summaryRecord the group summary of the notification that was removed + * @param notificationList the full notification list from NotificationManagerService + * @param summaryByGroupKey the map of group summaries from NotificationManagerService + */ + @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING) + protected void onGroupedNotificationRemovedWithDelay(final NotificationRecord summaryRecord, + final List<NotificationRecord> notificationList, + final Map<String, NotificationRecord> summaryByGroupKey) { + final StatusBarNotification sbn = summaryRecord.getSbn(); + if (!sbn.isAppGroup()) { + return; + } + + if (summaryRecord.isCanceled) { + return; + } + + if (mIsTestHarnessExempted) { + return; + } + + final NotificationSectioner sectioner = getSection(summaryRecord); + if (sectioner == null) { + if (DEBUG) { + Slog.i(TAG, + "Skipping autogrouping for " + summaryRecord + " no valid section found."); + } + return; + } + + final String pkgName = sbn.getPackageName(); + final FullyQualifiedGroupKey fullAggregateGroupKey = new FullyQualifiedGroupKey( + summaryRecord.getUserId(), pkgName, sectioner); + + // This notification is already aggregated + if (summaryRecord.getGroupKey().equals(fullAggregateGroupKey.toString())) { + return; + } + + synchronized (mAggregatedNotifications) { + if (isGroupSummaryWithoutChildren(summaryRecord, notificationList)) { + if (DEBUG) { + Slog.i(TAG, "isGroupSummaryWithoutChild " + summaryRecord); + } + addToUngroupedAndMaybeAggregate(summaryRecord, fullAggregateGroupKey, sectioner); + return; + } + + // Check if notification removal turned this group into a sparse/singleton group + if (Flags.notificationForceGroupSingletons()) { + try { + groupSparseGroups(summaryRecord, notificationList, summaryByGroupKey, sectioner, + fullAggregateGroupKey); + } catch (Throwable e) { + Slog.wtf(TAG, "Failed to group sparse groups", e); + } + } + } + } + private record NotificationMoveOp(NotificationRecord record, FullyQualifiedGroupKey oldGroup, FullyQualifiedGroupKey newGroup) { } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 9ddbace1ad45..15af36ba66af 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6597,6 +6597,14 @@ public class NotificationManagerService extends SystemService { android.Manifest.permission.INTERACT_ACROSS_USERS, "setNotificationListenerAccessGrantedForUser for user " + userId); } + if (mUmInternal.isVisibleBackgroundFullUser(userId)) { + // The main use case for visible background users is the Automotive multi-display + // configuration where a passenger can use a secondary display while the driver is + // using the main display. NotificationListeners is designed only for the current + // user and work profile. We added a condition to prevent visible background users + // from updating the data managed within the NotificationListeners object. + return; + } checkNotificationListenerAccess(); if (granted && listener.flattenToString().length() > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) { @@ -10521,6 +10529,27 @@ public class NotificationManagerService extends SystemService { mGroupHelper.onNotificationRemoved(r, mNotificationList); } }); + + // Wait 3 seconds so that the app has a chance to cancel/post + // a group summary or children + final NotificationRecord groupSummary = mSummaryByGroupKey.get(r.getGroupKey()); + if (groupSummary != null + && !GroupHelper.isAggregatedGroup(groupSummary) + && !groupSummary.getKey().equals(canceledKey)) { + // We only care about app-provided valid group summaries + final String summaryKey = groupSummary.getKey(); + mHandler.removeCallbacksAndEqualMessages(summaryKey); + mHandler.postDelayed(() -> { + synchronized (mNotificationLock) { + NotificationRecord summaryRecord = mNotificationsByKey.get( + summaryKey); + if (summaryRecord != null) { + mGroupHelper.onGroupedNotificationRemovedWithDelay( + summaryRecord, mNotificationList, mSummaryByGroupKey); + } + } + }, summaryKey, DELAY_FORCE_REGROUP_TIME); + } } else { mHandler.post(new Runnable() { @Override @@ -12725,6 +12754,20 @@ public class NotificationManagerService extends SystemService { } @Override + public void onUserUnlocked(int user) { + if (mUmInternal.isVisibleBackgroundFullUser(user)) { + // The main use case for visible background users is the Automotive + // multi-display configuration where a passenger can use a secondary + // display while the driver is using the main display. + // NotificationListeners is designed only for the current user and work + // profile. We added a condition to prevent visible background users from + // updating the data managed within the NotificationListeners object. + return; + } + super.onUserUnlocked(user); + } + + @Override protected boolean allowRebindForParentUser() { return true; } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 81dc38a02191..dc173b124884 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -1941,7 +1941,17 @@ public class ZenModeHelper { @Nullable public Policy getNotificationPolicy(UserHandle user) { synchronized (mConfigLock) { - return getNotificationPolicy(getConfigLocked(user)); + if (Flags.modesMultiuser()) { + // Return a fallback (default) policy for users without a zen config. + // Note that zen updates (setPolicy, setFilter) won't be applied, so this is mostly + // about preventing NPEs for careless callers. + ZenModeConfig config = getConfigLocked(user); + return config != null + ? getNotificationPolicy(config) + : getNotificationPolicy(mDefaultConfig); + } else { + return getNotificationPolicy(getConfigLocked(user)); + } } } diff --git a/services/core/java/com/android/server/pm/InstallDependencyHelper.java b/services/core/java/com/android/server/pm/InstallDependencyHelper.java index 527d68049537..42b8dd71c6e2 100644 --- a/services/core/java/com/android/server/pm/InstallDependencyHelper.java +++ b/services/core/java/com/android/server/pm/InstallDependencyHelper.java @@ -16,13 +16,16 @@ package com.android.server.pm; +import static android.content.pm.PackageInstaller.ACTION_INSTALL_DEPENDENCY; import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; import static android.os.Process.SYSTEM_UID; import android.annotation.NonNull; +import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.ResolveInfo; import android.content.pm.SharedLibraryInfo; import android.content.pm.dependencyinstaller.DependencyInstallerCallback; @@ -33,12 +36,15 @@ import android.os.Handler; import android.os.OutcomeReceiver; import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.ServiceConnector; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -48,22 +54,27 @@ import java.util.concurrent.TimeUnit; public class InstallDependencyHelper { private static final String TAG = InstallDependencyHelper.class.getSimpleName(); private static final boolean DEBUG = true; - private static final String ACTION_INSTALL_DEPENDENCY = - "android.intent.action.INSTALL_DEPENDENCY"; + private static final String ROLE_SYSTEM_DEPENDENCY_INSTALLER = + "android.app.role.SYSTEM_DEPENDENCY_INSTALLER"; // The maximum amount of time to wait before the system unbinds from the verifier. private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6); private static final long REQUEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(1); - private final SharedLibrariesImpl mSharedLibraries; private final Context mContext; + private final SharedLibrariesImpl mSharedLibraries; + private final PackageInstallerService mPackageInstallerService; private final Object mRemoteServiceLock = new Object(); + @GuardedBy("mTrackers") + private final List<DependencyInstallTracker> mTrackers = new ArrayList<>(); @GuardedBy("mRemoteServiceLock") private ServiceConnector<IDependencyInstallerService> mRemoteService = null; - InstallDependencyHelper(Context context, SharedLibrariesImpl sharedLibraries) { + InstallDependencyHelper(Context context, SharedLibrariesImpl sharedLibraries, + PackageInstallerService packageInstallerService) { mContext = context; mSharedLibraries = sharedLibraries; + mPackageInstallerService = packageInstallerService; } void resolveLibraryDependenciesIfNeeded(PackageLite pkg, Computer snapshot, int userId, @@ -98,19 +109,8 @@ public class InstallDependencyHelper { return; } - IDependencyInstallerCallback serviceCallback = new IDependencyInstallerCallback.Stub() { - @Override - public void onAllDependenciesResolved(int[] sessionIds) throws RemoteException { - // TODO(b/372862145): Implement waiting for sessions to finish installation - callback.onResult(null); - } - - @Override - public void onFailureToResolveAllDependencies() throws RemoteException { - onError(callback, "Failed to resolve all dependencies automatically"); - } - }; - + IDependencyInstallerCallback serviceCallback = + new DependencyInstallerCallbackCallOnce(handler, callback); boolean scheduleSuccess; synchronized (mRemoteServiceLock) { scheduleSuccess = mRemoteService.run(service -> { @@ -123,10 +123,28 @@ public class InstallDependencyHelper { } } - private void onError(CallOnceProxy callback, String msg) { + void notifySessionComplete(int sessionId, boolean success) { + if (DEBUG) { + Slog.i(TAG, "Session complete for " + sessionId + " result: " + success); + } + synchronized (mTrackers) { + List<DependencyInstallTracker> completedTrackers = new ArrayList<>(); + for (DependencyInstallTracker tracker: mTrackers) { + if (!tracker.onSessionComplete(sessionId, success)) { + completedTrackers.add(tracker); + } + } + mTrackers.removeAll(completedTrackers); + } + } + + private static void onError(CallOnceProxy callback, String msg) { PackageManagerException pe = new PackageManagerException( INSTALL_FAILED_MISSING_SHARED_LIBRARY, msg); callback.onError(pe); + if (DEBUG) { + Slog.i(TAG, "Orig session error: " + msg); + } } private boolean bindToDependencyInstallerIfNeeded(int userId, Handler handler, @@ -140,8 +158,20 @@ public class InstallDependencyHelper { } } + RoleManager roleManager = mContext.getSystemService(RoleManager.class); + if (roleManager == null) { + Slog.w(TAG, "Cannot find RoleManager system service"); + return false; + } + List<String> holders = roleManager.getRoleHoldersAsUser( + ROLE_SYSTEM_DEPENDENCY_INSTALLER, UserHandle.of(userId)); + if (holders.isEmpty()) { + Slog.w(TAG, "No holders of ROLE_SYSTEM_DEPENDENCY_INSTALLER found"); + return false; + } + Intent serviceIntent = new Intent(ACTION_INSTALL_DEPENDENCY); - // TODO(b/372862145): Use RoleManager to find the package name + serviceIntent.setPackage(holders.getFirst()); List<ResolveInfo> resolvedIntents = snapshot.queryIntentServicesInternal( serviceIntent, /*resolvedType=*/ null, /*flags=*/0, userId, SYSTEM_UID, Process.INVALID_PID, @@ -151,7 +181,6 @@ public class InstallDependencyHelper { return false; } - ResolveInfo resolveInfo = resolvedIntents.getFirst(); ComponentName componentName = resolveInfo.getComponentInfo().getComponentName(); serviceIntent.setComponent(componentName); @@ -253,4 +282,135 @@ public class InstallDependencyHelper { } } } + + /** + * Ensure we call one of the outcomes only once, on the right handler. + * + * Repeated calls will be no-op. + */ + private class DependencyInstallerCallbackCallOnce extends IDependencyInstallerCallback.Stub { + + private final Handler mHandler; + private final CallOnceProxy mCallback; + + @GuardedBy("this") + private boolean mCalled = false; + + DependencyInstallerCallbackCallOnce(Handler handler, CallOnceProxy callback) { + mHandler = handler; + mCallback = callback; + } + + // TODO(b/372862145): Consider turning the binder call to two-way so that we can + // throw IllegalArgumentException + @Override + public void onAllDependenciesResolved(int[] sessionIds) throws RemoteException { + synchronized (this) { + if (mCalled) { + return; + } + mCalled = true; + } + + ArraySet<Integer> set = new ArraySet<>(); + for (int i = 0; i < sessionIds.length; i++) { + if (DEBUG) { + Slog.i(TAG, "onAllDependenciesResolved called with " + sessionIds[i]); + } + set.add(sessionIds[i]); + } + + DependencyInstallTracker tracker = new DependencyInstallTracker(mCallback, set); + synchronized (mTrackers) { + mTrackers.add(tracker); + } + + // In case any of the session ids have already been installed, check if they + // are valid. + mHandler.post(() -> { + if (DEBUG) { + Slog.i(TAG, "onAllDependenciesResolved cleaning up invalid sessions"); + } + + for (int i = 0; i < sessionIds.length; i++) { + int sessionId = sessionIds[i]; + SessionInfo sessionInfo = mPackageInstallerService.getSessionInfo(sessionId); + + // Continue waiting if session exists and hasn't passed or failed yet. + if (sessionInfo != null && !sessionInfo.isSessionApplied + && !sessionInfo.isSessionFailed) { + continue; + } + + if (DEBUG) { + Slog.i(TAG, "onAllDependenciesResolved cleaning up finished" + + " session: " + sessionId); + } + + // If session info is null, we assume it to be success. + // TODO(b/372862145): Check historical sessions to be more precise. + boolean success = sessionInfo == null || sessionInfo.isSessionApplied; + + notifySessionComplete(sessionId, /*success=*/success); + } + }); + } + + @Override + public void onFailureToResolveAllDependencies() throws RemoteException { + synchronized (this) { + if (mCalled) { + return; + } + onError(mCallback, "Failed to resolve all dependencies automatically"); + mCalled = true; + } + } + } + + /** + * Tracks a list of session ids against a particular callback. + * + * If all the sessions completes successfully, it invokes the positive flow. If any of the + * sessions fails, it invokes the failure flow immediately. + */ + // TODO(b/372862145): Determine and add support for rebooting while dependency is being resolved + private static class DependencyInstallTracker { + private final CallOnceProxy mCallback; + private final ArraySet<Integer> mPendingSessionIds; + + DependencyInstallTracker(CallOnceProxy callback, ArraySet<Integer> pendingSessionIds) { + mCallback = callback; + mPendingSessionIds = pendingSessionIds; + } + + /** + * Process a session complete event. + * + * Returns true if we still need to continue tracking. + */ + public boolean onSessionComplete(int sessionId, boolean success) { + synchronized (this) { + if (!mPendingSessionIds.contains(sessionId)) { + // This had no impact on tracker, so continue tracking + return true; + } + + if (!success) { + // If one of the dependency fails, the orig session would fail too. + onError(mCallback, "Failed to install all dependencies"); + // TODO(b/372862145): Abandon the rest of the pending sessions. + return false; // No point in tracking anymore + } + + mPendingSessionIds.remove(sessionId); + if (mPendingSessionIds.isEmpty()) { + mCallback.onResult(null); + return false; // Nothing to track anymore + } + return true; // Keep on tracking + } + } + + } } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 8168c5493304..e5e274450655 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -229,6 +229,7 @@ final class InstallPackageHelper { private final SharedLibrariesImpl mSharedLibraries; private final PackageManagerServiceInjector mInjector; private final UpdateOwnershipHelper mUpdateOwnershipHelper; + private final InstallDependencyHelper mInstallDependencyHelper; private final Object mInternalLock = new Object(); @GuardedBy("mInternalLock") @@ -239,7 +240,8 @@ final class InstallPackageHelper { AppDataHelper appDataHelper, RemovePackageHelper removePackageHelper, DeletePackageHelper deletePackageHelper, - BroadcastHelper broadcastHelper) { + BroadcastHelper broadcastHelper, + InstallDependencyHelper installDependencyHelper) { mPm = pm; mInjector = pm.mInjector; mAppDataHelper = appDataHelper; @@ -253,6 +255,7 @@ final class InstallPackageHelper { mPackageAbiHelper = pm.mInjector.getAbiHelper(); mSharedLibraries = pm.mInjector.getSharedLibrariesImpl(); mUpdateOwnershipHelper = pm.mInjector.getUpdateOwnershipHelper(); + mInstallDependencyHelper = installDependencyHelper; } /** @@ -1364,6 +1367,10 @@ final class InstallPackageHelper { } } } + + for (InstallRequest request : requests) { + mInstallDependencyHelper.notifySessionComplete(request.getSessionId(), success); + } } @GuardedBy("mPm.mInstallLock") diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index a4152a724d64..47b785040d44 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -327,7 +327,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements context, mInstallThread.getLooper(), new AppStateHelper(context)); mPackageArchiver = new PackageArchiver(mContext, mPm); mInstallDependencyHelper = new InstallDependencyHelper(mContext, - mPm.mInjector.getSharedLibrariesImpl()); + mPm.mInjector.getSharedLibrariesImpl(), this); LocalServices.getService(SystemServiceManager.class).startService( new Lifecycle(context, this)); @@ -337,6 +337,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements return mStagingManager; } + InstallDependencyHelper getInstallDependencyHelper() { + return mInstallDependencyHelper; + } + boolean okToSendBroadcasts() { return mOkToSendBroadcasts; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 807445ef062d..715633410575 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2118,7 +2118,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper, mBroadcastHelper); mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper, mRemovePackageHelper, - mDeletePackageHelper, mBroadcastHelper); + mDeletePackageHelper, mBroadcastHelper, + injector.getPackageInstallerService().getInstallDependencyHelper()); mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager, mInjector.getUserManagerInternal(), mDeletePackageHelper); diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index e339cecd4d3c..aa235c2258ac 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -3601,7 +3601,7 @@ class PackageManagerShellCommand extends ShellCommand { break; case "--disable-auto-install-dependencies": if (Flags.sdkDependencyInstaller()) { - sessionParams.setEnableAutoInstallDependencies(false); + sessionParams.setAutoInstallDependenciesEnabled(false); } else { throw new IllegalArgumentException("Unknown option " + opt); } diff --git a/services/core/java/com/android/server/pm/SaferIntentUtils.java b/services/core/java/com/android/server/pm/SaferIntentUtils.java index bc36fabc7f67..854e142c7c2f 100644 --- a/services/core/java/com/android/server/pm/SaferIntentUtils.java +++ b/services/core/java/com/android/server/pm/SaferIntentUtils.java @@ -26,6 +26,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; import android.compat.annotation.EnabledAfter; import android.compat.annotation.Overridable; import android.content.Intent; @@ -88,6 +89,22 @@ public class SaferIntentUtils { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) private static final long IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS = 229362273; + /** + * Intents sent from apps enabling this feature will stop resolving to components with + * non matching intent filters, even when explicitly setting a component name, unless the + * target components are in the same app as the calling app. + * <p> + * When an app registers an exported component in its manifest and adds <intent-filter>s, + * the component can be started by any intent - even those that do not match the intent filter. + * This has proven to be something that many developers find counterintuitive. + * Without checking the intent when the component is started, in some circumstances this can + * allow 3P apps to trigger internal-only functionality. + */ + @ChangeId + @Overridable + @Disabled + private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188; + @Nullable private static ParsedMainComponent infoToComponent( ComponentInfo info, ComponentResolverApi resolver, boolean isReceiver) { @@ -249,6 +266,20 @@ public class SaferIntentUtils { */ public static void enforceIntentFilterMatching( IntentArgs args, List<ResolveInfo> resolveInfos) { + // Switch to the new intent matching logic if the feature flag is enabled. + // Otherwise, use the existing AppCompat based implementation. + if (Flags.enableIntentMatchingFlags()) { + enforceIntentFilterMatchingWithIntentMatchingFlags(args, resolveInfos); + } else { + enforceIntentFilterMatchingWithAppCompat(args, resolveInfos); + } + } + + /** + * This version of the method is implemented in Android B and uses "IntentMatchingFlags" + */ + private static void enforceIntentFilterMatchingWithIntentMatchingFlags( + IntentArgs args, List<ResolveInfo> resolveInfos) { if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return; // Do not enforce filter matching when the caller is system or root @@ -339,6 +370,97 @@ public class SaferIntentUtils { } /** + * This version of the method is implemented in Android V and uses "AppCompat" + */ + private static void enforceIntentFilterMatchingWithAppCompat( + IntentArgs args, List<ResolveInfo> resolveInfos) { + if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return; + + // Do not enforce filter matching when the caller is system or root + if (ActivityManager.canAccessUnexportedComponents(args.callingUid)) return; + + final Computer computer = (Computer) args.snapshot; + final ComponentResolverApi resolver = computer.getComponentResolver(); + + final Printer logPrinter = DEBUG_INTENT_MATCHING + ? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM) + : null; + + final boolean enforceMatch = Flags.enforceIntentFilterMatch() + && args.isChangeEnabled(ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS); + final boolean blockNullAction = Flags.blockNullActionIntents() + && args.isChangeEnabled(IntentFilter.BLOCK_NULL_ACTION_INTENTS); + + for (int i = resolveInfos.size() - 1; i >= 0; --i) { + final ComponentInfo info = resolveInfos.get(i).getComponentInfo(); + + // Skip filter matching when the caller is targeting the same app + if (UserHandle.isSameApp(args.callingUid, info.applicationInfo.uid)) { + continue; + } + + final ParsedMainComponent comp = infoToComponent(info, resolver, args.isReceiver); + + if (comp == null || comp.getIntents().isEmpty()) { + continue; + } + + Boolean match = null; + + if (args.intent.getAction() == null) { + args.reportEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH, + enforceMatch && blockNullAction); + if (blockNullAction) { + // Skip intent filter matching if blocking null action + match = false; + } + } + + if (match == null) { + // Check if any intent filter matches + for (int j = 0, size = comp.getIntents().size(); j < size; ++j) { + IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter(); + if (IntentResolver.intentMatchesFilter( + intentFilter, args.intent, args.resolvedType)) { + match = true; + break; + } + } + } + + // At this point, the value `match` has the following states: + // null : Intent does not match any intent filter + // false: Null action intent detected AND blockNullAction == true + // true : The intent matches at least one intent filter + + if (match == null) { + args.reportEvent( + UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH, + enforceMatch); + match = false; + } + + if (!match) { + // All non-matching intents has to be marked accordingly + if (Flags.enforceIntentFilterMatch()) { + args.intent.addExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); + } + if (enforceMatch) { + Slog.w(TAG, "Intent does not match component's intent filter: " + args.intent); + Slog.w(TAG, "Access blocked: " + comp.getComponentName()); + if (DEBUG_INTENT_MATCHING) { + Slog.v(TAG, "Component intent filters:"); + comp.getIntents().forEach(f -> f.getIntentFilter().dump(logPrinter, " ")); + Slog.v(TAG, "-----------------------------"); + } + resolveInfos.remove(i); + } + } + } + } + + /** * Filter non-exported components from the componentList if intent is implicit. * <p> * Implicit intents cannot be used to start Services since API 21+. diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b2b8aaf7dd2a..066fce068d61 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -6122,8 +6122,11 @@ public class UserManagerService extends IUserManager.Stub { // If the user switch hasn't been explicitly toggled on or off by the user, turn it on. if (android.provider.Settings.Global.getString(mContext.getContentResolver(), android.provider.Settings.Global.USER_SWITCHER_ENABLED) == null) { - android.provider.Settings.Global.putInt(mContext.getContentResolver(), - android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1); + if (Resources.getSystem().getBoolean( + com.android.internal.R.bool.config_enableUserSwitcherUponUserCreation)) { + android.provider.Settings.Global.putInt(mContext.getContentResolver(), + android.provider.Settings.Global.USER_SWITCHER_ENABLED, 1); + } } } } diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java index 453c6ef41437..e75f852eb437 100644 --- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java +++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java @@ -137,6 +137,8 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, "com.android.server.policy.PROPERTY_FEATURE_REAR_DISPLAY"; private static final String PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT = "com.android.server.policy.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT"; + private static final String PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT = + "com.android.server.policy.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT"; @@ -281,6 +283,9 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider, case PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT: systemProperties.add(DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT); break; + case PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT: + systemProperties.add(DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT); + break; default: Slog.w(TAG, "Parsed unknown flag with name: " + propertyString); break; diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java index 71f67d82dbec..aba15c83f907 100644 --- a/services/core/java/com/android/server/power/hint/HintManagerService.java +++ b/services/core/java/com/android/server/power/hint/HintManagerService.java @@ -34,7 +34,9 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.hardware.power.ChannelConfig; import android.hardware.power.CpuHeadroomParams; +import android.hardware.power.CpuHeadroomResult; import android.hardware.power.GpuHeadroomParams; +import android.hardware.power.GpuHeadroomResult; import android.hardware.power.IPower; import android.hardware.power.SessionConfig; import android.hardware.power.SessionTag; @@ -79,6 +81,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -96,10 +99,10 @@ public final class HintManagerService extends SystemService { private static final int EVENT_CLEAN_UP_UID = 3; @VisibleForTesting static final int CLEAN_UP_UID_DELAY_MILLIS = 1000; + // The minimum interval between the headroom calls as rate limiting. private static final int DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS = 1000; private static final int DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS = 1000; private static final int HEADROOM_INTERVAL_UNSUPPORTED = -1; - @VisibleForTesting static final int DEFAULT_HEADROOM_PID = -1; @VisibleForTesting final long mHintSessionPreferredRate; @@ -184,73 +187,77 @@ public final class HintManagerService extends SystemService { private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint"; private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager"; private static final String PROPERTY_USE_HAL_HEADROOMS = "persist.hms.use_hal_headrooms"; + private static final String PROPERTY_CHECK_HEADROOM_TID = "persist.hms.check_headroom_tid"; private Boolean mFMQUsesIntegratedEventFlag = false; private final Object mCpuHeadroomLock = new Object(); - private static class CpuHeadroomCacheItem { - long mExpiredTimeMillis; - CpuHeadroomParamsInternal mParams; - float[] mHeadroom; - long mPid; - CpuHeadroomCacheItem(long expiredTimeMillis, CpuHeadroomParamsInternal params, - float[] headroom, long pid) { - mExpiredTimeMillis = expiredTimeMillis; - mParams = params; - mPid = pid; - mHeadroom = headroom; - } + // this cache tracks the expiration time of the items and performs cleanup on lookup + private static class HeadroomCache<K, V> { + final List<HeadroomCacheItem> mItemList; + final Map<K, HeadroomCacheItem> mKeyItemMap; + final long mItemExpDurationMillis; + + class HeadroomCacheItem { + long mExpTime; + K mKey; + V mValue; - private boolean match(CpuHeadroomParamsInternal params, long pid) { - if (mParams == null && params == null) return true; - if (mParams != null) { - return mParams.equals(params) && pid == mPid; + HeadroomCacheItem(K k, V v) { + mExpTime = System.currentTimeMillis() + mItemExpDurationMillis; + mKey = k; + mValue = v; } - return false; - } - private boolean isExpired() { - return System.currentTimeMillis() > mExpiredTimeMillis; + boolean isExpired() { + return mExpTime <= System.currentTimeMillis(); + } } - } - - @GuardedBy("mCpuHeadroomLock") - private final List<CpuHeadroomCacheItem> mCpuHeadroomCache; - private final long mCpuHeadroomIntervalMillis; - - private final Object mGpuHeadroomLock = new Object(); - private static class GpuHeadroomCacheItem { - long mExpiredTimeMillis; - GpuHeadroomParamsInternal mParams; - float mHeadroom; - - GpuHeadroomCacheItem(long expiredTimeMillis, GpuHeadroomParamsInternal params, - float headroom) { - mExpiredTimeMillis = expiredTimeMillis; - mParams = params; - mHeadroom = headroom; + void add(K key, V value) { + if (mKeyItemMap.containsKey(key)) { + final HeadroomCacheItem item = mKeyItemMap.get(key); + mItemList.remove(item); + } + final HeadroomCacheItem item = new HeadroomCacheItem(key, value); + mItemList.add(item); + mKeyItemMap.put(key, item); } - private boolean match(GpuHeadroomParamsInternal params) { - if (mParams == null && params == null) return true; - if (mParams != null) { - return mParams.equals(params); + V get(K key) { + while (!mItemList.isEmpty() && mItemList.getFirst().isExpired()) { + mKeyItemMap.remove(mItemList.removeFirst().mKey); } - return false; + final HeadroomCacheItem item = mKeyItemMap.get(key); + if (item == null) { + return null; + } + return item.mValue; } - private boolean isExpired() { - return System.currentTimeMillis() > mExpiredTimeMillis; + HeadroomCache(int size, long expDurationMillis) { + mItemList = new LinkedList<>(); + mKeyItemMap = new ArrayMap<>(size); + mItemExpDurationMillis = expDurationMillis; } } + @GuardedBy("mCpuHeadroomLock") + private final HeadroomCache<CpuHeadroomParams, CpuHeadroomResult> mCpuHeadroomCache; + private final long mCpuHeadroomIntervalMillis; + + private final Object mGpuHeadroomLock = new Object(); + @GuardedBy("mGpuHeadroomLock") - private final List<GpuHeadroomCacheItem> mGpuHeadroomCache; + private final HeadroomCache<GpuHeadroomParams, GpuHeadroomResult> mGpuHeadroomCache; private final long mGpuHeadroomIntervalMillis; + // these are set to default values in CpuHeadroomParamsInternal and GpuHeadroomParamsInternal + private final int mDefaultCpuHeadroomCalculationWindowMillis; + private final int mDefaultGpuHeadroomCalculationWindowMillis; + @VisibleForTesting final IHintManager.Stub mService = new BinderService(); @@ -303,26 +310,31 @@ public final class HintManagerService extends SystemService { } } mCpuHeadroomIntervalMillis = cpuHeadroomIntervalMillis; + mDefaultCpuHeadroomCalculationWindowMillis = + new CpuHeadroomParamsInternal().calculationWindowMillis; + mDefaultGpuHeadroomCalculationWindowMillis = + new GpuHeadroomParamsInternal().calculationWindowMillis; mGpuHeadroomIntervalMillis = gpuHeadroomIntervalMillis; if (mCpuHeadroomIntervalMillis > 0) { - mCpuHeadroomCache = new ArrayList<>(4); + mCpuHeadroomCache = new HeadroomCache<>(2, mCpuHeadroomIntervalMillis); } else { mCpuHeadroomCache = null; } if (mGpuHeadroomIntervalMillis > 0) { - mGpuHeadroomCache = new ArrayList<>(2); + mGpuHeadroomCache = new HeadroomCache<>(2, mGpuHeadroomIntervalMillis); } else { mGpuHeadroomCache = null; } } private long checkCpuHeadroomSupport() { + final CpuHeadroomParams params = new CpuHeadroomParams(); + params.tids = new int[]{Process.myPid()}; try { synchronized (mCpuHeadroomLock) { - final CpuHeadroomParams defaultParams = new CpuHeadroomParams(); - defaultParams.pid = Process.myPid(); - float[] ret = mPowerHal.getCpuHeadroom(defaultParams); - if (ret != null && ret.length > 0) { + final CpuHeadroomResult ret = mPowerHal.getCpuHeadroom(params); + if (ret != null && ret.getTag() == CpuHeadroomResult.globalHeadroom + && !Float.isNaN(ret.getGlobalHeadroom())) { return Math.max( DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS, mPowerHal.getCpuHeadroomMinIntervalMillis()); @@ -330,27 +342,29 @@ public final class HintManagerService extends SystemService { } } catch (UnsupportedOperationException e) { - Slog.w(TAG, "getCpuHeadroom HAL API is not supported", e); + Slog.w(TAG, "getCpuHeadroom HAL API is not supported, params: " + params, e); } catch (RemoteException e) { - Slog.e(TAG, "getCpuHeadroom HAL API fails, disabling the API", e); + Slog.e(TAG, "getCpuHeadroom HAL API fails, disabling the API, params: " + params, e); } return HEADROOM_INTERVAL_UNSUPPORTED; } private long checkGpuHeadroomSupport() { + final GpuHeadroomParams params = new GpuHeadroomParams(); try { synchronized (mGpuHeadroomLock) { - float ret = mPowerHal.getGpuHeadroom(new GpuHeadroomParams()); - if (!Float.isNaN(ret)) { + final GpuHeadroomResult ret = mPowerHal.getGpuHeadroom(params); + if (ret != null && ret.getTag() == GpuHeadroomResult.globalHeadroom && !Float.isNaN( + ret.getGlobalHeadroom())) { return Math.max( DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS, mPowerHal.getGpuHeadroomMinIntervalMillis()); } } } catch (UnsupportedOperationException e) { - Slog.w(TAG, "getGpuHeadroom HAL API is not supported", e); + Slog.w(TAG, "getGpuHeadroom HAL API is not supported, params: " + params, e); } catch (RemoteException e) { - Slog.e(TAG, "getGpuHeadroom HAL API fails, disabling the API", e); + Slog.e(TAG, "getGpuHeadroom HAL API fails, disabling the API, params: " + params, e); } return HEADROOM_INTERVAL_UNSUPPORTED; } @@ -1445,109 +1459,98 @@ public final class HintManagerService extends SystemService { } @Override - public float[] getCpuHeadroom(@Nullable CpuHeadroomParamsInternal params) { + public CpuHeadroomResult getCpuHeadroom(@NonNull CpuHeadroomParamsInternal params) { if (mCpuHeadroomIntervalMillis <= 0) { throw new UnsupportedOperationException(); } - CpuHeadroomParams halParams = new CpuHeadroomParams(); - halParams.pid = Binder.getCallingPid(); - if (params != null) { - halParams.calculationType = params.calculationType; - halParams.selectionType = params.selectionType; - if (params.usesDeviceHeadroom) { - halParams.pid = DEFAULT_HEADROOM_PID; - } - } - synchronized (mCpuHeadroomLock) { - while (!mCpuHeadroomCache.isEmpty()) { - if (mCpuHeadroomCache.getFirst().isExpired()) { - mCpuHeadroomCache.removeFirst(); - } else { - break; + final CpuHeadroomParams halParams = new CpuHeadroomParams(); + halParams.tids = new int[]{Binder.getCallingPid()}; + halParams.calculationType = params.calculationType; + halParams.calculationWindowMillis = params.calculationWindowMillis; + halParams.selectionType = params.selectionType; + if (params.usesDeviceHeadroom) { + halParams.tids = new int[]{}; + } else if (params.tids != null && params.tids.length > 0) { + if (params.tids.length > 5) { + throw new IllegalArgumentException( + "More than 5 TIDs is requested: " + params.tids.length); + } + if (SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_TID, true)) { + final int tgid = Process.getThreadGroupLeader(Binder.getCallingPid()); + for (int tid : params.tids) { + if (Process.getThreadGroupLeader(tid) != tgid) { + throw new SecurityException("TID " + tid + + " doesn't belong to the calling process with pid " + + tgid); + } } } - for (int i = 0; i < mCpuHeadroomCache.size(); ++i) { - final CpuHeadroomCacheItem item = mCpuHeadroomCache.get(i); - if (item.match(params, halParams.pid)) { - item.mExpiredTimeMillis = - System.currentTimeMillis() + mCpuHeadroomIntervalMillis; - mCpuHeadroomCache.remove(i); - mCpuHeadroomCache.add(item); - return item.mHeadroom; - } + halParams.tids = params.tids; + } + if (halParams.calculationWindowMillis + == mDefaultCpuHeadroomCalculationWindowMillis) { + synchronized (mCpuHeadroomLock) { + final CpuHeadroomResult res = mCpuHeadroomCache.get(halParams); + if (res != null) return res; } } // return from HAL directly try { - float[] headroom = mPowerHal.getCpuHeadroom(halParams); - if (headroom == null || headroom.length == 0) { - Slog.wtf(TAG, - "CPU headroom from Power HAL is invalid: " + Arrays.toString(headroom)); - return new float[]{Float.NaN}; - } - synchronized (mCpuHeadroomLock) { - mCpuHeadroomCache.add(new CpuHeadroomCacheItem( - System.currentTimeMillis() + mCpuHeadroomIntervalMillis, - params, headroom, halParams.pid - )); + final CpuHeadroomResult result = mPowerHal.getCpuHeadroom(halParams); + if (result == null) { + Slog.wtf(TAG, "CPU headroom from Power HAL is invalid"); + return null; + } + if (halParams.calculationWindowMillis + == mDefaultCpuHeadroomCalculationWindowMillis) { + synchronized (mCpuHeadroomLock) { + mCpuHeadroomCache.add(halParams, result); + } } - return headroom; - + return result; } catch (RemoteException e) { - return new float[]{Float.NaN}; + Slog.e(TAG, "Failed to get CPU headroom from Power HAL", e); + return null; } } @Override - public float getGpuHeadroom(@Nullable GpuHeadroomParamsInternal params) { + public GpuHeadroomResult getGpuHeadroom(@NonNull GpuHeadroomParamsInternal params) { if (mGpuHeadroomIntervalMillis <= 0) { throw new UnsupportedOperationException(); } - GpuHeadroomParams halParams = new GpuHeadroomParams(); - if (params != null) { - halParams.calculationType = params.calculationType; - } - synchronized (mGpuHeadroomLock) { - while (!mGpuHeadroomCache.isEmpty()) { - if (mGpuHeadroomCache.getFirst().isExpired()) { - mGpuHeadroomCache.removeFirst(); - } else { - break; - } - } - for (int i = 0; i < mGpuHeadroomCache.size(); ++i) { - final GpuHeadroomCacheItem item = mGpuHeadroomCache.get(i); - if (item.match(params)) { - item.mExpiredTimeMillis = - System.currentTimeMillis() + mGpuHeadroomIntervalMillis; - mGpuHeadroomCache.remove(i); - mGpuHeadroomCache.add(item); - return item.mHeadroom; - } + final GpuHeadroomParams halParams = new GpuHeadroomParams(); + halParams.calculationType = params.calculationType; + halParams.calculationWindowMillis = params.calculationWindowMillis; + if (halParams.calculationWindowMillis + == mDefaultGpuHeadroomCalculationWindowMillis) { + synchronized (mGpuHeadroomLock) { + final GpuHeadroomResult res = mGpuHeadroomCache.get(halParams); + if (res != null) return res; } } // return from HAL directly try { - float headroom = mPowerHal.getGpuHeadroom(halParams); - if (Float.isNaN(headroom)) { - Slog.wtf(TAG, - "GPU headroom from Power HAL is NaN"); - return Float.NaN; - } - synchronized (mGpuHeadroomLock) { - mGpuHeadroomCache.add(new GpuHeadroomCacheItem( - System.currentTimeMillis() + mGpuHeadroomIntervalMillis, - params, headroom - )); + final GpuHeadroomResult headroom = mPowerHal.getGpuHeadroom(halParams); + if (headroom == null) { + Slog.wtf(TAG, "GPU headroom from Power HAL is invalid"); + return null; + } + if (halParams.calculationWindowMillis + == mDefaultGpuHeadroomCalculationWindowMillis) { + synchronized (mGpuHeadroomLock) { + mGpuHeadroomCache.add(halParams, headroom); + } } return headroom; } catch (RemoteException e) { - return Float.NaN; + Slog.e(TAG, "Failed to get GPU headroom from Power HAL", e); + return null; } } @Override - public long getCpuHeadroomMinIntervalMillis() throws RemoteException { + public long getCpuHeadroomMinIntervalMillis() { if (mCpuHeadroomIntervalMillis <= 0) { throw new UnsupportedOperationException(); } @@ -1555,7 +1558,7 @@ public final class HintManagerService extends SystemService { } @Override - public long getGpuHeadroomMinIntervalMillis() throws RemoteException { + public long getGpuHeadroomMinIntervalMillis() { if (mGpuHeadroomIntervalMillis <= 0) { throw new UnsupportedOperationException(); } @@ -1591,17 +1594,23 @@ public final class HintManagerService extends SystemService { CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal(); params.selectionType = CpuHeadroomParams.SelectionType.ALL; params.usesDeviceHeadroom = true; - pw.println("CPU headroom: " + Arrays.toString(getCpuHeadroom(params))); + CpuHeadroomResult ret = getCpuHeadroom(params); + pw.println("CPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom())); params = new CpuHeadroomParamsInternal(); params.selectionType = CpuHeadroomParams.SelectionType.PER_CORE; params.usesDeviceHeadroom = true; - pw.println("CPU headroom per core: " + Arrays.toString(getCpuHeadroom(params))); + ret = getCpuHeadroom(params); + pw.println("CPU headroom per core: " + (ret == null ? "N/A" + : Arrays.toString(ret.getPerCoreHeadroom()))); } catch (Exception e) { + Slog.d(TAG, "Failed to dump CPU headroom", e); pw.println("CPU headroom: N/A"); } try { - pw.println("GPU headroom: " + getGpuHeadroom(null)); + GpuHeadroomResult ret = getGpuHeadroom(new GpuHeadroomParamsInternal()); + pw.println("GPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom())); } catch (Exception e) { + Slog.d(TAG, "Failed to dump GPU headroom", e); pw.println("GPU headroom: N/A"); } } diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java index 606bd1dd0f3f..63e8d9973237 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java @@ -26,6 +26,7 @@ import android.os.BatteryUsageStatsQuery; import android.os.Handler; import android.os.Process; import android.util.Log; +import android.util.LogWriter; import android.util.Slog; import android.util.SparseArray; @@ -34,6 +35,7 @@ import com.android.internal.os.CpuScalingPolicies; import com.android.internal.os.MonotonicClock; import com.android.internal.os.PowerProfile; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -44,6 +46,8 @@ import java.util.List; */ public class BatteryUsageStatsProvider { private static final String TAG = "BatteryUsageStatsProv"; + private static final boolean DEBUG = false; + private final Context mContext; private final PowerAttributor mPowerAttributor; private final PowerStatsStore mPowerStatsStore; @@ -262,17 +266,25 @@ public class BatteryUsageStatsProvider { private BatteryUsageStats getBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query, long currentTimeMs) { + BatteryUsageStats batteryUsageStats; if ((query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_ACCUMULATED) != 0) { - return getAccumulatedBatteryUsageStats(stats, query, currentTimeMs); + batteryUsageStats = getAccumulatedBatteryUsageStats(stats, query, currentTimeMs); } else if (query.getAggregatedToTimestamp() == 0) { BatteryUsageStats.Builder builder = computeBatteryUsageStats(stats, query, query.getMonotonicStartTime(), query.getMonotonicEndTime(), currentTimeMs); - return builder.build(); + batteryUsageStats = builder.build(); } else { - return getAggregatedBatteryUsageStats(stats, query); + batteryUsageStats = getAggregatedBatteryUsageStats(stats, query); + } + if (DEBUG) { + Slog.d(TAG, "query = " + query); + PrintWriter pw = new PrintWriter(new LogWriter(Log.DEBUG, TAG)); + batteryUsageStats.dump(pw, ""); + pw.flush(); } + return batteryUsageStats; } private BatteryUsageStats getAccumulatedBatteryUsageStats(BatteryStatsImpl stats, @@ -319,7 +331,7 @@ public class BatteryUsageStatsProvider { if (accumulatedStats.builder == null) { accumulatedStats.builder = new BatteryUsageStats.Builder( - stats.getCustomEnergyConsumerNames(), false, true, true, true, 0); + stats.getCustomEnergyConsumerNames(), true, true, true, 0); accumulatedStats.startWallClockTime = stats.getStartClockTime(); accumulatedStats.builder.setStatsStartTimestamp(accumulatedStats.startWallClockTime); } @@ -338,8 +350,6 @@ public class BatteryUsageStatsProvider { private BatteryUsageStats.Builder computeBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query, long monotonicStartTime, long monotonicEndTime, long currentTimeMs) { - final boolean includePowerModels = (query.getFlags() - & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; final boolean includeProcessStateData = ((query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0) && stats.isProcessStateDataAvailable(); @@ -353,8 +363,7 @@ public class BatteryUsageStatsProvider { } final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder( - customEnergyConsumerNames, includePowerModels, - includeProcessStateData, query.isScreenStateDataNeeded(), + customEnergyConsumerNames, includeProcessStateData, query.isScreenStateDataNeeded(), query.isPowerStateDataNeeded(), minConsumedPowerThreshold); synchronized (stats) { @@ -444,8 +453,6 @@ public class BatteryUsageStatsProvider { private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryStatsImpl stats, BatteryUsageStatsQuery query) { - final boolean includePowerModels = (query.getFlags() - & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; final boolean includeProcessStateData = ((query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0) && stats.isProcessStateDataAvailable(); @@ -453,7 +460,7 @@ public class BatteryUsageStatsProvider { final String[] customEnergyConsumerNames = stats.getCustomEnergyConsumerNames(); final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - customEnergyConsumerNames, includePowerModels, includeProcessStateData, + customEnergyConsumerNames, includeProcessStateData, query.isScreenStateDataNeeded(), query.isPowerStateDataNeeded(), minConsumedPowerThreshold); if (mPowerStatsStore == null) { diff --git a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java index b2442c81b2e5..ef0e63bece90 100644 --- a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java +++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java @@ -224,13 +224,12 @@ class PowerStatsExporter { BatteryConsumer.Key key = getKeyForPartialTotal(batteryUsageStatsBuilder, deviceScope, powerComponentId, screenState, powerState); if (key != null) { - deviceScope.addConsumedPower(key, totalPower[0], - BatteryConsumer.POWER_MODEL_UNDEFINED); + deviceScope.addConsumedPower(key, totalPower[0]); deviceScope.addUsageDurationMillis(key, durationMs[0]); } key = deviceScope.getKey(powerComponentId, BatteryConsumer.PROCESS_STATE_UNSPECIFIED); if (key != null) { - deviceScope.addConsumedPower(key, totalPower[0], BatteryConsumer.POWER_MODEL_UNDEFINED); + deviceScope.addConsumedPower(key, totalPower[0]); deviceScope.addUsageDurationMillis(key, durationMs[0]); } } @@ -373,7 +372,7 @@ class PowerStatsExporter { BatteryConsumer.Key key = builder.getKey(powerComponentId, procState, resultScreenState, resultPowerState); if (key != null) { - builder.addConsumedPower(key, power, BatteryConsumer.POWER_MODEL_UNDEFINED); + builder.addConsumedPower(key, power); builder.addUsageDurationMillis(key, duration); } } @@ -384,8 +383,7 @@ class PowerStatsExporter { BatteryConsumer.PROCESS_STATE_UNSPECIFIED); if (key != null) { builder.addConsumedPower(key, - powerByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED], - BatteryConsumer.POWER_MODEL_UNDEFINED); + powerByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED]); builder.addUsageDurationMillis(key, durationByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED]); } @@ -399,11 +397,9 @@ class PowerStatsExporter { BatteryConsumer.Key key = getKeyForPartialTotal(batteryUsageStatsBuilder, allAppsScope, powerComponentId, screenState, powerState); if (key != null) { - allAppsScope.addConsumedPower(key, powerAllApps, - BatteryConsumer.POWER_MODEL_UNDEFINED); + allAppsScope.addConsumedPower(key, powerAllApps); } - allAppsScope.addConsumedPower(powerComponentId, powerAllApps, - BatteryConsumer.POWER_MODEL_UNDEFINED); + allAppsScope.addConsumedPower(powerComponentId, powerAllApps); } @Nullable diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java index e8723b91a541..2cc08c327b71 100644 --- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java +++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java @@ -46,7 +46,9 @@ import com.android.server.SystemService; import com.android.server.pm.UserManagerInternal; import com.android.server.security.advancedprotection.features.AdvancedProtectionHook; import com.android.server.security.advancedprotection.features.AdvancedProtectionProvider; +import com.android.server.security.advancedprotection.features.DisallowCellular2GAdvancedProtectionHook; import com.android.server.security.advancedprotection.features.DisallowInstallUnknownSourcesAdvancedProtectionHook; +import com.android.server.security.advancedprotection.features.MemoryTaggingExtensionHook; import java.io.FileDescriptor; import java.util.ArrayList; @@ -80,6 +82,12 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub if (android.security.Flags.aapmFeatureDisableInstallUnknownSources()) { mHooks.add(new DisallowInstallUnknownSourcesAdvancedProtectionHook(mContext, enabled)); } + if (android.security.Flags.aapmFeatureMemoryTaggingExtension()) { + mHooks.add(new MemoryTaggingExtensionHook(mContext, enabled)); + } + if (android.security.Flags.aapmFeatureDisableCellular2g()) { + mHooks.add(new DisallowCellular2GAdvancedProtectionHook(mContext, enabled)); + } } // Only for tests diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java new file mode 100644 index 000000000000..b9c8d3dc5319 --- /dev/null +++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowCellular2GAdvancedProtectionHook.java @@ -0,0 +1,86 @@ +/* + * 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.security.advancedprotection.features; + +import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY; +import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G; + +import android.annotation.NonNull; +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.os.UserManager; +import android.security.advancedprotection.AdvancedProtectionFeature; +import android.telephony.TelephonyManager; +import android.util.Slog; + +/** @hide */ +public final class DisallowCellular2GAdvancedProtectionHook extends AdvancedProtectionHook { + private static final String TAG = "AdvancedProtectionDisallowCellular2G"; + + private final AdvancedProtectionFeature mFeature = + new AdvancedProtectionFeature(FEATURE_ID_DISALLOW_CELLULAR_2G); + private final DevicePolicyManager mDevicePolicyManager; + private final TelephonyManager mTelephonyManager; + + public DisallowCellular2GAdvancedProtectionHook(@NonNull Context context, boolean enabled) { + super(context, enabled); + mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); + mTelephonyManager = context.getSystemService(TelephonyManager.class); + + setPolicy(enabled); + } + + @NonNull + @Override + public AdvancedProtectionFeature getFeature() { + return mFeature; + } + + @Override + public boolean isAvailable() { + return mTelephonyManager.isDataCapable(); + } + + private void setPolicy(boolean enabled) { + Slog.i(TAG, "setPolicy called with " + enabled); + + if (enabled) { + Slog.d(TAG, "Setting DISALLOW_CELLULAR_2G_GLOBALLY restriction"); + mDevicePolicyManager.addUserRestrictionGlobally( + ADVANCED_PROTECTION_SYSTEM_ENTITY, UserManager.DISALLOW_CELLULAR_2G); + } else { + Slog.d(TAG, "Clearing DISALLOW_CELLULAR_2G_GLOBALLY restriction"); + mDevicePolicyManager.clearUserRestrictionGlobally( + ADVANCED_PROTECTION_SYSTEM_ENTITY, UserManager.DISALLOW_CELLULAR_2G); + } + } + + @Override + public void onAdvancedProtectionChanged(boolean enabled) { + setPolicy(enabled); + + // Leave 2G disabled even if APM is disabled. + if (!enabled) { + long oldAllowedTypes = + mTelephonyManager.getAllowedNetworkTypesForReason( + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G); + long newAllowedTypes = oldAllowedTypes & ~TelephonyManager.NETWORK_CLASS_BITMASK_2G; + mTelephonyManager.setAllowedNetworkTypesForReason( + TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, newAllowedTypes); + } + } +} diff --git a/services/core/java/com/android/server/security/advancedprotection/features/MemoryTaggingExtensionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/MemoryTaggingExtensionHook.java new file mode 100644 index 000000000000..d3d39371e883 --- /dev/null +++ b/services/core/java/com/android/server/security/advancedprotection/features/MemoryTaggingExtensionHook.java @@ -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.server.security.advancedprotection.features; + +import static android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY; +import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_ENABLE_MTE; + +import android.annotation.NonNull; +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.os.SystemProperties; +import android.security.advancedprotection.AdvancedProtectionFeature; +import android.util.Slog; + +/** @hide */ +public final class MemoryTaggingExtensionHook + extends AdvancedProtectionHook { + private static final String TAG = "AdvancedProtectionMTE"; + private static final String MTE_DPM_SYSTEM_PROPERTY = + "ro.arm64.memtag.bootctl_device_policy_manager"; + private static final String MTE_SETTINGS_SYSTEM_PROPERTY = + "ro.arm64.memtag.bootctl_settings_toggle"; + + private final AdvancedProtectionFeature mFeature = new AdvancedProtectionFeature( + FEATURE_ID_ENABLE_MTE); + private final DevicePolicyManager mDevicePolicyManager; + + public MemoryTaggingExtensionHook(@NonNull Context context, + boolean enabled) { + super(context, enabled); + mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); + onAdvancedProtectionChanged(enabled); + } + + @NonNull + @Override + public AdvancedProtectionFeature getFeature() { + return mFeature; + } + + @Override + public boolean isAvailable() { + return SystemProperties.getBoolean(MTE_DPM_SYSTEM_PROPERTY, + SystemProperties.getBoolean(MTE_SETTINGS_SYSTEM_PROPERTY, false)); + } + + @Override + public void onAdvancedProtectionChanged(boolean enabled) { + if (!isAvailable()) { + Slog.i(TAG, "MTE unavailable on device, skipping."); + return; + } + final int mtePolicy; + if (enabled) { + mtePolicy = DevicePolicyManager.MTE_ENABLED; + } else { + mtePolicy = DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY; + } + + Slog.d(TAG, "Setting MTE state to " + mtePolicy); + try { + mDevicePolicyManager.setMtePolicy(ADVANCED_PROTECTION_SYSTEM_ENTITY, mtePolicy); + } catch (UnsupportedOperationException e) { + Slog.i(TAG, "Setting MTE policy unsupported", e); + } + } +} diff --git a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java index 06e9dcdcbedd..0ea88e8523f0 100644 --- a/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java +++ b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java @@ -36,6 +36,9 @@ public class DataAggregator { private static final int MSG_DISABLE = 2; private static final int STORED_EVENTS_SIZE_LIMIT = 1024; + private static final IntrusionDetectionAdminReceiver ADMIN_RECEIVER = + new IntrusionDetectionAdminReceiver(); + private final IntrusionDetectionService mIntrusionDetectionService; private final ArrayList<DataSource> mDataSources; @@ -60,10 +63,19 @@ public class DataAggregator { * Initialize DataSources * @return Whether the initialization succeeds. */ - // TODO: Add the corresponding data sources public boolean initialize() { SecurityLogSource securityLogSource = new SecurityLogSource(mContext, this); mDataSources.add(securityLogSource); + + NetworkLogSource networkLogSource = new NetworkLogSource(mContext, this); + ADMIN_RECEIVER.setNetworkLogEventCallback(networkLogSource); + mDataSources.add(networkLogSource); + + for (DataSource ds : mDataSources) { + if (!ds.initialize()) { + return false; + } + } return true; } diff --git a/services/core/java/com/android/server/security/intrusiondetection/DataSource.java b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java index 0bc448245b76..61fac46be82d 100644 --- a/services/core/java/com/android/server/security/intrusiondetection/DataSource.java +++ b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java @@ -18,6 +18,11 @@ package com.android.server.security.intrusiondetection; public interface DataSource { /** + * Initialize the data source. + */ + boolean initialize(); + + /** * Enable the data collection. */ void enable(); diff --git a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java new file mode 100644 index 000000000000..dba7374fe02a --- /dev/null +++ b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionAdminReceiver.java @@ -0,0 +1,42 @@ +/* + * 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.security.intrusiondetection; + +import android.app.admin.DeviceAdminReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Slog; + +public class IntrusionDetectionAdminReceiver extends DeviceAdminReceiver { + private static final String TAG = "IntrusionDetectionAdminReceiver"; + + private static NetworkLogSource sNetworkLogSource; + + @Override + public void onNetworkLogsAvailable( + Context context, Intent intent, long batchToken, int networkLogsCount) { + if (sNetworkLogSource != null) { + sNetworkLogSource.onNetworkLogsAvailable(batchToken); + } else { + Slog.w(TAG, "Network log receiver is not initialized"); + } + } + + public void setNetworkLogEventCallback(NetworkLogSource networkLogSource) { + sNetworkLogSource = networkLogSource; + } +} diff --git a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java index 82f39b327cea..b25656ebf47f 100644 --- a/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java +++ b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java @@ -60,7 +60,7 @@ public class IntrusionDetectionEventTransportConnection implements ServiceConnec if (!bindService()) { return false; } - AndroidFuture<Integer> resultFuture = new AndroidFuture<>(); + AndroidFuture<Boolean> resultFuture = new AndroidFuture<>(); try { mService.initialize(resultFuture); } catch (RemoteException e) { @@ -68,8 +68,8 @@ public class IntrusionDetectionEventTransportConnection implements ServiceConnec unbindService(); return false; } - Integer result = getFutureResult(resultFuture); - if (result != null && result == 0) { + Boolean result = getFutureResult(resultFuture); + if (result != null && result == true) { return true; } else { unbindService(); @@ -83,22 +83,22 @@ public class IntrusionDetectionEventTransportConnection implements ServiceConnec * @return Whether the data is added to the binder service. */ public boolean addData(List<IntrusionDetectionEvent> data) { - AndroidFuture<Integer> resultFuture = new AndroidFuture<>(); + AndroidFuture<Boolean> resultFuture = new AndroidFuture<>(); try { mService.addData(data, resultFuture); } catch (RemoteException e) { Slog.e(TAG, "Remote Exception", e); return false; } - Integer result = getFutureResult(resultFuture); - return result != null && result == 0; + Boolean result = getFutureResult(resultFuture); + return result != null && result == true; } /** * Release the BackupTransport binder service. */ public void release() { - AndroidFuture<Integer> resultFuture = new AndroidFuture<>(); + AndroidFuture<Boolean> resultFuture = new AndroidFuture<>(); try { mService.release(resultFuture); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java new file mode 100644 index 000000000000..1c93d3f9c6a1 --- /dev/null +++ b/services/core/java/com/android/server/security/intrusiondetection/NetworkLogSource.java @@ -0,0 +1,134 @@ +/* + * 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.security.intrusiondetection; + +import android.app.admin.ConnectEvent; +import android.app.admin.DevicePolicyManager; +import android.app.admin.DnsEvent; +import android.app.admin.NetworkEvent; +import android.content.ComponentName; +import android.content.Context; +import android.security.intrusiondetection.IntrusionDetectionEvent; +import android.util.Slog; + +import java.util.List; +import java.util.stream.Collectors; + +public class NetworkLogSource implements DataSource { + + private static final String TAG = "IntrusionDetectionEvent NetworkLogSource"; + + private DevicePolicyManager mDpm; + private ComponentName mAdmin; + private DataAggregator mDataAggregator; + + public NetworkLogSource(Context context, DataAggregator dataAggregator) { + mDataAggregator = dataAggregator; + mDpm = context.getSystemService(DevicePolicyManager.class); + mAdmin = new ComponentName(context, IntrusionDetectionAdminReceiver.class); + } + + @Override + public boolean initialize() { + try { + if (!mDpm.isAdminActive(mAdmin)) { + Slog.e(TAG, "Admin " + mAdmin.flattenToString() + "is not active admin"); + return false; + } + } catch (SecurityException e) { + Slog.e(TAG, "Security exception in initialize: ", e); + return false; + } + return true; + } + + @Override + public void enable() { + enableNetworkLog(); + } + + @Override + public void disable() { + disableNetworkLog(); + } + + private void enableNetworkLog() { + if (!isNetworkLogEnabled()) { + mDpm.setNetworkLoggingEnabled(mAdmin, true); + } + } + + private void disableNetworkLog() { + if (isNetworkLogEnabled()) { + mDpm.setNetworkLoggingEnabled(mAdmin, false); + } + } + + private boolean isNetworkLogEnabled() { + return mDpm.isNetworkLoggingEnabled(mAdmin); + } + + /** + * Retrieve network logs when onNetworkLogsAvailable callback is received. + * + * @param batchToken The token representing the current batch of network logs. + */ + public void onNetworkLogsAvailable(long batchToken) { + List<NetworkEvent> events; + try { + events = mDpm.retrieveNetworkLogs(mAdmin, batchToken); + } catch (SecurityException e) { + Slog.e( + TAG, + "Admin " + + mAdmin.flattenToString() + + "does not have permission to retrieve network logs", + e); + return; + } + if (events == null) { + if (!isNetworkLogEnabled()) { + Slog.w(TAG, "Network logging is disabled"); + } else { + Slog.e(TAG, "Invalid batch token: " + batchToken); + } + return; + } + + List<IntrusionDetectionEvent> intrusionDetectionEvents = + events.stream() + .filter(event -> event != null) + .map(event -> toIntrusionDetectionEvent(event)) + .collect(Collectors.toList()); + mDataAggregator.addBatchData(intrusionDetectionEvents); + } + + private IntrusionDetectionEvent toIntrusionDetectionEvent(NetworkEvent event) { + if (event instanceof DnsEvent) { + DnsEvent dnsEvent = (DnsEvent) event; + return new IntrusionDetectionEvent(dnsEvent); + } else if (event instanceof ConnectEvent) { + ConnectEvent connectEvent = (ConnectEvent) event; + return new IntrusionDetectionEvent(connectEvent); + } + throw new IllegalArgumentException( + "Invalid event type with ID: " + + event.getId() + + "from package: " + + event.getPackageName()); + } +} diff --git a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java index 226f9d879cab..c5f736e383b2 100644 --- a/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java +++ b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java @@ -22,6 +22,7 @@ import android.app.admin.DevicePolicyManager; import android.app.admin.SecurityLog.SecurityEvent; import android.content.Context; import android.security.intrusiondetection.IntrusionDetectionEvent; +import android.util.Slog; import java.util.List; import java.util.concurrent.Executor; @@ -33,7 +34,7 @@ public class SecurityLogSource implements DataSource { private static final String TAG = "IntrusionDetection SecurityLogSource"; - private SecurityEventCallback mEventCallback = new SecurityEventCallback(); + private SecurityEventCallback mEventCallback; private DevicePolicyManager mDpm; private Executor mExecutor; private DataAggregator mDataAggregator; @@ -42,9 +43,26 @@ public class SecurityLogSource implements DataSource { mDataAggregator = dataAggregator; mDpm = context.getSystemService(DevicePolicyManager.class); mExecutor = Executors.newSingleThreadExecutor(); + } + + @Override + public boolean initialize() { + // Confirm caller is system and the device is managed. Otherwise logs will + // be redacted. + try { + if (!mDpm.isDeviceManaged()) { + Slog.e(TAG, "Caller does not have device owner permissions"); + return false; + } + } catch (SecurityException e) { + Slog.e(TAG, "Security exception in initialize: ", e); + return false; + } mEventCallback = new SecurityEventCallback(); + return true; } + @Override @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void enable() { @@ -72,12 +90,8 @@ public class SecurityLogSource implements DataSource { } } - /** - * Check if security audit logging is enabled for the caller. - * - * @return Whether security audit logging is enabled. - */ - public boolean isAuditLogEnabled() { + @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) + private boolean isAuditLogEnabled() { return mDpm.isAuditLogEnabled(); } diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 887e1861f789..708bca71eced 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -2528,22 +2528,24 @@ public class TrustManagerService extends SystemService { } private void notifyDeviceLockedListenersForUser(int userId, boolean locked) { - int numListeners = mDeviceLockedStateListeners.beginBroadcast(); - try { - IntStream.range(0, numListeners).forEach(i -> { - try { - Integer uid = (Integer) mDeviceLockedStateListeners.getBroadcastCookie(i); - if (userId == uid.intValue()) { - mDeviceLockedStateListeners.getBroadcastItem(i) - .onDeviceLockedStateChanged(locked); + synchronized (mDeviceLockedStateListeners) { + int numListeners = mDeviceLockedStateListeners.beginBroadcast(); + try { + IntStream.range(0, numListeners).forEach(i -> { + try { + Integer uid = (Integer) mDeviceLockedStateListeners.getBroadcastCookie(i); + if (userId == uid.intValue()) { + mDeviceLockedStateListeners.getBroadcastItem(i) + .onDeviceLockedStateChanged(locked); + } + } catch (RemoteException re) { + Log.i(TAG, "Service died", re); } - } catch (RemoteException re) { - Log.i(TAG, "Service died", re); - } - }); + }); - } finally { - mDeviceLockedStateListeners.finishBroadcast(); + } finally { + mDeviceLockedStateListeners.finishBroadcast(); + } } } } diff --git a/services/core/java/com/android/server/utils/LazyJniRegistrar.java b/services/core/java/com/android/server/utils/LazyJniRegistrar.java index ac4a92e12909..6d29e9e58a98 100644 --- a/services/core/java/com/android/server/utils/LazyJniRegistrar.java +++ b/services/core/java/com/android/server/utils/LazyJniRegistrar.java @@ -42,6 +42,9 @@ public final class LazyJniRegistrar { /** Registers native methods for ConsumerIrService. */ public static native void registerConsumerIrService(); + /** Registers native methods for GameManagerService. */ + public static native void registerGameManagerService(); + /** Registers native methods for VrManagerService. */ public static native void registerVrManagerService(); } diff --git a/services/core/java/com/android/server/utils/WatchableImpl.java b/services/core/java/com/android/server/utils/WatchableImpl.java index 8a04ccf2dbc6..fec435154c83 100644 --- a/services/core/java/com/android/server/utils/WatchableImpl.java +++ b/services/core/java/com/android/server/utils/WatchableImpl.java @@ -33,6 +33,7 @@ public class WatchableImpl implements Watchable { /** * The list of observers. */ + @GuardedBy("mObservers") protected final ArrayList<Watcher> mObservers = new ArrayList<>(); /** @@ -83,7 +84,9 @@ public class WatchableImpl implements Watchable { * @return The number of registered observers. */ public int registeredObserverCount() { - return mObservers.size(); + synchronized (mObservers) { + return mObservers.size(); + } } /** diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 9cb8c1ada246..797a350e4395 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -617,11 +617,11 @@ final class VibrationSettings { private void updateRingerMode() { synchronized (mLock) { - // If audio manager was not loaded yet then assume most restrictive mode. - // This will be loaded again as soon as the audio manager is loaded in onSystemReady. - mRingerMode = (mAudioManager == null) - ? AudioManager.RINGER_MODE_SILENT - : mAudioManager.getRingerModeInternal(); + if (mAudioManager == null) { + // Service not ready yet or audio service not available, skip this update request. + return; + } + mRingerMode = mAudioManager.getRingerModeInternal(); } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index d019516cd069..bbef5785dfcb 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1513,11 +1513,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } if (wallpaper.getComponent() != null && isPackageModified(wallpaper.getComponent().getPackageName())) { + ServiceInfo serviceInfo = null; try { - mContext.getPackageManager().getServiceInfo(wallpaper.getComponent(), - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); - } catch (NameNotFoundException e) { + serviceInfo = mIPackageManager.getServiceInfo( + wallpaper.getComponent(), PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, wallpaper.userId); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call IPackageManager.getServiceInfo", e); + } + if (serviceInfo == null) { Slog.e(TAG, "Wallpaper component gone, removing: " + wallpaper.getComponent()); clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, false, null); @@ -3177,7 +3181,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub throw new IllegalArgumentException("Invalid crop rect supplied: " + crop); } int orientation = screenOrientations[i]; - if (orientation == ORIENTATION_UNKNOWN && cropMap.size() > 1) { + if (orientation == ORIENTATION_UNKNOWN && crops.size() > 1) { throw new IllegalArgumentException("Invalid crops supplied: the UNKNOWN" + "screen orientation should only be used in a singleton map"); } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 7cbacd6b0b82..dd769173fb34 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -22,11 +22,9 @@ import static android.os.Build.IS_USER; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; -import static com.android.internal.util.DumpUtils.dumpSparseArray; import static com.android.internal.util.DumpUtils.dumpSparseArrayValues; import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY; import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER; @@ -50,23 +48,15 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowTracingLegacy.WINSCOPE_EXT; import android.accessibilityservice.AccessibilityTrace; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Application; import android.content.Context; import android.content.pm.PackageManagerInternal; -import android.graphics.BLASTBufferQueue; -import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Insets; import android.graphics.Matrix; -import android.graphics.Paint; import android.graphics.Path; -import android.graphics.PixelFormat; import android.graphics.Point; -import android.graphics.PorterDuff.Mode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; @@ -84,25 +74,16 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.util.TypedValue; import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.MagnificationSpec; import android.view.Surface; -import android.view.Surface.OutOfResourcesException; -import android.view.SurfaceControl; import android.view.ViewConfiguration; import android.view.WindowInfo; import android.view.WindowManager; import android.view.WindowManager.TransitionFlags; import android.view.WindowManager.TransitionType; -import android.view.WindowManagerPolicyConstants; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; -import com.android.internal.R; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import com.android.internal.util.TraceBuffer; import com.android.internal.util.function.pooled.PooledLambda; @@ -112,7 +93,6 @@ import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow; import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; -import com.android.window.flags.Flags; import java.io.File; import java.io.IOException; @@ -302,36 +282,6 @@ final class AccessibilityController { } } - /** It is only used by unit test. */ - @VisibleForTesting - Surface forceShowMagnifierSurface(int displayId) { - final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); - if (displayMagnifier != null) { - displayMagnifier.mMagnifiedViewport.mWindow.setAlpha(DisplayMagnifier.MagnifiedViewport - .ViewportWindow.AnimationController.MAX_ALPHA); - return displayMagnifier.mMagnifiedViewport.mWindow.mSurface; - } - return null; - } - - void onWindowLayersChanged(int displayId) { - if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK - | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { - mAccessibilityTracing.logTrace(TAG + ".onWindowLayersChanged", - FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, - "displayId=" + displayId); - } - final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); - if (displayMagnifier != null) { - displayMagnifier.onWindowLayersChanged(); - } - final WindowsForAccessibilityObserver windowsForA11yObserver = - mWindowsForAccessibilityObserver.get(displayId); - if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindows(); - } - } - void onDisplaySizeChanged(DisplayContent displayContent) { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK @@ -564,9 +514,6 @@ final class AccessibilityController { } void dump(PrintWriter pw, String prefix) { - dumpSparseArray(pw, prefix, mDisplayMagnifiers, "magnification display", - (index, key) -> pw.printf("%sDisplay #%d:", prefix + " ", key), - dm -> dm.dump(pw, "")); dumpSparseArrayValues(pw, prefix, mWindowsForAccessibilityObserver, "windows for accessibility observer"); mAccessibilityWindowsPopulator.dump(pw, prefix); @@ -624,7 +571,6 @@ final class AccessibilityController { private final Context mDisplayContext; private final WindowManagerService mService; - private final MagnifiedViewport mMagnifiedViewport; private final Handler mHandler; private final DisplayContent mDisplayContent; private final Display mDisplay; @@ -661,8 +607,6 @@ final class AccessibilityController { mDisplay = display; mHandler = new MyHandler(mService.mH.getLooper()); mUserContextChangedNotifier = new UserContextChangedNotifier(mHandler); - mMagnifiedViewport = Flags.alwaysDrawMagnificationFullscreenBorder() - ? null : new MagnifiedViewport(); mAccessibilityTracing = AccessibilityController.getAccessibilityControllerInternal(mService); mLongAnimationDuration = mDisplayContext.getResources().getInteger( @@ -704,10 +648,6 @@ final class AccessibilityController { } else { mMagnificationSpec.clear(); } - - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.setShowMagnifiedBorderIfNeeded(); - } } void setFullscreenMagnificationActivated(boolean activated) { @@ -716,10 +656,6 @@ final class AccessibilityController { FLAGS_MAGNIFICATION_CALLBACK, "activated=" + activated); } mIsFullscreenMagnificationActivated = activated; - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.setMagnifiedRegionBorderShown(activated, true); - mMagnifiedViewport.showMagnificationBoundsIfNeeded(); - } } boolean isFullscreenMagnificationActivated() { @@ -730,18 +666,6 @@ final class AccessibilityController { return mIsFullscreenMagnificationActivated; } - void onWindowLayersChanged() { - if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { - mAccessibilityTracing.logTrace( - LOG_TAG + ".onWindowLayersChanged", FLAGS_MAGNIFICATION_CALLBACK); - } - if (DEBUG_LAYERS) { - Slog.i(LOG_TAG, "Layers changed."); - } - recomputeBounds(); - mService.scheduleAnimationLocked(); - } - void onDisplaySizeChanged(DisplayContent displayContent) { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { mAccessibilityTracing.logTrace(LOG_TAG + ".onDisplaySizeChanged", @@ -754,9 +678,6 @@ final class AccessibilityController { } recomputeBounds(); - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.onDisplaySizeChanged(); - } mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED); } @@ -927,10 +848,6 @@ final class AccessibilityController { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { mAccessibilityTracing.logTrace(LOG_TAG + ".destroy", FLAGS_MAGNIFICATION_CALLBACK); } - - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.destroyWindow(); - } } void recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded() { @@ -940,10 +857,6 @@ final class AccessibilityController { FLAGS_MAGNIFICATION_CALLBACK); } recomputeBounds(); - - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.drawWindowIfNeeded(); - } } void recomputeBounds() { @@ -1051,16 +964,9 @@ final class AccessibilityController { } visibleWindows.clear(); - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.intersectWithDrawBorderInset(screenWidth, screenHeight); - } - final boolean magnifiedChanged = !mOldMagnificationRegion.equals(mMagnificationRegion); if (magnifiedChanged) { - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.updateBorderDrawingStatus(screenWidth, screenHeight); - } mOldMagnificationRegion.set(mMagnificationRegion); final SomeArgs args = SomeArgs.obtain(); args.arg1 = Region.obtain(mMagnificationRegion); @@ -1140,420 +1046,11 @@ final class AccessibilityController { outSize.set(bounds.width(), bounds.height()); } - void dump(PrintWriter pw, String prefix) { - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.dump(pw, prefix); - } - } - - private final class MagnifiedViewport { - - private final float mBorderWidth; - private final int mHalfBorderWidth; - private final int mDrawBorderInset; - - @Nullable private final ViewportWindow mWindow; - - private boolean mFullRedrawNeeded; - - MagnifiedViewport() { - mBorderWidth = mDisplayContext.getResources().getDimension( - com.android.internal.R.dimen.accessibility_magnification_indicator_width); - mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2); - mDrawBorderInset = (int) mBorderWidth / 2; - mWindow = new ViewportWindow(mDisplayContext); - } - - void updateBorderDrawingStatus(int screenWidth, int screenHeight) { - mWindow.setBounds(mMagnificationRegion); - final Rect dirtyRect = mTempRect1; - if (mFullRedrawNeeded) { - mFullRedrawNeeded = false; - dirtyRect.set(mDrawBorderInset, mDrawBorderInset, - screenWidth - mDrawBorderInset, - screenHeight - mDrawBorderInset); - mWindow.invalidate(dirtyRect); - } else { - final Region dirtyRegion = mTempRegion3; - dirtyRegion.set(mMagnificationRegion); - dirtyRegion.op(mOldMagnificationRegion, Region.Op.XOR); - dirtyRegion.getBounds(dirtyRect); - mWindow.invalidate(dirtyRect); - } - } - - void setShowMagnifiedBorderIfNeeded() { - // If this message is pending, we are in a rotation animation and do not want - // to show the border. We will do so when the pending message is handled. - if (!mHandler.hasMessages( - MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) { - setMagnifiedRegionBorderShown( - isFullscreenMagnificationActivated(), true); - } - } - - // Can be called outside of a surface transaction - void showMagnificationBoundsIfNeeded() { - if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { - mAccessibilityTracing.logTrace(LOG_TAG + ".showMagnificationBoundsIfNeeded", - FLAGS_MAGNIFICATION_CALLBACK); - } - mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED) - .sendToTarget(); - } - - void intersectWithDrawBorderInset(int screenWidth, int screenHeight) { - mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset, - screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset, - Region.Op.INTERSECT); - } - - void onDisplaySizeChanged() { - // If fullscreen magnification is activated, hide the border immediately so - // the user does not see strange artifacts during display size changed caused by - // rotation or folding/unfolding the device. In the rotation case, the - // screenshot used for rotation already has the border. After the rotation is - // completed we will show the border. - if (isFullscreenMagnificationActivated()) { - setMagnifiedRegionBorderShown(false, false); - final long delay = (long) (mLongAnimationDuration - * mService.getWindowAnimationScaleLocked()); - Message message = mHandler.obtainMessage( - MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED); - mHandler.sendMessageDelayed(message, delay); - } - mWindow.updateSize(); - } - - void setMagnifiedRegionBorderShown(boolean shown, boolean animate) { - if (mWindow.setShown(shown, animate)) { - mFullRedrawNeeded = true; - // Clear the old region, so recomputeBounds will refresh the current region. - mOldMagnificationRegion.set(0, 0, 0, 0); - } - } - - void drawWindowIfNeeded() { - mWindow.postDrawIfNeeded(); - } - - void destroyWindow() { - mWindow.releaseSurface(); - } - - void dump(PrintWriter pw, String prefix) { - mWindow.dump(pw, prefix); - } - - // TODO(291891390): Remove this class when we clean up the flag - // alwaysDrawMagnificationFullscreenBorder - private final class ViewportWindow implements Runnable { - private static final String SURFACE_TITLE = "Magnification Overlay"; - - private final Region mBounds = new Region(); - private final Rect mDirtyRect = new Rect(); - private final Paint mPaint = new Paint(); - - private final SurfaceControl mSurfaceControl; - /** After initialization, it should only be accessed from animation thread. */ - private final SurfaceControl.Transaction mTransaction; - private final BLASTBufferQueue mBlastBufferQueue; - private final Surface mSurface; - - private final AnimationController mAnimationController; - - private boolean mShown; - private boolean mLastSurfaceShown; - private int mAlpha; - private int mPreviousAlpha; - - private volatile boolean mInvalidated; - - ViewportWindow(Context context) { - SurfaceControl surfaceControl = null; - try { - surfaceControl = mDisplayContent - .makeOverlay() - .setName(SURFACE_TITLE) - .setBLASTLayer() - .setFormat(PixelFormat.TRANSLUCENT) - .setCallsite("ViewportWindow") - .build(); - } catch (OutOfResourcesException oore) { - /* ignore */ - } - mSurfaceControl = surfaceControl; - mDisplay.getRealSize(mScreenSize); - mBlastBufferQueue = new BLASTBufferQueue(SURFACE_TITLE, mSurfaceControl, - mScreenSize.x, mScreenSize.y, PixelFormat.RGBA_8888); - - final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); - final int layer = - mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY) * - WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER; - t.setLayer(mSurfaceControl, layer).setPosition(mSurfaceControl, 0, 0); - InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t, - mDisplayContent.getDisplayId(), "Magnification Overlay"); - t.apply(); - mTransaction = t; - mSurface = mBlastBufferQueue.createSurface(); - - mAnimationController = new AnimationController(context, - mService.mH.getLooper()); - - TypedValue typedValue = new TypedValue(); - context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight, - typedValue, true); - final int borderColor = context.getColor(typedValue.resourceId); - - mPaint.setStyle(Paint.Style.STROKE); - mPaint.setStrokeWidth(mBorderWidth); - mPaint.setColor(borderColor); - - mInvalidated = true; - } - - /** Returns {@code true} if the state is changed to shown. */ - boolean setShown(boolean shown, boolean animate) { - synchronized (mService.mGlobalLock) { - if (mShown == shown) { - return false; - } - mShown = shown; - mAnimationController.onFrameShownStateChanged(shown, animate); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown); - } - } - return shown; - } - - @SuppressWarnings("unused") - // Called reflectively from an animator. - int getAlpha() { - synchronized (mService.mGlobalLock) { - return mAlpha; - } - } - - void setAlpha(int alpha) { - synchronized (mService.mGlobalLock) { - if (mAlpha == alpha) { - return; - } - mAlpha = alpha; - invalidate(null); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha); - } - } - } - - void setBounds(Region bounds) { - synchronized (mService.mGlobalLock) { - if (mBounds.equals(bounds)) { - return; - } - mBounds.set(bounds); - invalidate(mDirtyRect); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds); - } - } - } - - void updateSize() { - synchronized (mService.mGlobalLock) { - getDisplaySizeLocked(mScreenSize); - mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y, - PixelFormat.RGBA_8888); - invalidate(mDirtyRect); - } - } - - void invalidate(Rect dirtyRect) { - if (dirtyRect != null) { - mDirtyRect.set(dirtyRect); - } else { - mDirtyRect.setEmpty(); - } - mInvalidated = true; - mService.scheduleAnimationLocked(); - } - - void postDrawIfNeeded() { - if (mInvalidated) { - mService.mAnimationHandler.post(this); - } - } - - @Override - public void run() { - drawOrRemoveIfNeeded(); - } - - /** - * This method must only be called by animation handler directly to make sure - * thread safe and there is no lock held outside. - */ - private void drawOrRemoveIfNeeded() { - // Drawing variables (alpha, dirty rect, and bounds) access is synchronized - // using WindowManagerGlobalLock. Grab copies of these values before - // drawing on the canvas so that drawing can be performed outside of the lock. - int alpha; - boolean redrawBounds; - Rect drawingRect = null; - Region drawingBounds = null; - synchronized (mService.mGlobalLock) { - if (mBlastBufferQueue.mNativeObject == 0) { - // Complete removal since releaseSurface has been called. - if (mSurface.isValid()) { - mTransaction.remove(mSurfaceControl).apply(); - mSurface.release(); - } - return; - } - if (!mInvalidated) { - return; - } - mInvalidated = false; - - alpha = mAlpha; - // For b/325863281, we should ensure the drawn border path is cleared when - // alpha = 0. Therefore, we cache the last used alpha when drawing as - // mPreviousAlpha and check it here. If mPreviousAlpha > 0, which means - // the border is showing now, then we should still redraw the clear path - // on the canvas so the border is cleared. - redrawBounds = mAlpha > 0 || mPreviousAlpha > 0; - if (redrawBounds) { - drawingBounds = new Region(mBounds); - // Empty dirty rectangle means unspecified. - if (mDirtyRect.isEmpty()) { - mBounds.getBounds(mDirtyRect); - } - mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth); - drawingRect = new Rect(mDirtyRect); - if (DEBUG_VIEWPORT_WINDOW) { - Slog.i(LOG_TAG, "ViewportWindow bounds: " + mBounds); - Slog.i(LOG_TAG, "ViewportWindow dirty rect: " + mDirtyRect); - } - } - } - - final boolean showSurface; - // Draw without holding WindowManagerGlobalLock. - if (redrawBounds) { - Canvas canvas = null; - try { - canvas = mSurface.lockCanvas(drawingRect); - } catch (IllegalArgumentException | OutOfResourcesException e) { - /* ignore */ - } - if (canvas == null) { - return; - } - canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); - mPaint.setAlpha(alpha); - canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint); - mSurface.unlockCanvasAndPost(canvas); - mPreviousAlpha = alpha; - } - - showSurface = alpha > 0; - - if (showSurface && !mLastSurfaceShown) { - mTransaction.show(mSurfaceControl).apply(); - mLastSurfaceShown = true; - } else if (!showSurface && mLastSurfaceShown) { - mTransaction.hide(mSurfaceControl).apply(); - mLastSurfaceShown = false; - } - } - - @GuardedBy("mService.mGlobalLock") - void releaseSurface() { - mBlastBufferQueue.destroy(); - // Post to perform cleanup on the thread which handles mSurface. - mService.mAnimationHandler.post(this); - } - - void dump(PrintWriter pw, String prefix) { - pw.println(prefix - + " mBounds= " + mBounds - + " mDirtyRect= " + mDirtyRect - + " mWidth= " + mScreenSize.x - + " mHeight= " + mScreenSize.y); - } - - private final class AnimationController extends Handler { - private static final String PROPERTY_NAME_ALPHA = "alpha"; - - private static final int MIN_ALPHA = 0; - private static final int MAX_ALPHA = 255; - - private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1; - - private final ValueAnimator mShowHideFrameAnimator; - - AnimationController(Context context, Looper looper) { - super(looper); - mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this, - PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA); - - Interpolator interpolator = new DecelerateInterpolator(2.5f); - final long longAnimationDuration = context.getResources().getInteger( - com.android.internal.R.integer.config_longAnimTime); - - mShowHideFrameAnimator.setInterpolator(interpolator); - mShowHideFrameAnimator.setDuration(longAnimationDuration); - } - - void onFrameShownStateChanged(boolean shown, boolean animate) { - obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED, - shown ? 1 : 0, animate ? 1 : 0).sendToTarget(); - } - - @Override - public void handleMessage(Message message) { - switch (message.what) { - case MSG_FRAME_SHOWN_STATE_CHANGED: { - final boolean shown = message.arg1 == 1; - final boolean animate = message.arg2 == 1; - - if (animate) { - if (mShowHideFrameAnimator.isRunning()) { - mShowHideFrameAnimator.reverse(); - } else { - if (shown) { - mShowHideFrameAnimator.start(); - } else { - mShowHideFrameAnimator.reverse(); - } - } - } else { - mShowHideFrameAnimator.cancel(); - if (shown) { - setAlpha(MAX_ALPHA); - } else { - setAlpha(MIN_ALPHA); - } - } - } break; - } - } - } - } - } - private class MyHandler extends Handler { public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1; public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3; public static final int MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED = 4; - - // TODO(291891390): Remove this field when we clean up the flag - // alwaysDrawMagnificationFullscreenBorder - public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5; - public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6; + public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 5; MyHandler(Looper looper) { super(looper); @@ -1577,17 +1074,6 @@ final class AccessibilityController { mCallbacks.onDisplaySizeChanged(); } break; - case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { - synchronized (mService.mGlobalLock) { - if (isFullscreenMagnificationActivated()) { - if (!Flags.alwaysDrawMagnificationFullscreenBorder()) { - mMagnifiedViewport.setMagnifiedRegionBorderShown(true, true); - } - mService.scheduleAnimationLocked(); - } - } - } break; - case MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED: { final boolean shown = message.arg1 == 1; mCallbacks.onImeWindowVisibilityChanged(shown); @@ -1784,22 +1270,13 @@ final class AccessibilityController { mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked( mDisplayId, visibleWindows); - if (!com.android.server.accessibility.Flags.computeWindowChangesOnA11yV2()) { - windows = buildWindowInfoListLocked(visibleWindows, screenSize); - } - // Gets the top focused display Id and window token for supporting multi-display. topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId(); topFocusedWindowToken = topFocusedWindowState.mClient.asBinder(); } - if (com.android.server.accessibility.Flags.computeWindowChangesOnA11yV2()) { - mCallback.onAccessibilityWindowsChanged(forceSend, topFocusedDisplayId, - topFocusedWindowToken, screenSize, visibleWindows); - } else { - mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId, - topFocusedWindowToken, windows); - } + mCallback.onAccessibilityWindowsChanged(forceSend, topFocusedDisplayId, + topFocusedWindowToken, screenSize, visibleWindows); // Recycle the windows as we do not need them. for (final AccessibilityWindowsPopulator.AccessibilityWindow window : visibleWindows) { @@ -1808,166 +1285,6 @@ final class AccessibilityController { mInitialized = true; } - // Here are old code paths, called when computeWindowChangesOnA11yV2 flag is disabled. - // LINT.IfChange - - /** - * From a list of windows, decides windows to be exposed to accessibility based on touchable - * region in the screen. - */ - private List<WindowInfo> buildWindowInfoListLocked(List<AccessibilityWindow> visibleWindows, - Point screenSize) { - final List<WindowInfo> windows = new ArrayList<>(); - final Set<IBinder> addedWindows = mTempBinderSet; - addedWindows.clear(); - - boolean focusedWindowAdded = false; - - final int visibleWindowCount = visibleWindows.size(); - - Region unaccountedSpace = mTempRegion; - unaccountedSpace.set(0, 0, screenSize.x, screenSize.y); - - // Iterate until we figure out what is touchable for the entire screen. - for (int i = 0; i < visibleWindowCount; i++) { - final AccessibilityWindow a11yWindow = visibleWindows.get(i); - final Region regionInWindow = new Region(); - a11yWindow.getTouchableRegionInWindow(regionInWindow); - if (windowMattersToAccessibility(a11yWindow, regionInWindow, unaccountedSpace)) { - addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows); - if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) { - updateUnaccountedSpace(a11yWindow, unaccountedSpace); - } - focusedWindowAdded |= a11yWindow.isFocused(); - } else if (a11yWindow.isUntouchableNavigationBar()) { - // If this widow is navigation bar without touchable region, accounting the - // region of navigation bar inset because all touch events from this region - // would be received by launcher, i.e. this region is a un-touchable one - // for the application. - unaccountedSpace.op( - getSystemBarInsetsFrame( - mService.mWindowMap.get(a11yWindow.getWindowInfo().token)), - unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - } - - if (unaccountedSpace.isEmpty() && focusedWindowAdded) { - break; - } - } - - // Remove child/parent references to windows that were not added. - final int windowCount = windows.size(); - for (int i = 0; i < windowCount; i++) { - WindowInfo window = windows.get(i); - if (!addedWindows.contains(window.parentToken)) { - window.parentToken = null; - } - if (window.childTokens != null) { - final int childTokenCount = window.childTokens.size(); - for (int j = childTokenCount - 1; j >= 0; j--) { - if (!addedWindows.contains(window.childTokens.get(j))) { - window.childTokens.remove(j); - } - } - // Leave the child token list if empty. - } - } - - addedWindows.clear(); - - return windows; - } - - // Some windows should be excluded from unaccounted space computation, though they still - // should be reported - private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) { - // Do not account space of trusted non-touchable windows, except the split-screen - // divider. - // If it's not trusted, touch events are not sent to the windows behind it. - if (!a11yWindow.isTouchable() - && (a11yWindow.getType() != TYPE_DOCK_DIVIDER) - && a11yWindow.isTrustedOverlay()) { - return false; - } - - if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { - return false; - } - return true; - } - - private boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow, - Region regionInScreen, Region unaccountedSpace) { - if (a11yWindow.isFocused()) { - return true; - } - - // Ignore non-touchable windows, except the split-screen divider, which is - // occasionally non-touchable but still useful for identifying split-screen - // mode and the PIP menu. - if (!a11yWindow.isTouchable() - && (a11yWindow.getType() != TYPE_DOCK_DIVIDER - && !a11yWindow.isPIPMenu())) { - return false; - } - - // If the window is completely covered by other windows - ignore. - if (unaccountedSpace.quickReject(regionInScreen)) { - return false; - } - - // Add windows of certain types not covered by modal windows. - if (isReportedWindowType(a11yWindow.getType())) { - return true; - } - - return false; - } - - private void updateUnaccountedSpace(AccessibilityWindow a11yWindow, - Region unaccountedSpace) { - if (a11yWindow.getType() - != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { - // Account for the space this window takes if the window - // is not an accessibility overlay which does not change - // the reported windows. - final Region touchableRegion = mTempRegion2; - a11yWindow.getTouchableRegionInScreen(touchableRegion); - unaccountedSpace.op(touchableRegion, unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - } - } - - private static void addPopulatedWindowInfo(AccessibilityWindow a11yWindow, - Region regionInScreen, List<WindowInfo> out, Set<IBinder> tokenOut) { - final WindowInfo window = a11yWindow.getWindowInfo(); - if (window.token == null) { - // The window was used in calculating visible windows but does not have an - // associated IWindow token, so exclude it from the list returned to accessibility. - return; - } - window.regionInScreen.set(regionInScreen); - window.layer = tokenOut.size(); - out.add(window); - tokenOut.add(window.token); - } - - private static boolean isReportedWindowType(int windowType) { - return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER - && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS - && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY - && windowType != WindowManager.LayoutParams.TYPE_DRAG - && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER - && windowType != WindowManager.LayoutParams.TYPE_POINTER - && windowType != TYPE_MAGNIFICATION_OVERLAY - && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY - && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY - && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION); - } - - // LINT.ThenChange(/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java) - private WindowState getTopFocusWindow() { return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus; } diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java index fd2a909f8b05..7fc11e6c3ac9 100644 --- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java +++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java @@ -724,8 +724,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener { } // Compute system bar insets frame if needed. - if (com.android.server.accessibility.Flags.computeWindowChangesOnA11yV2() - && windowState != null && instance.isUntouchableNavigationBar()) { + if (windowState != null && instance.isUntouchableNavigationBar()) { final InsetsSourceProvider provider = windowState.getControllableInsetProvider(); if (provider != null) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index f70dec175c06..d2546e49267a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3230,8 +3230,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return false; } // If the user preference respects aspect ratio, then it becomes non-resizable. - return !mAppCompatController.getAppCompatOverrides().getAppCompatAspectRatioOverrides() - .shouldApplyUserMinAspectRatioOverride(); + return mAppCompatController.getAppCompatOverrides().getAppCompatAspectRatioOverrides() + .userPreferenceCompatibleWithNonResizability(); } boolean isResizeable() { @@ -4575,10 +4575,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // at #postWindowRemoveCleanupLocked return false; } + + // Link the fixed rotation transform to this activity since we are transferring the + // starting window. + if (fromActivity.hasFixedRotationTransform()) { + mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(this, + false /* checkOpening */); + } // Do not transfer if the orientation doesn't match, redraw starting window while it is // on top will cause flicker. - if (fromActivity.getRequestedConfigurationOrientation() - != getRequestedConfigurationOrientation()) { + final int fromOrientation = fromActivity.getConfiguration().orientation; + final int requestedOrientation = getRequestedConfigurationOrientation(); + if (requestedOrientation == ORIENTATION_UNDEFINED) { + if (fromOrientation != getConfiguration().orientation) { + return false; + } + } else if (fromOrientation != requestedOrientation) { return false; } // In this case, the starting icon has already been displayed, so start @@ -4592,13 +4604,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final long origId = Binder.clearCallingIdentity(); try { - // Link the fixed rotation transform to this activity since we are transferring the - // starting window. - if (fromActivity.hasFixedRotationTransform()) { - mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(this, - false /* checkOpening */); - } - // Transfer the starting window over to the new token. mStartingData = fromActivity.mStartingData; mStartingSurface = fromActivity.mStartingSurface; diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java index 90c0866d7dff..086b11c25a8d 100644 --- a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java +++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java @@ -135,6 +135,12 @@ class AppCompatAspectRatioOverrides { && aspectRatio != USER_MIN_ASPECT_RATIO_FULLSCREEN; } + boolean userPreferenceCompatibleWithNonResizability() { + final int aspectRatio = getUserMinAspectRatioOverrideCode(); + return aspectRatio == USER_MIN_ASPECT_RATIO_UNSET + || aspectRatio == USER_MIN_ASPECT_RATIO_FULLSCREEN; + } + boolean shouldApplyUserFullscreenOverride() { if (isUserFullscreenOverrideEnabled()) { final int aspectRatio = getUserMinAspectRatioOverrideCode(); diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java index 9754595581a0..47d30c98120c 100644 --- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java +++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java @@ -164,12 +164,14 @@ class AppCompatCameraOverrides { * <p>The treatment is enabled when the following conditions are met: * <ul> * <li>Feature flag gating the camera compatibility free-form treatment is enabled. - * <li>Activity is opted in by the device manufacturer with override. + * <li>Activity is opted-in using per-app override, or the treatment is enabled for all apps. * </ul> */ boolean shouldApplyFreeformTreatmentForCameraCompat() { - return Flags.enableCameraCompatForDesktopWindowing() && isChangeEnabled(mActivityRecord, - OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT); + return Flags.enableCameraCompatForDesktopWindowing() && (isChangeEnabled(mActivityRecord, + OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT) + || mActivityRecord.mWmService.mAppCompatConfiguration + .isCameraCompatFreeformWindowingTreatmentEnabled()); } boolean isOverrideOrientationOnlyForCameraEnabled() { diff --git a/services/core/java/com/android/server/wm/AppCompatConfiguration.java b/services/core/java/com/android/server/wm/AppCompatConfiguration.java index 38c6de146293..9a15c4a8bff2 100644 --- a/services/core/java/com/android/server/wm/AppCompatConfiguration.java +++ b/services/core/java/com/android/server/wm/AppCompatConfiguration.java @@ -304,6 +304,11 @@ final class AppCompatConfiguration { // See RefreshCallbackItem for context. private boolean mIsCameraCompatRefreshCycleThroughStopEnabled = true; + // Whether camera compat freeform treatment should be enabled for all eligible activities. + // This has the same effect as enabling the per-app override + // ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT for every app. + private boolean mIsCameraCompatFreeformWindowingTreatmentEnabled = false; + // Whether should ignore app requested orientation in response to an app // calling Activity#setRequestedOrientation. See // LetterboxUiController#shouldIgnoreRequestedOrientation for details. @@ -1351,6 +1356,30 @@ final class AppCompatConfiguration { } /** + * Sets whether the camera compatibility treatment in freeform windowing mode is enabled for + * all fixed-orientation apps when using camera. + */ + void setIsCameraCompatFreeformWindowingTreatmentEnabled(boolean enabled) { + mIsCameraCompatFreeformWindowingTreatmentEnabled = enabled; + } + + /** + * Whether the camera compatibility treatment in freeform windowing mode is enabled for all + * fixed-orientation apps when using camera. + */ + boolean isCameraCompatFreeformWindowingTreatmentEnabled() { + return mIsCameraCompatFreeformWindowingTreatmentEnabled; + } + + /** + * Resets whether the camera compatibility treatment in freeform windowing mode is enabled for + * all fixed-orientation apps when using camera. + */ + void resetIsCameraCompatFreeformWindowingTreatmentEnabled() { + mIsCameraCompatFreeformWindowingTreatmentEnabled = false; + } + + /** * Checks whether rotation compat policy for immersive apps that prevents auto rotation * into non-optimal screen orientation while in fullscreen is enabled at build time. This is * used when we need to safely initialize a component before the {@link DeviceConfig} flag diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java index 145a3767c149..203932d4df58 100644 --- a/services/core/java/com/android/server/wm/AppCompatController.java +++ b/services/core/java/com/android/server/wm/AppCompatController.java @@ -76,6 +76,16 @@ class AppCompatController { mAppCompatSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(mActivityRecord, mAppCompatOverrides); mAllowRestrictedResizability = AppCompatUtils.asLazy(() -> { + // Application level. + try { + if (packageManager.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, + mActivityRecord.packageName).getBoolean()) { + return true; + } + } catch (PackageManager.NameNotFoundException e) { + // Fall through. + } + // Activity level. try { return packageManager.getPropertyAsUser( PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java index 0b5872b3e601..93ccd74c6b23 100644 --- a/services/core/java/com/android/server/wm/ContentRecorder.java +++ b/services/core/java/com/android/server/wm/ContentRecorder.java @@ -32,6 +32,7 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.media.projection.IMediaProjectionManager; +import android.media.projection.StopReason; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -226,6 +227,7 @@ final class ContentRecorder implements WindowContainerListener { + "size %s", mDisplayContent.getDisplayId(), recordedContentBounds, recordedContentOrientation, surfaceSize); + updateMirroredSurface(mRecordedWindowContainer.getSyncTransaction(), recordedContentBounds, surfaceSize); } else { @@ -295,12 +297,12 @@ final class ContentRecorder implements WindowContainerListener { * Ensure recording does not fall back to the display stack; ensure the recording is stopped * and the client notified by tearing down the virtual display. */ - private void stopMediaProjection() { + private void stopMediaProjection(@StopReason int stopReason) { ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Content Recording: Stop MediaProjection on virtual display %d", mDisplayContent.getDisplayId()); if (mMediaProjectionManager != null) { - mMediaProjectionManager.stopActiveProjection(); + mMediaProjectionManager.stopActiveProjection(stopReason); } } @@ -507,7 +509,7 @@ final class ContentRecorder implements WindowContainerListener { if (shouldExitTaskRecording) { // Clean up the cached session first to ensure recording doesn't re-start, since // tearing down the display will generate display events which will trickle back here. - stopMediaProjection(); + stopMediaProjection(StopReason.STOP_ERROR); } } @@ -599,9 +601,13 @@ final class ContentRecorder implements WindowContainerListener { mLastRecordedBounds = new Rect(recordedContentBounds); mLastConsumingSurfaceSize.x = surfaceSize.x; mLastConsumingSurfaceSize.y = surfaceSize.y; - // Request to notify the client about the resize. - mMediaProjectionManager.notifyActiveProjectionCapturedContentResized( - mLastRecordedBounds.width(), mLastRecordedBounds.height()); + + // Request to notify the client about the updated bounds. + mMediaProjectionManager.notifyCaptureBoundsChanged( + mContentRecordingSession.getContentToRecord(), + mContentRecordingSession.getTargetUid(), + mLastRecordedBounds + ); } /** @@ -641,7 +647,7 @@ final class ContentRecorder implements WindowContainerListener { clearContentRecordingSession(); // Clean up the cached session first to ensure recording doesn't re-start, since // tearing down the display will generate display events which will trickle back here. - stopMediaProjection(); + stopMediaProjection(StopReason.STOP_TARGET_REMOVED); } // WindowContainerListener @@ -674,10 +680,10 @@ final class ContentRecorder implements WindowContainerListener { } @VisibleForTesting interface MediaProjectionManagerWrapper { - void stopActiveProjection(); - void notifyActiveProjectionCapturedContentResized(int width, int height); + void stopActiveProjection(@StopReason int stopReason); void notifyActiveProjectionCapturedContentVisibilityChanged(boolean isVisible); void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode); + void notifyCaptureBoundsChanged(int contentToRecord, int targetUid, Rect captureBounds); } private static final class RemoteMediaProjectionManagerWrapper implements @@ -691,7 +697,7 @@ final class ContentRecorder implements WindowContainerListener { } @Override - public void stopActiveProjection() { + public void stopActiveProjection(@StopReason int stopReason) { fetchMediaProjectionManager(); if (mIMediaProjectionManager == null) { return; @@ -700,7 +706,7 @@ final class ContentRecorder implements WindowContainerListener { ProtoLog.e(WM_DEBUG_CONTENT_RECORDING, "Content Recording: stopping active projection for display %d", mDisplayId); - mIMediaProjectionManager.stopActiveProjection(); + mIMediaProjectionManager.stopActiveProjection(stopReason); } catch (RemoteException e) { ProtoLog.e(WM_DEBUG_CONTENT_RECORDING, "Content Recording: Unable to tell MediaProjectionManagerService to stop " @@ -710,52 +716,51 @@ final class ContentRecorder implements WindowContainerListener { } @Override - public void notifyActiveProjectionCapturedContentResized(int width, int height) { + public void notifyActiveProjectionCapturedContentVisibilityChanged(boolean isVisible) { fetchMediaProjectionManager(); if (mIMediaProjectionManager == null) { return; } try { - mIMediaProjectionManager.notifyActiveProjectionCapturedContentResized(width, - height); + mIMediaProjectionManager.notifyActiveProjectionCapturedContentVisibilityChanged( + isVisible); } catch (RemoteException e) { ProtoLog.e(WM_DEBUG_CONTENT_RECORDING, "Content Recording: Unable to tell MediaProjectionManagerService about " - + "resizing the active projection: %s", + + "visibility change on the active projection: %s", e); } } @Override - public void notifyActiveProjectionCapturedContentVisibilityChanged(boolean isVisible) { + public void notifyWindowingModeChanged(int contentToRecord, int targetUid, + int windowingMode) { fetchMediaProjectionManager(); if (mIMediaProjectionManager == null) { return; } try { - mIMediaProjectionManager.notifyActiveProjectionCapturedContentVisibilityChanged( - isVisible); + mIMediaProjectionManager.notifyWindowingModeChanged( + contentToRecord, targetUid, windowingMode); } catch (RemoteException e) { ProtoLog.e(WM_DEBUG_CONTENT_RECORDING, - "Content Recording: Unable to tell MediaProjectionManagerService about " - + "visibility change on the active projection: %s", - e); + "Content Recording: Unable to tell log windowing mode change: %s", e); } } @Override - public void notifyWindowingModeChanged(int contentToRecord, int targetUid, - int windowingMode) { + public void notifyCaptureBoundsChanged(int contentToRecord, int targetUid, + Rect captureBounds) { fetchMediaProjectionManager(); if (mIMediaProjectionManager == null) { return; } try { - mIMediaProjectionManager.notifyWindowingModeChanged( - contentToRecord, targetUid, windowingMode); + mIMediaProjectionManager.notifyCaptureBoundsChanged( + contentToRecord, targetUid, captureBounds); } catch (RemoteException e) { ProtoLog.e(WM_DEBUG_CONTENT_RECORDING, - "Content Recording: Unable to tell log windowing mode change: %s", e); + "Content Recording: Unable to tell log bounds change: %s", e); } } diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java index 6f8c17a9ac75..4e79e377a2a3 100644 --- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java +++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java @@ -403,6 +403,7 @@ class DeferredDisplayUpdater { || first.renderFrameRate != second.renderFrameRate || first.hasArrSupport != second.hasArrSupport || !Objects.equals(first.frameRateCategoryRate, second.frameRateCategoryRate) + || !Arrays.equals(first.supportedRefreshRates, second.supportedRefreshRates) || first.defaultModeId != second.defaultModeId || first.userPreferredModeId != second.userPreferredModeId || !Arrays.equals(first.supportedModes, second.supportedModes) diff --git a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java index c8cb62132b4c..43855aa3d247 100644 --- a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java +++ b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java @@ -196,10 +196,11 @@ public class DesktopAppCompatAspectRatioPolicy { if (!aspectRatioOverrides.shouldOverrideMinAspectRatio() && !AppCompatCameraPolicy.shouldOverrideMinAspectRatioForCamera(mActivityRecord)) { - if (mActivityRecord.isUniversalResizeable()) { + final float minAspectRatio = info.getMinAspectRatio(); + if (minAspectRatio == 0 || mActivityRecord.isUniversalResizeable()) { return 0; } - return info.getMinAspectRatio(); + return minAspectRatio; } if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY) @@ -242,10 +243,11 @@ public class DesktopAppCompatAspectRatioPolicy { if (mTransparentPolicy.isRunning()) { return mTransparentPolicy.getInheritedMaxAspectRatio(); } - if (mActivityRecord.isUniversalResizeable()) { + final float maxAspectRatio = mActivityRecord.info.getMaxAspectRatio(); + if (maxAspectRatio == 0 || mActivityRecord.isUniversalResizeable()) { return 0; } - return mActivityRecord.info.getMaxAspectRatio(); + return maxAspectRatio; } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e9e550e72a00..9a33df13bb6a 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5502,14 +5502,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Attach the SystemUiContext to this DisplayContent the get latest configuration. // Note that the SystemUiContext will be removed automatically if this DisplayContent // is detached. - final WindowProcessController wpc = mAtmService.getProcessController( - getDisplayUiContext().getIApplicationThread()); - mWmService.mWindowContextListenerController.registerWindowContainerListener( - wpc, getDisplayUiContext().getWindowContextToken(), this, - INVALID_WINDOW_TYPE, null /* options */); + registerSystemUiContext(); } } + private void registerSystemUiContext() { + final WindowProcessController wpc = mAtmService.getProcessController( + getDisplayUiContext().getIApplicationThread()); + mWmService.mWindowContextListenerController.registerWindowContainerListener( + wpc, getDisplayUiContext().getWindowContextToken(), this, + INVALID_WINDOW_TYPE, null /* options */); + } + @Override void assignChildLayers(SurfaceControl.Transaction t) { assignRelativeLayerForIme(t, false /* forceUpdate */); diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 4cf1fb400fe7..df209ff4cf50 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -485,6 +485,9 @@ public class DisplayRotation { if (isDefaultDisplay) { updateOrientationListenerLw(); } + } else if (mCompatPolicyForImmersiveApps != null + && mCompatPolicyForImmersiveApps.deferOrientationUpdate()) { + return false; } return updateRotationUnchecked(forceUpdate); } diff --git a/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java index 094434d07cfe..046ed614dc19 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java @@ -17,10 +17,13 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; +import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.Configuration.Orientation; @@ -66,6 +69,37 @@ final class DisplayRotationImmersiveAppCompatPolicy { } /** + * Returns {@code true} if the orientation update should be skipped and it will update when + * transition is done. This is to keep the orientation which was preserved by + * {@link #isRotationLockEnforced} from being changed by a transient launch (i.e. recents). + */ + boolean deferOrientationUpdate() { + if (mDisplayRotation.getUserRotation() != USER_ROTATION_FREE + || mDisplayRotation.getLastOrientation() != SCREEN_ORIENTATION_UNSPECIFIED) { + return false; + } + final WindowOrientationListener orientationListener = + mDisplayRotation.getOrientationListener(); + if (orientationListener == null + || orientationListener.getProposedRotation() == mDisplayRotation.getRotation()) { + return false; + } + // The above conditions mean that isRotationLockEnforced might have taken effect: + // Auto-rotation is enabled and the proposed rotation is not applied. + // Then the update should defer until the transition idle to avoid disturbing animation. + if (!mDisplayContent.mTransitionController.hasTransientLaunch(mDisplayContent)) { + return false; + } + mDisplayContent.mTransitionController.mStateValidators.add(() -> { + if (!isRotationLockEnforcedLocked(orientationListener.getProposedRotation())) { + mDisplayContent.mWmService.updateRotation(false /* alwaysSendConfiguration */, + false /* forceRelayout */); + } + }); + return true; + } + + /** * Decides whether it is necessary to lock screen rotation, preventing auto rotation, based on * the top activity configuration and proposed screen rotation. * diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 49f717e228d2..48e1c069821c 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -346,12 +346,15 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); controlTarget.setImeInputTargetRequestedVisibility(imeVisible); + // not all virtual displays have an ImeInsetsSourceProvider, so it is not + // guaranteed that the IME will be started when the control target reports its + // requested visibility back. Thus, invoking the listener here. + invokeOnImeRequestedChangedListener(imeInsetsTarget, statsToken); } else { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY); } } - invokeOnImeRequestedChangedListener(imeInsetsTarget, statsToken); } // TODO(b/353463205) check callers to see if we can make statsToken @NonNull diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index f0e12fec3107..27f82d90fdac 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -495,11 +495,18 @@ class RecentTasks { mTaskNotificationController.notifyTaskListUpdated(); } - private void notifyTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess) { + private void notifyTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess, + boolean removedForAddTask) { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed, killProcess); } mTaskNotificationController.notifyTaskListUpdated(); + if (removedForAddTask) { + mTaskNotificationController.notifyRecentTaskRemovedForAddTask(task.mTaskId); + } + } + private void notifyTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess) { + notifyTaskRemoved(task, wasTrimmed, killProcess, false /* removedForAddTask */); } /** @@ -1635,7 +1642,8 @@ class RecentTasks { // from becoming dangling. mHiddenTasks.add(0, removedTask); } - notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */); + notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */, + true /* removedForAddTask */); if (DEBUG_RECENTS_TRIM_TASKS) { Slog.d(TAG, "Trimming task=" + removedTask + " for addition of task=" + task); diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java index 70b214cc71b5..88e534351e2e 100644 --- a/services/core/java/com/android/server/wm/SafeActivityOptions.java +++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java @@ -361,10 +361,13 @@ public class SafeActivityOptions { } // If launched from bubble is specified, then ensure that the caller is system or sysui. - if (options.getLaunchedFromBubble() && !isSystemOrSystemUI(callingPid, callingUid)) { + if ((options.getLaunchedFromBubble() || options.getTaskAlwaysOnTop()) + && !isSystemOrSystemUI(callingPid, callingUid)) { final String msg = "Permission Denial: starting " + getIntentString(intent) + " from " + callerApp + " (pid=" + callingPid - + ", uid=" + callingUid + ") with launchedFromBubble=true"; + + ", uid=" + callingUid + ") with" + + (options.getLaunchedFromBubble() ? " launchedFromBubble=true" : "") + + (options.getTaskAlwaysOnTop() ? " taskAlwaysOnTop=true" : ""); Slog.w(TAG, msg); throw new SecurityException(msg); } diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 586f3c35c0c4..c3649fe98056 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -62,6 +62,8 @@ class TaskChangeNotificationController { private static final int NOTIFY_TASK_MOVED_TO_BACK_LISTENERS_MSG = 27; private static final int NOTIFY_LOCK_TASK_MODE_CHANGED_MSG = 28; private static final int NOTIFY_TASK_SNAPSHOT_INVALIDATED_LISTENERS_MSG = 29; + private static final int NOTIFY_RECENT_TASK_REMOVED_FOR_ADD_TASK_LISTENERS_MSG = 30; + // Delay in notifying task stack change listeners (in millis) private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; @@ -167,6 +169,10 @@ class TaskChangeNotificationController { l.onRecentTaskListFrozenChanged(m.arg1 != 0); }; + private final TaskStackConsumer mNotifyRecentTaskRemovedForAddTask = (l, m) -> { + l.onRecentTaskRemovedForAddTask(m.arg1); + }; + private final TaskStackConsumer mNotifyTaskFocusChanged = (l, m) -> { l.onTaskFocusChanged(m.arg1, m.arg2 != 0); }; @@ -261,6 +267,9 @@ class TaskChangeNotificationController { case NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG: forAllRemoteListeners(mNotifyTaskListFrozen, msg); break; + case NOTIFY_RECENT_TASK_REMOVED_FOR_ADD_TASK_LISTENERS_MSG: + forAllRemoteListeners(mNotifyRecentTaskRemovedForAddTask, msg); + break; case NOTIFY_TASK_FOCUS_CHANGED_MSG: forAllRemoteListeners(mNotifyTaskFocusChanged, msg); break; @@ -541,6 +550,15 @@ class TaskChangeNotificationController { msg.sendToTarget(); } + /** Called when a task is removed from the recent tasks list. */ + void notifyRecentTaskRemovedForAddTask(int taskId) { + final Message msg = mHandler.obtainMessage( + NOTIFY_RECENT_TASK_REMOVED_FOR_ADD_TASK_LISTENERS_MSG, taskId, + 0 /* unused */); + forAllLocalListeners(mNotifyRecentTaskRemovedForAddTask, msg); + msg.sendToTarget(); + } + /** @see ITaskStackListener#onTaskFocusChanged(int, boolean) */ void notifyTaskFocusChanged(int taskId, boolean focused) { final Message msg = mHandler.obtainMessage(NOTIFY_TASK_FOCUS_CHANGED_MSG, diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index ce032b4f7f9a..c77b1d9a7bcf 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -46,7 +46,6 @@ import android.view.RemoteAnimationTarget; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; -import android.view.WindowInfo; import android.view.WindowManager.DisplayImePolicy; import android.view.inputmethod.ImeTracker; import android.window.ScreenCapture; @@ -158,26 +157,8 @@ public abstract class WindowManagerInternal { * accessibility changed. */ public interface WindowsForAccessibilityCallback { - - /** - * Called when the windows for accessibility changed. This is called if - * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2} is - * false. - * - * @param forceSend Send the windows for accessibility even if they haven't changed. - * @param topFocusedDisplayId The display Id which has the top focused window. - * @param topFocusedWindowToken The window token of top focused window. - * @param windows The windows for accessibility. - */ - void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId, - IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows); - /** - * Called when the windows for accessibility changed. This is called if - * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2} is - * true. - * TODO(b/322444245): Remove screenSize parameter by getting it from - * DisplayManager#getDisplay(int).getRealSize() on the a11y side. + * Called when the windows for accessibility changed. * * @param forceSend Send the windows for accessibility even if they haven't changed. * @param topFocusedDisplayId The display Id which has the top focused window. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8268cae12e3d..a0c0b9836507 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3030,8 +3030,8 @@ public class WindowManagerService extends IWindowManager.Stub mWindowContextListenerController.unregisterWindowContainerListener(clientToken); - final WindowToken token = wc.asWindowToken(); - if (token != null && token.isFromClient()) { + final WindowToken token = wc != null ? wc.asWindowToken() : null; + if (token != null && token.isFromClient() && token.getDisplayContent() != null) { removeWindowToken(token.token, token.getDisplayContent().getDisplayId()); } } @@ -10314,7 +10314,7 @@ public class WindowManagerService extends IWindowManager.Stub mH.post(() -> { Toast.makeText(mContext, Looper.getMainLooper(), mContext.getString(R.string.screen_not_shared_sensitive_content), - Toast.LENGTH_SHORT) + Toast.LENGTH_LONG) .show(); }); // If blocked due to notification protection (null window token) log protection applied diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index fe2bcc7a74f3..44f5f51eb623 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -1166,6 +1166,10 @@ public class WindowManagerShellCommand extends ShellCommand { case "--cameraCompatAspectRatio": runSetCameraCompatAspectRatio(pw); break; + case "--isCameraCompatFreeformWindowingTreatmentEnabled": + runSetBooleanFlag(pw, mAppCompatConfiguration + ::setIsCameraCompatFreeformWindowingTreatmentEnabled); + break; default: getErrPrintWriter().println( "Error: Unrecognized letterbox style option: " + arg); @@ -1260,6 +1264,10 @@ public class WindowManagerShellCommand extends ShellCommand { case "cameraCompatAspectRatio": mAppCompatConfiguration.resetCameraCompatAspectRatio(); break; + case "isCameraCompatFreeformWindowingTreatmentEnabled": + mAppCompatConfiguration + .resetIsCameraCompatFreeformWindowingTreatmentEnabled(); + break; default: getErrPrintWriter().println( "Error: Unrecognized letterbox style option: " + arg); @@ -1371,6 +1379,7 @@ public class WindowManagerShellCommand extends ShellCommand { mAppCompatConfiguration.resetCameraCompatRefreshEnabled(); mAppCompatConfiguration.resetCameraCompatRefreshCycleThroughStopEnabled(); mAppCompatConfiguration.resetCameraCompatAspectRatio(); + mAppCompatConfiguration.resetIsCameraCompatFreeformWindowingTreatmentEnabled(); } } @@ -1445,6 +1454,10 @@ public class WindowManagerShellCommand extends ShellCommand { + mAppCompatConfiguration.isUserAppAspectRatioSettingsEnabled()); pw.println("Is the fullscreen option in user aspect ratio settings enabled: " + mAppCompatConfiguration.isUserAppAspectRatioFullscreenEnabled()); + pw.println("Default aspect ratio for camera compat freeform: " + + mAppCompatConfiguration.getCameraCompatAspectRatio()); + pw.println("Is camera compatibility freeform treatment enabled for all apps: " + + mAppCompatConfiguration.isCameraCompatFreeformWindowingTreatmentEnabled()); } return 0; } @@ -1701,10 +1714,13 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" happen using the \"stopped -> resumed\" cycle rather than"); pw.println(" \"paused -> resumed\" cycle."); pw.println(" --cameraCompatAspectRatio aspectRatio"); - pw.println(" Aspect ratio of letterbox for fixed-orientation camera apps, during "); + pw.println(" Aspect ratio of letterbox for fixed-orientation camera apps, during"); pw.println(" freeform camera compat mode. If aspectRatio <= " + AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO); pw.println(" it will be ignored."); + pw.println(" --isCameraCompatFreeformWindowingTreatmentEnabled [true|1|false|0]"); + pw.println(" Whether camera compat treatment is enabled in freeform mode for all"); + pw.println(" eligible apps."); pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType"); pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha"); pw.println(" |horizontalPositionMultiplier|verticalPositionMultiplier"); @@ -1714,7 +1730,8 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" |persistentPositionMultiplierForHorizontalReachability"); pw.println(" |persistentPositionMultiplierForVerticalReachability"); pw.println(" |defaultPositionMultiplierForVerticalReachability"); - pw.println(" |cameraCompatAspectRatio]"); + pw.println(" |cameraCompatAspectRatio"); + pw.println(" |isCameraCompatFreeformWindowingTreatmentEnabled]"); pw.println(" Resets overrides to default values for specified properties separated"); pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'."); pw.println(" If no arguments provided, all values will be reset."); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index eaa3a37d5bf3..4c0cee404b68 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -38,7 +38,6 @@ cc_library_static { "com_android_server_adb_AdbDebuggingManager.cpp", "com_android_server_am_BatteryStatsService.cpp", "com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp", - "com_android_server_ConsumerIrService.cpp", "com_android_server_companion_virtual_InputController.cpp", "com_android_server_companion_virtual_VirtualDeviceImpl.cpp", "com_android_server_devicepolicy_CryptoTestHelper.cpp", @@ -63,7 +62,6 @@ cc_library_static { "com_android_server_SystemServer.cpp", "com_android_server_tv_TvUinputBridge.cpp", "com_android_server_tv_TvInputHal.cpp", - "com_android_server_vr_VrManagerService.cpp", "com_android_server_UsbAlsaJackDetector.cpp", "com_android_server_UsbAlsaMidiDevice.cpp", "com_android_server_UsbDeviceManager.cpp", @@ -75,14 +73,13 @@ cc_library_static { "com_android_server_am_LowMemDetector.cpp", "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp", "com_android_server_sensor_SensorService.cpp", - "com_android_server_utils_LazyJniRegistrar.cpp", "com_android_server_wm_TaskFpsCallbackController.cpp", "onload.cpp", ":lib_cachedAppOptimizer_native", ":lib_freezer_native", - ":lib_gameManagerService_native", ":lib_oomConnection_native", ":lib_anrTimer_native", + ":lib_lazilyRegisteredServices_native", ], include_dirs: [ @@ -248,13 +245,6 @@ filegroup { } filegroup { - name: "lib_gameManagerService_native", - srcs: [ - "com_android_server_app_GameManagerService.cpp", - ], -} - -filegroup { name: "lib_oomConnection_native", srcs: ["com_android_server_am_OomConnection.cpp"], } @@ -265,3 +255,13 @@ filegroup { "com_android_server_utils_AnrTimer.cpp", ], } + +filegroup { + name: "lib_lazilyRegisteredServices_native", + srcs: [ + "com_android_server_ConsumerIrService.cpp", + "com_android_server_app_GameManagerService.cpp", + "com_android_server_utils_LazyJniRegistrar.cpp", + "com_android_server_vr_VrManagerService.cpp", + ], +} diff --git a/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp b/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp index ad7781e3b8b5..0c0f8b02279b 100644 --- a/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp +++ b/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp @@ -22,6 +22,7 @@ namespace android { // Forward declared per-class registration methods. int register_android_server_ConsumerIrService(JNIEnv* env); +int register_android_server_app_GameManagerService(JNIEnv* env); int register_android_server_vr_VrManagerService(JNIEnv* env); namespace { @@ -33,12 +34,17 @@ void registerConsumerIrService(JNIEnv* env, jclass) { register_android_server_ConsumerIrService(env); } +void registerGameManagerService(JNIEnv* env, jclass) { + register_android_server_app_GameManagerService(env); +} + void registerVrManagerService(JNIEnv* env, jclass) { register_android_server_vr_VrManagerService(env); } static const JNINativeMethod sJniRegistrarMethods[] = { {"registerConsumerIrService", "()V", (void*)registerConsumerIrService}, + {"registerGameManagerService", "()V", (void*)registerGameManagerService}, {"registerVrManagerService", "()V", (void*)registerVrManagerService}, }; diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index c170ae99da04..df37ec3ef037 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -65,7 +65,6 @@ int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env); int register_android_server_companion_virtual_InputController(JNIEnv* env); int register_android_server_companion_virtual_VirtualDeviceImpl(JNIEnv* env); -int register_android_server_app_GameManagerService(JNIEnv* env); int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env); int register_com_android_server_display_DisplayControl(JNIEnv* env); int register_com_android_server_SystemClockTime(JNIEnv* env); @@ -131,7 +130,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_sensor_SensorService(vm, env); register_android_server_companion_virtual_InputController(env); register_android_server_companion_virtual_VirtualDeviceImpl(env); - register_android_server_app_GameManagerService(env); register_com_android_server_wm_TaskFpsCallbackController(env); register_com_android_server_display_DisplayControl(env); register_com_android_server_SystemClockTime(env); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index dde213de1d40..5bd70ef57fac 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -23708,24 +23708,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } public void setMtePolicy(int flags, String callerPackageName) { - final Set<Integer> allowedModes = - Set.of( - DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY, - DevicePolicyManager.MTE_DISABLED, - DevicePolicyManager.MTE_ENABLED); - Preconditions.checkArgument( - allowedModes.contains(flags), "Provided mode is not one of the allowed values."); - // In general, this API should be available when "bootctl_settings_toggle" is set, which - // signals that there is a control for MTE in the user settings and this API fundamentally - // is a way for the device admin to override that setting. - // Allow bootctl_device_policy_manager as an override, e.g. to offer the - // DevicePolicyManager only without a visible user setting. - if (!mInjector.systemPropertiesGetBoolean( - "ro.arm64.memtag.bootctl_device_policy_manager", - mInjector.systemPropertiesGetBoolean( - "ro.arm64.memtag.bootctl_settings_toggle", false))) { - throw new UnsupportedOperationException("device does not support MTE"); - } + checkMteSupportedAndAllowedPolicy(flags); final CallerIdentity caller = getCallerIdentity(callerPackageName); // For now we continue to restrict the DISABLED setting to device owner - we might need // another permission for this in future. @@ -23783,6 +23766,53 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public void setMtePolicyBySystem( + @NonNull String systemEntity, int policy) { + Objects.requireNonNull(systemEntity); + checkMteSupportedAndAllowedPolicy(policy); + + Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), + "Only system services can call setMtePolicyBySystem"); + + if (!Flags.setMtePolicyCoexistence()) { + throw new UnsupportedOperationException("System can not set MTE policy only"); + } + + EnforcingAdmin admin = EnforcingAdmin.createSystemEnforcingAdmin(systemEntity); + if (policy != DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) { + mDevicePolicyEngine.setGlobalPolicy( + PolicyDefinition.MEMORY_TAGGING, + admin, + new IntegerPolicyValue(policy)); + } else { + mDevicePolicyEngine.removeGlobalPolicy( + PolicyDefinition.MEMORY_TAGGING, + admin); + } + } + + private void checkMteSupportedAndAllowedPolicy(int policy) { + final Set<Integer> allowedModes = + Set.of( + DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY, + DevicePolicyManager.MTE_DISABLED, + DevicePolicyManager.MTE_ENABLED); + Preconditions.checkArgument( + allowedModes.contains(policy), "Provided mode is not one of the allowed values."); + // In general, this API should be available when "bootctl_settings_toggle" is set, which + // signals that there is a control for MTE in the user settings and this API fundamentally + // is a way for the device admin to override that setting. + // Allow bootctl_device_policy_manager as an override, e.g. to offer the + // DevicePolicyManager only without a visible user setting. + if (!mInjector.systemPropertiesGetBoolean( + "ro.arm64.memtag.bootctl_device_policy_manager", + mInjector.systemPropertiesGetBoolean( + "ro.arm64.memtag.bootctl_settings_toggle", false))) { + throw new UnsupportedOperationException("device does not support MTE"); + } + } + + @Override public int getMtePolicy(String callerPackageName) { final CallerIdentity caller = getCallerIdentity(callerPackageName); if (Flags.setMtePolicyCoexistence()) { @@ -24161,6 +24191,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String supervisionBackupId = "36.2.supervision-support"; boolean supervisionMigrated = maybeMigrateResetPasswordTokenLocked(supervisionBackupId); supervisionMigrated |= maybeMigrateSuspendedPackagesLocked(supervisionBackupId); + supervisionMigrated |= maybeMigrateSetKeyguardDisabledFeatures(supervisionBackupId); if (supervisionMigrated) { Slogf.i(LOG_TAG, "Backup made: " + supervisionBackupId); } @@ -24174,6 +24205,38 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Additional migration steps should repeat the pattern above with a new backupId. } + @GuardedBy("getLockObject()") + private boolean maybeMigrateSetKeyguardDisabledFeatures(String backupId) { + Slog.i(LOG_TAG, "Migrating set keyguard disabled features to policy engine"); + if (!Flags.setKeyguardDisabledFeaturesCoexistence()) { + return false; + } + if (mOwners.isSetKeyguardDisabledFeaturesMigrated()) { + return false; + } + // Create backup if none exists + mDevicePolicyEngine.createBackup(backupId); + try { + iterateThroughDpcAdminsLocked((admin, enforcingAdmin) -> { + if (admin.disabledKeyguardFeatures == 0) { + return; + } + int userId = enforcingAdmin.getUserId(); + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.KEYGUARD_DISABLED_FEATURES, + enforcingAdmin, + new IntegerPolicyValue(admin.disabledKeyguardFeatures), + userId); + }); + } catch (Exception e) { + Slog.wtf(LOG_TAG, "Failed to migrate set keyguard disabled to policy engine", e); + } + + Slog.i(LOG_TAG, "Marking set keyguard disabled features migration complete"); + mOwners.markSetKeyguardDisabledFeaturesMigrated(); + return true; + } + private void migratePermissionGrantStatePolicies() { Slogf.i(LOG_TAG, "Migrating PERMISSION_GRANT policy to device policy engine."); for (UserInfo userInfo : mUserManager.getUsers()) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index be4eea42f09e..1c75f2f39c80 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -695,6 +695,19 @@ class Owners { } } + void markSetKeyguardDisabledFeaturesMigrated() { + synchronized (mData) { + mData.mSetKeyguardDisabledFeaturesMigrated = true; + mData.writeDeviceOwner(); + } + } + + boolean isSetKeyguardDisabledFeaturesMigrated() { + synchronized (mData) { + return mData.mSetKeyguardDisabledFeaturesMigrated; + } + } + @GuardedBy("mData") void pushToAppOpsLocked() { if (!mSystemReady) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java index 1cae924a8cd1..caaf0964bb4e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java @@ -95,6 +95,8 @@ class OwnersData { "resetPasswordWithTokenMigrated"; private static final String ATTR_MEMORY_TAGGING_MIGRATED = "memoryTaggingMigrated"; + private static final String ATTR_SET_KEYGUARD_DISABLED_FEATURES_MIGRATED = + "setKeyguardDisabledFeaturesMigrated"; private static final String ATTR_MIGRATED_POST_UPGRADE = "migratedPostUpgrade"; @@ -129,6 +131,7 @@ class OwnersData { boolean mSuspendedPackagesMigrated = false; boolean mResetPasswordWithTokenMigrated = false; boolean mMemoryTaggingMigrated = false; + boolean mSetKeyguardDisabledFeaturesMigrated = false; boolean mPoliciesMigratedPostUpdate = false; @@ -434,6 +437,10 @@ class OwnersData { out.attributeBoolean(null, ATTR_MEMORY_TAGGING_MIGRATED, mMemoryTaggingMigrated); } + if (Flags.setKeyguardDisabledFeaturesCoexistence()) { + out.attributeBoolean(null, ATTR_SET_KEYGUARD_DISABLED_FEATURES_MIGRATED, + mSetKeyguardDisabledFeaturesMigrated); + } out.endTag(null, TAG_POLICY_ENGINE_MIGRATION); } @@ -510,6 +517,10 @@ class OwnersData { mMemoryTaggingMigrated = Flags.setMtePolicyCoexistence() && parser.getAttributeBoolean(null, ATTR_MEMORY_TAGGING_MIGRATED, false); + mSetKeyguardDisabledFeaturesMigrated = + Flags.setKeyguardDisabledFeaturesCoexistence() + && parser.getAttributeBoolean(null, + ATTR_SET_KEYGUARD_DISABLED_FEATURES_MIGRATED, false); break; default: Slog.e(TAG, "Unexpected tag: " + tag); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 82d49fcac305..112414e6c4df 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -422,6 +422,8 @@ public final class SystemServer implements Dumpable { "com.android.server.wifi.aware.WifiAwareService"; private static final String WIFI_P2P_SERVICE_CLASS = "com.android.server.wifi.p2p.WifiP2pService"; + private static final String WIFI_USD_SERVICE_CLASS = + "com.android.server.wifi.usd.UsdService"; private static final String CONNECTIVITY_SERVICE_APEX_PATH = "/apex/com.android.tethering/javalib/service-connectivity.jar"; private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS = @@ -2145,6 +2147,13 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startServiceFromJar( WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); t.traceEnd(); + // Start USD service + if (android.net.wifi.flags.Flags.usd()) { + t.traceBegin("StartUsd"); + mSystemServiceManager.startServiceFromJar( + WIFI_USD_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } } if (context.getPackageManager().hasSystemFeature( diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index e307e529a40b..228e32e98cc7 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -70,6 +70,7 @@ public final class ProfcollectForwardingService extends SystemService { private int mUsageSetting; private boolean mUploadEnabled; + private static boolean sVerityEnforced; private boolean mAdbActive; private IProfCollectd mIProfcollect; @@ -117,6 +118,13 @@ public final class ProfcollectForwardingService extends SystemService { mUsageSetting = -1; } + // Check verity, disable profile upload if not enforced. + final String verityMode = SystemProperties.get("ro.boot.veritymode"); + sVerityEnforced = verityMode.equals("enforcing"); + if (!sVerityEnforced) { + Log.d(LOG_TAG, "verity is not enforced: " + verityMode); + } + mUploadEnabled = context.getResources().getBoolean(R.bool.config_profcollectReportUploaderEnabled); @@ -373,6 +381,10 @@ public final class ProfcollectForwardingService extends SystemService { Log.i(LOG_TAG, "Upload is not enabled."); return; } + if (!sVerityEnforced) { + Log.i(LOG_TAG, "Verity is not enforced."); + return; + } Intent intent = new Intent() .setPackage("com.android.shell") .setAction("com.android.shell.action.PROFCOLLECT_UPLOAD") diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index 759976f79371..47e96d378149 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -17,6 +17,7 @@ package com.android.server.display; import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY; +import static android.Manifest.permission.ADD_MIRROR_DISPLAY; import static android.Manifest.permission.ADD_TRUSTED_DISPLAY; import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT; import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS; @@ -122,6 +123,7 @@ import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserManager; import android.os.test.FakePermissionEnforcer; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; @@ -1388,6 +1390,7 @@ public class DisplayManagerServiceTest { * Tests that it's not allowed to create an auto-mirror virtual display without * CAPTURE_VIDEO_OUTPUT permission or a virtual device that can mirror displays */ + @EnableFlags(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE) @Test public void createAutoMirrorDisplay_withoutPermissionOrAllowedVirtualDevice_throwsException() throws Exception { @@ -1397,7 +1400,8 @@ public class DisplayManagerServiceTest { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); IVirtualDevice virtualDevice = mock(IVirtualDevice.class); when(virtualDevice.getDeviceId()).thenReturn(1); - when(virtualDevice.canCreateMirrorDisplays()).thenReturn(false); + when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY)) + .thenReturn(PackageManager.PERMISSION_DENIED); when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true); when(mContext.checkCallingPermission(CAPTURE_VIDEO_OUTPUT)).thenReturn( PackageManager.PERMISSION_DENIED); @@ -1428,7 +1432,8 @@ public class DisplayManagerServiceTest { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); IVirtualDevice virtualDevice = mock(IVirtualDevice.class); when(virtualDevice.getDeviceId()).thenReturn(1); - when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true); + when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY)) + .thenReturn(PackageManager.PERMISSION_GRANTED); when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true); // Create an auto-mirror virtual display using a virtual device. @@ -1461,7 +1466,8 @@ public class DisplayManagerServiceTest { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); IVirtualDevice virtualDevice = mock(IVirtualDevice.class); when(virtualDevice.getDeviceId()).thenReturn(1); - when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true); + when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY)) + .thenReturn(PackageManager.PERMISSION_GRANTED); when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true); // Create an auto-mirror virtual display using a virtual device. @@ -1528,7 +1534,8 @@ public class DisplayManagerServiceTest { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); IVirtualDevice virtualDevice = mock(IVirtualDevice.class); when(virtualDevice.getDeviceId()).thenReturn(1); - when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true); + when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY)) + .thenReturn(PackageManager.PERMISSION_GRANTED); when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true); when(mContext.checkCallingPermission(ADD_ALWAYS_UNLOCKED_DISPLAY)) .thenReturn(PackageManager.PERMISSION_GRANTED); @@ -1564,7 +1571,8 @@ public class DisplayManagerServiceTest { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); IVirtualDevice virtualDevice = mock(IVirtualDevice.class); when(virtualDevice.getDeviceId()).thenReturn(1); - when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true); + when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY)) + .thenReturn(PackageManager.PERMISSION_GRANTED); when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true); // Create an auto-mirror virtual display using a virtual device. diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java index ff652a2727de..ad30f22fe060 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java @@ -17,6 +17,7 @@ package com.android.server.display; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; +import static android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY_GROUP; import static android.view.Display.FLAG_REAR; @@ -77,6 +78,9 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.test.TestLooper; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.view.Display; import android.view.DisplayAddress; import android.view.DisplayInfo; @@ -144,6 +148,9 @@ public class LogicalDisplayMapperTest { @Rule public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); @Mock LogicalDisplayMapper.Listener mListenerMock; @Mock Context mContextMock; @@ -691,6 +698,7 @@ public class LogicalDisplayMapperTest { } @Test + @RequiresFlagsEnabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION) public void testDeviceShouldNotBeWokenWhenExitingEmulatedState() { assertFalse(mLogicalDisplayMapper.shouldDeviceBeWoken(DEVICE_STATE_OPEN, DEVICE_STATE_EMULATED, diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 993569fb17fe..0d25426700a6 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -392,3 +392,10 @@ test_module_config { ], include_filters: ["com.android.server.StorageManagerServiceTest"], } + +test_module_config { + name: "FrameworksMockingServicesTests_service_batteryServiceTest", + base: "FrameworksMockingServicesTests", + test_suites: ["device-tests"], + include_filters: ["com.android.server.BatteryServiceTest"], +} diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp index 00543a8a9871..94d4b9522d60 100644 --- a/services/tests/mockingservicestests/jni/Android.bp +++ b/services/tests/mockingservicestests/jni/Android.bp @@ -22,8 +22,8 @@ cc_library_shared { srcs: [ ":lib_cachedAppOptimizer_native", ":lib_freezer_native", - ":lib_gameManagerService_native", ":lib_oomConnection_native", + ":lib_lazilyRegisteredServices_native", "onload.cpp", ], @@ -54,6 +54,8 @@ cc_library_shared { "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.common@1.2", "android.hardware.graphics.mapper@4.0", + "android.hardware.ir@1.0", + "android.hardware.vr@1.0", "android.hidl.token@1.0-utils", ], } diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp index cb246d15fce8..9b4c8178b092 100644 --- a/services/tests/mockingservicestests/jni/onload.cpp +++ b/services/tests/mockingservicestests/jni/onload.cpp @@ -26,8 +26,8 @@ namespace android { int register_android_server_am_CachedAppOptimizer(JNIEnv* env); int register_android_server_am_Freezer(JNIEnv* env); -int register_android_server_app_GameManagerService(JNIEnv* env); int register_android_server_am_OomConnection(JNIEnv* env); +int register_android_server_utils_LazyJniRegistrar(JNIEnv* env); }; using namespace android; @@ -44,7 +44,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) ALOG_ASSERT(env, "Could not retrieve the env!"); register_android_server_am_CachedAppOptimizer(env); register_android_server_am_Freezer(env); - register_android_server_app_GameManagerService(env); register_android_server_am_OomConnection(env); + register_android_server_utils_LazyJniRegistrar(env); return JNI_VERSION_1_4; } diff --git a/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java index 5e2f80bf8311..1fbd53a27a4f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/BatteryServiceTest.java @@ -79,6 +79,8 @@ public class BatteryServiceTest { private static final int UPDATED_BATTERY_HEALTH = 3; private static final int CURRENT_CHARGE_COUNTER = 4680000; private static final int UPDATED_CHARGE_COUNTER = 4218000; + private static final int CURRENT_MAX_CHARGING_CURRENT = 298125; + private static final int UPDATED_MAX_CHARGING_CURRENT = 398125; private static final int HANDLER_IDLE_TIME_MS = 5000; @Rule public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this) @@ -143,7 +145,7 @@ public class BatteryServiceTest { @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) public void onlyVoltageUpdated_lessThenOnePercent_broadcastNotSent() { mBatteryService.update(createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, - CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH)); + CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -156,7 +158,8 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(VOLTAGE_MORE_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, CURRENT_CHARGE_COUNTER, - CURRENT_BATTERY_HEALTH)); + CURRENT_BATTERY_HEALTH, + CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -165,13 +168,17 @@ public class BatteryServiceTest { @Test @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) - public void onlyVoltageUpdated_broadcastSent() { + public void voltageUpdated_withUpdateInChargingCurrent_broadcastSent() { mBatteryService.mLastBroadcastVoltageUpdateTime = SystemClock.elapsedRealtime() - 20000; + long lastChargingCurrentUpdateTime = + mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime; mBatteryService.update(createHealthInfo(VOLTAGE_MORE_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, - CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH)); + CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, UPDATED_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); + assertTrue(lastChargingCurrentUpdateTime + < mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime); verifyNumberOfTimesBroadcastSent(1); } @@ -180,7 +187,8 @@ public class BatteryServiceTest { public void onlyTempUpdated_lessThenOneDegreeCelsius_broadcastNotSent() { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, TEMP_LESS_THEN_ONE_DEGREE_CELSIUS, - CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH)); + CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, + CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -191,23 +199,31 @@ public class BatteryServiceTest { @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) public void tempUpdated_broadcastSent() { long lastVoltageUpdateTime = mBatteryService.mLastBroadcastVoltageUpdateTime; + long lastChargingCurrentUpdateTime = + mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime; mBatteryService.update( createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, TEMP_MORE_THEN_ONE_DEGREE_CELSIUS, - UPDATED_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH)); + UPDATED_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, + UPDATED_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); assertTrue(lastVoltageUpdateTime < mBatteryService.mLastBroadcastVoltageUpdateTime); + assertTrue(lastChargingCurrentUpdateTime + < mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime); verifyNumberOfTimesBroadcastSent(1); } @Test @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) - public void batteryHealthUpdated_voltageAndTempConst_broadcastSent() { + public void batteryHealthUpdated_withOtherExtrasConstant_broadcastSent() { + long lastVoltageUpdateTime = mBatteryService.mLastBroadcastVoltageUpdateTime; + long lastChargingCurrentUpdateTime = + mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime; mBatteryService.update( - createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, + createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, CURRENT_CHARGE_COUNTER, - UPDATED_BATTERY_HEALTH)); + UPDATED_BATTERY_HEALTH, UPDATED_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -217,10 +233,13 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, UPDATED_CHARGE_COUNTER, - UPDATED_BATTERY_HEALTH)); + UPDATED_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); + assertTrue(lastVoltageUpdateTime < mBatteryService.mLastBroadcastVoltageUpdateTime); + assertTrue(lastChargingCurrentUpdateTime + < mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime); verifyNumberOfTimesBroadcastSent(1); } @@ -228,7 +247,7 @@ public class BatteryServiceTest { @DisableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) public void voltageUpdated_lessThanOnePercent_flagDisabled_broadcastSent() { mBatteryService.update(createHealthInfo(VOLTAGE_LESS_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, - CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH)); + CURRENT_CHARGE_COUNTER, CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -241,7 +260,7 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, UPDATED_CHARGE_COUNTER, - CURRENT_BATTERY_HEALTH)); + CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -254,7 +273,7 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, TEMP_LESS_THEN_ONE_DEGREE_CELSIUS, UPDATED_CHARGE_COUNTER, - CURRENT_BATTERY_HEALTH)); + CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); @@ -267,10 +286,42 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, UPDATED_CHARGE_COUNTER, - CURRENT_BATTERY_HEALTH)); + CURRENT_BATTERY_HEALTH, CURRENT_MAX_CHARGING_CURRENT)); + + waitForHandlerToExecute(); + + verifyNumberOfTimesBroadcastSent(1); + } + + @Test + @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) + public void onlyMaxChargingCurrentUpdated_beforeFiveSeconds_broadcastNotSent() { + mBatteryService.update( + createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, + CURRENT_CHARGE_COUNTER, + CURRENT_BATTERY_HEALTH, + UPDATED_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); + verifyNumberOfTimesBroadcastSent(0); + } + + @Test + @EnableFlags(Flags.FLAG_RATE_LIMIT_BATTERY_CHANGED_BROADCAST) + public void maxChargingCurrentUpdated_afterFiveSeconds_broadcastSent() { + mBatteryService.mLastBroadcastMaxChargingCurrentUpdateTime = + SystemClock.elapsedRealtime() - 5000; + long lastVoltageUpdateTime = mBatteryService.mLastBroadcastVoltageUpdateTime; + mBatteryService.update( + createHealthInfo(VOLTAGE_MORE_THEN_ONE_PERCENT, CURRENT_BATTERY_TEMP, + CURRENT_CHARGE_COUNTER, + CURRENT_BATTERY_HEALTH, + UPDATED_MAX_CHARGING_CURRENT)); + + waitForHandlerToExecute(); + + assertTrue(lastVoltageUpdateTime < mBatteryService.mLastBroadcastVoltageUpdateTime); verifyNumberOfTimesBroadcastSent(1); } @@ -278,7 +329,8 @@ public class BatteryServiceTest { int batteryVoltage, int batteryTemperature, int batteryChargeCounter, - int batteryHealth) { + int batteryHealth, + int maxChargingCurrent) { HealthInfo h = new HealthInfo(); h.batteryVoltageMillivolts = batteryVoltage; h.batteryTemperatureTenthsCelsius = batteryTemperature; @@ -287,7 +339,7 @@ public class BatteryServiceTest { h.batteryHealth = batteryHealth; h.batteryPresent = true; h.batteryLevel = 100; - h.maxChargingCurrentMicroamps = 298125; + h.maxChargingCurrentMicroamps = maxChargingCurrent; h.batteryCurrentAverageMicroamps = -2812; h.batteryCurrentMicroamps = 298125; h.maxChargingVoltageMicrovolts = 3000; @@ -308,7 +360,8 @@ public class BatteryServiceTest { mBatteryService.update( createHealthInfo(CURRENT_BATTERY_VOLTAGE, CURRENT_BATTERY_TEMP, CURRENT_CHARGE_COUNTER, - CURRENT_BATTERY_HEALTH)); + CURRENT_BATTERY_HEALTH, + CURRENT_MAX_CHARGING_CURRENT)); waitForHandlerToExecute(); } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index a9569b4096ff..1efe4707fc11 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -105,6 +105,7 @@ import android.os.PowerManagerInternal; import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArrayMap; @@ -3260,6 +3261,24 @@ public class MockingOomAdjusterTests { "cch-empty"); } + @SuppressWarnings("GuardedBy") + @Test + @EnableFlags(Flags.FLAG_FIX_APPLY_OOMADJ_ORDER) + public void testUpdateOomAdj_ApplyOomAdjInCorrectOrder() { + final int numberOfApps = 5; + final ProcessRecord[] apps = new ProcessRecord[numberOfApps]; + for (int i = 0; i < numberOfApps; i++) { + apps[i] = spy(makeDefaultProcessRecord(MOCKAPP_PID + i, MOCKAPP_UID + i, + MOCKAPP_PROCESSNAME + i, MOCKAPP_PACKAGENAME + i, true)); + } + updateOomAdj(apps); + for (int i = 1; i < numberOfApps; i++) { + final int pre = mInjector.mSetOomAdjAppliedAt.get(apps[i - 1].mPid); + final int cur = mInjector.mSetOomAdjAppliedAt.get(apps[i].mPid); + assertTrue("setOomAdj is called in wrong order", pre < cur); + } + } + private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName, String packageName, boolean hasShownUi) { return new ProcessRecordBuilder(pid, uid, processName, packageName).setHasShownUi( @@ -3589,9 +3608,16 @@ public class MockingOomAdjusterTests { long mTimeOffsetMillis = 0; private SparseIntArray mLastSetOomAdj = new SparseIntArray(); + // A sequence number that increases every time setOomAdj is called + int mLastAppliedAt = 0; + // Holds the last sequence number setOomAdj is called for a pid + private SparseIntArray mSetOomAdjAppliedAt = new SparseIntArray(); + void reset() { mTimeOffsetMillis = 0; mLastSetOomAdj.clear(); + mLastAppliedAt = 0; + mSetOomAdjAppliedAt.clear(); } void jumpUptimeAheadTo(long uptimeMillis) { @@ -3616,6 +3642,7 @@ public class MockingOomAdjusterTests { final int pid = proc.getPid(); if (pid <= 0) continue; mLastSetOomAdj.put(pid, proc.mState.getCurAdj()); + mSetOomAdjAppliedAt.put(pid, mLastAppliedAt++); } } @@ -3623,6 +3650,7 @@ public class MockingOomAdjusterTests { void setOomAdj(int pid, int uid, int adj) { if (pid <= 0) return; mLastSetOomAdj.put(pid, adj); + mSetOomAdjAppliedAt.put(pid, mLastAppliedAt++); } @Override diff --git a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java index c77ab0f303d9..745424836faa 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java @@ -21,10 +21,14 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static org.mockito.Mockito.verify; + import android.content.ContentResolver; import android.os.SystemProperties; +import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; +import android.util.proto.ProtoOutputStream; import com.android.dx.mockito.inline.extended.ExtendedMockito; @@ -34,6 +38,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Answers; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; @@ -111,6 +116,20 @@ public class SettingsToPropertiesMapperTest { } @Test + public void testClearAconfigStorageOverride() { + SettingsToPropertiesMapper spyMapper = Mockito.spy(new SettingsToPropertiesMapper( + mMockContentResolver, TEST_MAPPING, new String[] {}, new String[] {})); + HashMap flags = new HashMap(); + flags.put("test_package.test_flag", null); + DeviceConfig.Properties props = new DeviceConfig.Properties("test_namespace", flags); + + spyMapper.setLocalOverridesInNewStorage(props); + + verify(spyMapper).writeFlagOverrideRemovalRequest(new ProtoOutputStream(), + "test_package", "test_flag", true); + } + + @Test public void validateRegisteredGlobalSettings() { HashSet<String> hashSet = new HashSet<>(); for (String globalSetting : SettingsToPropertiesMapper.sGlobalSettings) { diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java index 19e43b6fa851..2461e9e79acf 100644 --- a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java @@ -196,6 +196,31 @@ public class BackupAgentConnectionManagerTest { } @Test + public void bindToAgentSynchronous_unexpectedAgentConnected_doesNotReturnWrongAgent() + throws Exception { + when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), + anyInt(), anyBoolean())).thenReturn(true); + // This is so that IBackupAgent.Stub.asInterface() works. + when(mBackupAgentStub.queryLocalInterface(any())).thenReturn(mBackupAgentStub); + when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID); + + // This is going to block until it receives the callback so we need to run it on a + // separate thread. + Thread testThread = new Thread(() -> setBackupAgentResult( + mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo, + ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD)), + "backup-agent-connection-manager-test"); + testThread.start(); + // Give the testThread a head start, otherwise agentConnected() might run before + // bindToAgentSynchronous() is called. + Thread.sleep(500); + mConnectionManager.agentConnected("com.other.package", mBackupAgentStub); + testThread.join(100); // Avoid waiting the full timeout. + + assertThat(mBackupAgentResult).isNull(); + } + + @Test @DisableFlags(Flags.FLAG_ENABLE_RESTRICTED_MODE_CHANGES) public void bindToAgentSynchronous_restrictedModeChangesFlagOff_shouldUseRestrictedMode() throws Exception { @@ -345,6 +370,7 @@ public class BackupAgentConnectionManagerTest { @Test public void agentDisconnected_cancelsCurrentOperations() throws Exception { + when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID); when(mOperationStorage.operationTokensForPackage(eq(TEST_PACKAGE))).thenReturn( ImmutableSet.of(123, 456, 789)); when(mConnectionManager.getThreadForCancellation(any())).thenAnswer(invocation -> { diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index dd7ce21e3628..c831475577d8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -16,6 +16,7 @@ package com.android.server.job; +import static android.app.job.Flags.FLAG_HANDLE_ABANDONED_JOBS; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; @@ -82,6 +83,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.WorkSource; import android.os.WorkSource.WorkChain; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; @@ -1056,6 +1058,75 @@ public class JobSchedulerServiceTest { /** * Confirm that * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} + * returns a job with the correct delay for abandoned jobs. + */ + @Test + @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS) + public void testGetRescheduleJobForFailure_abandonedJob() { + final long nowElapsed = sElapsedRealtimeClock.millis(); + final long initialBackoffMs = MINUTE_IN_MILLIS; + mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO = 3; + + JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure", + createJobInfo() + .setBackoffCriteria(initialBackoffMs, JobInfo.BACKOFF_POLICY_LINEAR)); + assertEquals(JobStatus.NO_EARLIEST_RUNTIME, originalJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, originalJob.getLatestRunTimeElapsed()); + + // failure = 1, systemStop = 0, abandoned = 1 + JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob, + JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); + assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + + // failure = 2, systemstop = 0, abandoned = 2 + rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, + JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); + assertEquals(nowElapsed + (2 * initialBackoffMs), rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + + // failure = 3, systemstop = 0, abandoned = 3 + rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, + JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); + assertEquals(nowElapsed + (3 * initialBackoffMs), rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + + // failure = 4, systemstop = 0, abandoned = 4 + rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, + JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED); + assertEquals( + nowElapsed + ((long) Math.scalb((float) initialBackoffMs, 3)), + rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + + // failure = 4, systemstop = 1, abandoned = 4 + rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, + JobParameters.STOP_REASON_DEVICE_STATE, + JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL); + assertEquals( + nowElapsed + ((long) Math.scalb((float) initialBackoffMs, 3)), + rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + + // failure = 4, systemStop = 4 / SYSTEM_STOP_TO_FAILURE_RATIO, abandoned = 4 + for (int i = 0; i < mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO; ++i) { + rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, + JobParameters.STOP_REASON_SYSTEM_PROCESSING, + JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED); + } + assertEquals( + nowElapsed + ((long) Math.scalb((float) initialBackoffMs, 4)), + rescheduledJob.getEarliestRunTime()); + assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed()); + } + + /** + * Confirm that + * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} * returns a job that is correctly marked as demoted by the user. */ @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java index c6a6865f1cf1..c64973a67589 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java @@ -706,14 +706,14 @@ public class FlexibilityControllerTest { // "True" start is nowElapsed + HOUR_IN_MILLIS nowElapsed + HOUR_IN_MILLIS + adjustmentMs, nowElapsed + 2 * HOUR_IN_MILLIS, - 0 /* numFailures */, 0 /* numSystemStops */, + 0 /* numFailures */, 0 /* numAbandonedFailures */, 0 /* numSystemStops */, JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 0, 0); jsFlex = new JobStatus(jsFlex, // "True" start is nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS nowElapsed + 2 * HOUR_IN_MILLIS - 20 * MINUTE_IN_MILLIS + adjustmentMs, nowElapsed + 2 * HOUR_IN_MILLIS, - 0 /* numFailures */, 0 /* numSystemStops */, + 0 /* numFailures */, 0 /* numAbandonedFailures */, 0 /* numSystemStops */, JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 0, 0); @@ -726,13 +726,13 @@ public class FlexibilityControllerTest { jsBasic = new JobStatus(jsBasic, nowElapsed + 30 * MINUTE_IN_MILLIS, NO_LATEST_RUNTIME, - 1 /* numFailures */, 1 /* numSystemStops */, + 1 /* numFailures */, 0 /* numAbandonedFailures */, 1 /* numSystemStops */, JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 0, 0); jsFlex = new JobStatus(jsFlex, nowElapsed + 30 * MINUTE_IN_MILLIS, NO_LATEST_RUNTIME, - 1 /* numFailures */, 1 /* numSystemStops */, + 1 /* numFailures */, 0 /* numAbandonedFailures */, 1 /* numSystemStops */, JobSchedulerService.sSystemClock.millis() /* lastSuccessfulRunTime */, 0, 0); @@ -847,21 +847,24 @@ public class FlexibilityControllerTest { JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS); JobStatus js = createJobStatus("time", jb); js = new JobStatus( - js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, /* numSystemStops */ 0, + js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, + 0 /* numAbandonedFailures */, /* numSystemStops */ 0, 0, FROZEN_TIME, FROZEN_TIME); assertEquals(mFcConfig.RESCHEDULED_JOB_DEADLINE_MS, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); js = new JobStatus( - js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, /* numSystemStops */ 1, + js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 2, + 0 /* numAbandonedFailures */, /* numSystemStops */ 1, 0, FROZEN_TIME, FROZEN_TIME); assertEquals(2 * mFcConfig.RESCHEDULED_JOB_DEADLINE_MS, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); js = new JobStatus( - js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, /* numSystemStops */ 10, + js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, + 0 /* numAbandonedFailures */, /* numSystemStops */ 10, 0, FROZEN_TIME, FROZEN_TIME); assertEquals(mFcConfig.MAX_RESCHEDULED_DEADLINE_MS, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); @@ -1092,11 +1095,13 @@ public class FlexibilityControllerTest { JobInfo.Builder jb = createJob(0); JobStatus js = createJobStatus("time", jb); js = new JobStatus( - js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, /* numSystemStops */ 0, + js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 1, + /* numAbandonedFailures */ 0, /* numSystemStops */ 0, 0, FROZEN_TIME, FROZEN_TIME); assertFalse(js.hasFlexibilityConstraint()); js = new JobStatus( - js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, /* numSystemStops */ 1, + js, FROZEN_TIME, NO_LATEST_RUNTIME, /* numFailures */ 0, + /* numAbandonedFailures */ 0, /* numSystemStops */ 1, 0, FROZEN_TIME, FROZEN_TIME); assertFalse(js.hasFlexibilityConstraint()); } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java index 2d0f4b69e2fe..86101cf591e7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java @@ -459,35 +459,35 @@ public class JobStatusTest { int numFailures = 1; int numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); // 2+ failures, priority should be lowered as much as possible. numFailures = 2; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); numFailures = 5; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); numFailures = 8; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); // System stops shouldn't factor in the downgrade. numSystemStops = 10; numFailures = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); // Less than 2 failures, but job is downgraded. numFailures = 1; numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); } @@ -505,44 +505,44 @@ public class JobStatusTest { int numFailures = 1; int numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); // Failures in [2,4), priority should be lowered slightly. numFailures = 2; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority()); numFailures = 3; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority()); // Failures in [4,6), priority should be lowered more. numFailures = 4; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); numFailures = 5; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); // 6+ failures, priority should be lowered as much as possible. numFailures = 6; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority()); numFailures = 12; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority()); // System stops shouldn't factor in the downgrade. numSystemStops = 10; numFailures = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); } @@ -563,32 +563,32 @@ public class JobStatusTest { int numFailures = 1; int numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); numFailures = 4; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); numFailures = 5; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); // 6+ failures, priority should be lowered as much as possible. numFailures = 6; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority()); numFailures = 12; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority()); // System stops shouldn't factor in the downgrade. numSystemStops = 10; numFailures = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); } @@ -606,28 +606,28 @@ public class JobStatusTest { int numFailures = 1; int numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); // 2+ failures, priority shouldn't be affected while job is still a UI job numFailures = 2; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); numFailures = 5; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); numFailures = 8; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); // System stops shouldn't factor in the downgrade. numSystemStops = 10; numFailures = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MAX, job.getEffectivePriority()); // Job can no long run as user-initiated. Downgrades should be effective. @@ -641,28 +641,28 @@ public class JobStatusTest { numFailures = 1; numSystemStops = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); // 2+ failures, priority should start getting lower numFailures = 2; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_DEFAULT, job.getEffectivePriority()); numFailures = 5; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_LOW, job.getEffectivePriority()); numFailures = 8; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_MIN, job.getEffectivePriority()); // System stops shouldn't factor in the downgrade. numSystemStops = 10; numFailures = 0; job = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, numFailures, - numSystemStops, 0, 0, 0); + 0, numSystemStops, 0, 0, 0); assertEquals(JobInfo.PRIORITY_HIGH, job.getEffectivePriority()); } @@ -772,14 +772,14 @@ public class JobStatusTest { assertTrue(job.shouldTreatAsUserInitiatedJob()); JobStatus rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, - 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0); assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); assertFalse(job.shouldTreatAsUserInitiatedJob()); rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, - 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0); assertFalse(rescheduledJob.shouldTreatAsUserInitiatedJob()); rescheduledJob.removeInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); @@ -797,14 +797,14 @@ public class JobStatusTest { assertTrue(job.shouldTreatAsUserInitiatedJob()); JobStatus rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, - 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0); assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ); assertFalse(job.shouldTreatAsUserInitiatedJob()); rescheduledJob = new JobStatus(job, NO_EARLIEST_RUNTIME, NO_LATEST_RUNTIME, - 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0); assertFalse(rescheduledJob.shouldTreatAsUserInitiatedJob()); rescheduledJob.removeInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java index 769f071e3ddc..405024cc0e34 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java @@ -134,7 +134,7 @@ public class ApexManagerTest { mMockSystem.system().validateFinalState(); mInstallPackageHelper = new InstallPackageHelper(mPmService, mock(AppDataHelper.class), mock(RemovePackageHelper.class), mock(DeletePackageHelper.class), - mock(BroadcastHelper.class)); + mock(BroadcastHelper.class), mock(InstallDependencyHelper.class)); } @NonNull diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java index 20ac0781e2ed..0304a74f7654 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java @@ -79,12 +79,14 @@ public class InstallDependencyHelperTest { @Mock private SharedLibrariesImpl mSharedLibraries; @Mock private Context mContext; @Mock private Computer mComputer; + @Mock private PackageInstallerService mPackageInstallerService; private InstallDependencyHelper mInstallDependencyHelper; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mInstallDependencyHelper = new InstallDependencyHelper(mContext, mSharedLibraries); + mInstallDependencyHelper = new InstallDependencyHelper(mContext, mSharedLibraries, + mPackageInstallerService); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 0a6edf1b9831..b53dbc834351 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -218,6 +218,8 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { val handler = TestHandler(null) val defaultAppProvider: DefaultAppProvider = mock() val backgroundHandler = TestHandler(null) + val packageInstallerService: PackageInstallerService = mock() + val installDependencyHelper: InstallDependencyHelper = mock() val updateOwnershipHelper: UpdateOwnershipHelper = mock() } @@ -306,6 +308,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { whenever(mocks.injector.handler) { mocks.handler } whenever(mocks.injector.defaultAppProvider) { mocks.defaultAppProvider } whenever(mocks.injector.backgroundHandler) { mocks.backgroundHandler } + whenever(mocks.injector.packageInstallerService) { mocks.packageInstallerService } whenever(mocks.injector.updateOwnershipHelper) { mocks.updateOwnershipHelper } whenever(mocks.injector.getSystemService(AppOpsManager::class.java)) { mocks.appOpsManager } wheneverStatic { SystemConfig.getInstance() }.thenReturn(mocks.systemConfig) @@ -332,6 +335,8 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { DEVICE_PROVISIONING_PACKAGE_NAME } whenever(mocks.apexManager.activeApexInfos).thenReturn(DEFAULT_ACTIVE_APEX_INFO_LIST) + whenever(mocks.packageInstallerService.installDependencyHelper).thenReturn( + mocks.installDependencyHelper) whenever(mocks.settings.packagesLocked).thenReturn(mSettingsMap) whenever(mocks.settings.internalVersion).thenReturn(DEFAULT_VERSION_INFO) whenever(mocks.settings.keySetManagerService).thenReturn(mocks.keySetManagerService) diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java index e631cb66eaf7..313c01d5ce58 100644 --- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java +++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java @@ -18,7 +18,6 @@ package com.android.server.power.hint; import static com.android.server.power.hint.HintManagerService.CLEAN_UP_UID_DELAY_MILLIS; -import static com.android.server.power.hint.HintManagerService.DEFAULT_HEADROOM_PID; import static com.google.common.truth.Truth.assertThat; @@ -53,7 +52,9 @@ import android.hardware.common.fmq.MQDescriptor; import android.hardware.power.ChannelConfig; import android.hardware.power.ChannelMessage; import android.hardware.power.CpuHeadroomParams; +import android.hardware.power.CpuHeadroomResult; import android.hardware.power.GpuHeadroomParams; +import android.hardware.power.GpuHeadroomResult; import android.hardware.power.IPower; import android.hardware.power.SessionConfig; import android.hardware.power.SessionTag; @@ -1251,67 +1252,98 @@ public class HintManagerServiceTest { CpuHeadroomParams halParams1 = new CpuHeadroomParams(); halParams1.calculationType = CpuHeadroomParams.CalculationType.MIN; halParams1.selectionType = CpuHeadroomParams.SelectionType.ALL; - halParams1.pid = Process.myPid(); + halParams1.tids = new int[]{Process.myPid()}; CpuHeadroomParamsInternal params2 = new CpuHeadroomParamsInternal(); params2.usesDeviceHeadroom = true; - params2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE; + params2.calculationType = CpuHeadroomParams.CalculationType.MIN; params2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE; CpuHeadroomParams halParams2 = new CpuHeadroomParams(); - halParams2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE; + halParams2.calculationType = CpuHeadroomParams.CalculationType.MIN; halParams2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE; - halParams2.pid = DEFAULT_HEADROOM_PID; + halParams2.tids = new int[]{}; + + CpuHeadroomParamsInternal params3 = new CpuHeadroomParamsInternal(); + params3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE; + params3.selectionType = CpuHeadroomParams.SelectionType.ALL; + CpuHeadroomParams halParams3 = new CpuHeadroomParams(); + halParams3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE; + halParams3.selectionType = CpuHeadroomParams.SelectionType.ALL; + halParams3.tids = new int[]{Process.myPid()}; + + // this params should not be cached as the window is not default + CpuHeadroomParamsInternal params4 = new CpuHeadroomParamsInternal(); + params4.calculationWindowMillis = 123; + CpuHeadroomParams halParams4 = new CpuHeadroomParams(); + halParams4.calculationType = CpuHeadroomParams.CalculationType.MIN; + halParams4.selectionType = CpuHeadroomParams.SelectionType.ALL; + halParams4.calculationWindowMillis = 123; + halParams4.tids = new int[]{Process.myPid()}; - float[] headroom1 = new float[] {0.1f}; - when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(headroom1); - float[] headroom2 = new float[] {0.1f, 0.5f}; - when(mIPowerMock.getCpuHeadroom(eq(halParams2))).thenReturn(headroom2); + float headroom1 = 0.1f; + CpuHeadroomResult halRet1 = CpuHeadroomResult.globalHeadroom(headroom1); + when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(halRet1); + float[] headroom2 = new float[] {0.2f, 0.2f}; + CpuHeadroomResult halRet2 = CpuHeadroomResult.perCoreHeadroom(headroom2); + when(mIPowerMock.getCpuHeadroom(eq(halParams2))).thenReturn(halRet2); + float headroom3 = 0.3f; + CpuHeadroomResult halRet3 = CpuHeadroomResult.globalHeadroom(headroom3); + when(mIPowerMock.getCpuHeadroom(eq(halParams3))).thenReturn(halRet3); + float headroom4 = 0.4f; + CpuHeadroomResult halRet4 = CpuHeadroomResult.globalHeadroom(headroom4); + when(mIPowerMock.getCpuHeadroom(eq(halParams4))).thenReturn(halRet4); HintManagerService service = createService(); clearInvocations(mIPowerMock); service.getBinderServiceInstance().getCpuHeadroomMinIntervalMillis(); verify(mIPowerMock, times(0)).getCpuHeadroomMinIntervalMillis(); - service.getBinderServiceInstance().getCpuHeadroom(params1); + assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1)); - service.getBinderServiceInstance().getCpuHeadroom(params2); + assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2)); + assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams3)); + assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); // verify cache is working clearInvocations(mIPowerMock); - assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1), - 0.01f); - assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getCpuHeadroom(any()); + assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); + assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3)); + assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4)); + verify(mIPowerMock, times(1)).getCpuHeadroom(any()); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1)); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams2)); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); // after 1 more second it should be served with cache still Thread.sleep(1000); clearInvocations(mIPowerMock); - assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1), - 0.01f); - assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getCpuHeadroom(any()); - - // after 1.5 more second it should be served with cache still as timer reset - Thread.sleep(1500); - clearInvocations(mIPowerMock); - assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1), - 0.01f); - assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getCpuHeadroom(any()); + assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); + assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3)); + assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4)); + verify(mIPowerMock, times(1)).getCpuHeadroom(any()); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1)); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams2)); + verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval - Thread.sleep(2100); + Thread.sleep(1100); clearInvocations(mIPowerMock); - assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1), - 0.01f); - assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2), - 0.01f); + assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); + assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3)); + assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4)); + verify(mIPowerMock, times(4)).getCpuHeadroom(any()); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams3)); + verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); } @Test @@ -1322,59 +1354,52 @@ public class HintManagerServiceTest { halParams1.calculationType = GpuHeadroomParams.CalculationType.MIN; GpuHeadroomParamsInternal params2 = new GpuHeadroomParamsInternal(); + params2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE; + params2.calculationWindowMillis = 123; GpuHeadroomParams halParams2 = new GpuHeadroomParams(); - params2.calculationType = - halParams2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE; + halParams2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE; + halParams2.calculationWindowMillis = 123; float headroom1 = 0.1f; - when(mIPowerMock.getGpuHeadroom(eq(halParams1))).thenReturn(headroom1); + GpuHeadroomResult halRet1 = GpuHeadroomResult.globalHeadroom(headroom1); + when(mIPowerMock.getGpuHeadroom(eq(halParams1))).thenReturn(halRet1); float headroom2 = 0.2f; - when(mIPowerMock.getGpuHeadroom(eq(halParams2))).thenReturn(headroom2); + GpuHeadroomResult halRet2 = GpuHeadroomResult.globalHeadroom(headroom2); + when(mIPowerMock.getGpuHeadroom(eq(halParams2))).thenReturn(halRet2); HintManagerService service = createService(); clearInvocations(mIPowerMock); service.getBinderServiceInstance().getGpuHeadroomMinIntervalMillis(); verify(mIPowerMock, times(0)).getGpuHeadroomMinIntervalMillis(); - assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1), - 0.01f); - assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2), - 0.01f); + assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); + verify(mIPowerMock, times(2)).getGpuHeadroom(any()); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1)); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); // verify cache is working clearInvocations(mIPowerMock); - assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1), - 0.01f); - assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getGpuHeadroom(any()); + assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); + verify(mIPowerMock, times(1)).getGpuHeadroom(any()); + verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1)); + verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); // after 1 more second it should be served with cache still Thread.sleep(1000); clearInvocations(mIPowerMock); - assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1), - 0.01f); - assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getGpuHeadroom(any()); - - // after 1.5 more second it should be served with cache still as timer reset - Thread.sleep(1500); - clearInvocations(mIPowerMock); - assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1), - 0.01f); - assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2), - 0.01f); - verify(mIPowerMock, times(0)).getGpuHeadroom(any()); + assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); + verify(mIPowerMock, times(1)).getGpuHeadroom(any()); + verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1)); + verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval - Thread.sleep(2100); + Thread.sleep(1100); clearInvocations(mIPowerMock); - assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1), - 0.01f); - assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2), - 0.01f); + assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); + assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); + verify(mIPowerMock, times(2)).getGpuHeadroom(any()); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1)); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java index c0be8652f303..4b91d8418fc3 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java @@ -79,8 +79,6 @@ public class AmbientDisplayPowerCalculatorTest { // 100,000,00 uC / 1000 (micro-/milli-) / 360 (seconds/hour) = 27.777778 mAh assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) .isWithin(PRECISION).of(27.777778); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test @@ -140,8 +138,6 @@ public class AmbientDisplayPowerCalculatorTest { // (seconds/hour) = 27.777778 mAh assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) .isWithin(PRECISION).of(83.33333); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test @@ -163,8 +159,6 @@ public class AmbientDisplayPowerCalculatorTest { .isEqualTo(90 * MINUTE_IN_MS); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) .isWithin(PRECISION).of(15.0); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -195,7 +189,5 @@ public class AmbientDisplayPowerCalculatorTest { .isEqualTo(120 * MINUTE_IN_MS); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) .isWithin(PRECISION).of(35.0); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java index 5d50e6c9f715..9da89fcf2e84 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java @@ -311,10 +311,6 @@ public class BatteryUsageStatsAtomTest { bus.getAggregateBatteryConsumer(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE), proto.deviceBatteryConsumer); - for (int i = 0; i < BatteryConsumer.POWER_COMPONENT_COUNT; i++) { - assertPowerComponentModel(i, abc.getPowerModel(i), proto); - } - // Now for the UidBatteryConsumers. final List<android.os.UidBatteryConsumer> uidConsumers = bus.getUidBatteryConsumers(); uidConsumers.sort((a, b) -> a.getUid() - b.getUid()); @@ -450,34 +446,6 @@ public class BatteryUsageStatsAtomTest { } } - /** - * Validates the PowerComponentModel object that matches powerComponent. - */ - private void assertPowerComponentModel(int powerComponent, - @BatteryConsumer.PowerModel int powerModel, BatteryUsageStatsAtomsProto proto) { - boolean found = false; - for (BatteryUsageStatsAtomsProto.PowerComponentModel powerComponentModel : - proto.componentModels) { - if (powerComponentModel.component == powerComponent) { - if (found) { - fail("Power component " + BatteryConsumer.powerComponentIdToString( - powerComponent) + " found multiple times in the proto"); - } - found = true; - final int expectedPowerModel = BatteryConsumer.powerModelToProtoEnum(powerModel); - assertEquals(expectedPowerModel, powerComponentModel.powerModel); - } - } - if (!found) { - final int model = BatteryConsumer.powerModelToProtoEnum(powerModel); - assertEquals( - "Power component " + BatteryConsumer.powerComponentIdToString(powerComponent) - + " was not found in the proto but has a defined power model.", - BatteryUsageStatsAtomsProto.PowerComponentModel.UNDEFINED, - model); - } - } - /** Converts charge from milliamp hours (mAh) to decicoulombs (dC). */ private long convertMahToDc(double powerMah) { return (long) (powerMah * 36 + 0.5); @@ -486,7 +454,6 @@ public class BatteryUsageStatsAtomTest { private BatteryUsageStats buildBatteryUsageStats() { final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[]{"CustomConsumer1", "CustomConsumer2"}, - /* includePowerModels */ true, /* includeProcessStats */ true, /* includeScreenStateData */ false, /* includePowerStateData */ false, @@ -524,13 +491,13 @@ public class BatteryUsageStatsAtomTest { final BatteryConsumer.Key keyCached = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU, BatteryConsumer.PROCESS_STATE_CACHED); - uidBuilder.addConsumedPower(keyFg, 9100, BatteryConsumer.POWER_MODEL_POWER_PROFILE) + uidBuilder.addConsumedPower(keyFg, 9100) .addUsageDurationMillis(keyFg, 8100) - .addConsumedPower(keyBg, 9200, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) + .addConsumedPower(keyBg, (double) 9200) .addUsageDurationMillis(keyBg, 8200) - .addConsumedPower(keyFgs, 9300, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) + .addConsumedPower(keyFgs, (double) 9300) .addUsageDurationMillis(keyFgs, 8300) - .addConsumedPower(keyCached, 9400, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) + .addConsumedPower(keyCached, (double) 9400) .addUsageDurationMillis(keyCached, 8400); final BatteryConsumer.Key keyCustomFg = uidBuilder.getKey( @@ -539,10 +506,8 @@ public class BatteryUsageStatsAtomTest { final BatteryConsumer.Key keyCustomBg = uidBuilder.getKey( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, BatteryConsumer.PROCESS_STATE_BACKGROUND); - uidBuilder.addConsumedPower( - keyCustomFg, 100, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); - uidBuilder.addConsumedPower( - keyCustomBg, 350, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + uidBuilder.addConsumedPower(keyCustomFg, 100); + uidBuilder.addConsumedPower(keyCustomBg, 350); builder.getOrCreateUidBatteryConsumerBuilder(UID_1) .setPackageWithHighestDrain("myPackage1") @@ -557,14 +522,11 @@ public class BatteryUsageStatsAtomTest { builder.getAggregateBatteryConsumerBuilder(AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE) .addConsumedPower(30000) .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU, 20100, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + BatteryConsumer.POWER_COMPONENT_CPU, 20100) .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_AUDIO, 0, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) // Empty + BatteryConsumer.POWER_COMPONENT_AUDIO, 0) // Empty .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_CAMERA, 20150, - BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) + BatteryConsumer.POWER_COMPONENT_CAMERA, 20150) .addConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200) .addUsageDurationMillis( @@ -576,8 +538,7 @@ public class BatteryUsageStatsAtomTest { builder.getAggregateBatteryConsumerBuilder( BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS) .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU, 10100, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + BatteryConsumer.POWER_COMPONENT_CPU, 10100) .addConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200); @@ -587,7 +548,7 @@ public class BatteryUsageStatsAtomTest { @Test public void testLargeAtomTruncated() throws Exception { final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[0], true, false, false, false, 0); + new BatteryUsageStats.Builder(new String[0], false, false, false, 0); // If not truncated, this BatteryUsageStats object would generate a proto buffer // significantly larger than 50 Kb for (int i = 0; i < 3000; i++) { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java index 383616eb59eb..a3c7ece386c7 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java @@ -423,8 +423,6 @@ public class BatteryUsageStatsRule implements TestRule { mBatteryUsageStats = null; } final String[] customPowerComponentNames = mBatteryStats.getCustomEnergyConsumerNames(); - final boolean includePowerModels = (query.getFlags() - & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; final boolean includeProcessStateData = (query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0; final boolean includeScreenStateData = (query.getFlags() @@ -433,7 +431,7 @@ public class BatteryUsageStatsRule implements TestRule { & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_STATE) != 0; final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold(); BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - customPowerComponentNames, includePowerModels, includeProcessStateData, + customPowerComponentNames, includeProcessStateData, includeScreenStateData, includePowerStateData, minConsumedPowerThreshold); SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats(); for (int i = 0; i < uidStats.size(); i++) { diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java index 9771da590e37..dd50431b598e 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java @@ -17,8 +17,6 @@ package com.android.server.power.stats; import static android.os.BatteryConsumer.POWER_COMPONENT_ANY; -import static android.os.BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION; -import static android.os.BatteryConsumer.POWER_MODEL_UNDEFINED; import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND; import static android.os.BatteryConsumer.PROCESS_STATE_CACHED; import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND; @@ -237,7 +235,7 @@ public class BatteryUsageStatsTest { final BatteryUsageStats stats1 = buildBatteryUsageStats1(false).build(); final BatteryUsageStats stats2 = buildBatteryUsageStats2(new String[]{"FOO"}, true).build(); final BatteryUsageStats sum = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, true, 0) + new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, 0) .add(stats1) .add(stats2) .build(); @@ -248,15 +246,13 @@ public class BatteryUsageStatsTest { for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) { if (uidBatteryConsumer.getUid() == APP_UID1) { assertUidBatteryConsumer(uidBatteryConsumer, 1200 + 924, null, - 5321, 6900, 532, 423, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400 + 345, - POWER_MODEL_UNDEFINED, + 5321, 6900, 532, 423, 400 + 345, 500 + 456, 1167, 1478, true, 3554, 4732, 3998, 444, 3554, 15542, 3776, 17762, 3998, 19982, 444, 1110); } else if (uidBatteryConsumer.getUid() == APP_UID2) { assertUidBatteryConsumer(uidBatteryConsumer, 1332, "bar", - 1111, 2220, 2, 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444, - BatteryConsumer.POWER_MODEL_POWER_PROFILE, + 1111, 2220, 2, 333, 444, 555, 666, 777, true, 1777, 2443, 1999, 321, 1777, 7771, 1888, 8881, 1999, 9991, 321, 654); @@ -280,7 +276,7 @@ public class BatteryUsageStatsTest { @Test public void testAdd_customComponentMismatch() throws Exception { final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, true, 0); + new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, 0); final BatteryUsageStats stats = buildBatteryUsageStats2(new String[]{"BAR"}, false).build(); assertThrows(IllegalArgumentException.class, () -> builder.add(stats)); @@ -291,7 +287,7 @@ public class BatteryUsageStatsTest { @Test public void testAdd_processStateDataMismatch() throws Exception { final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, true, 0); + new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, true, 0); final BatteryUsageStats stats = buildBatteryUsageStats2(new String[]{"FOO"}, false).build(); assertThrows(IllegalArgumentException.class, () -> builder.add(stats)); @@ -328,7 +324,7 @@ public class BatteryUsageStatsTest { final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true, true, + new BatteryUsageStats.Builder(new String[]{"FOO"}, true, includeScreenState, includePowerState, 0) .setBatteryCapacity(4000) .setDischargePercentage(20) @@ -339,8 +335,8 @@ public class BatteryUsageStatsTest { addUidBatteryConsumer(builder, batteryStats, APP_UID1, "foo", 1000, 1500, 500, - 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400, - BatteryConsumer.POWER_MODEL_POWER_PROFILE, 500, 600, 800, + 300, 400, + 500, 600, 800, 1777, 7771, 1888, 8881, 1999, 9991, 123, 456); addAggregateBatteryConsumer(builder, @@ -373,7 +369,7 @@ public class BatteryUsageStatsTest { final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(customPowerComponentNames, true, + new BatteryUsageStats.Builder(customPowerComponentNames, includeProcessStateData, true, true, 0); builder.setDischargePercentage(30) .setDischargedPowerRange(1234, 2345) @@ -382,14 +378,14 @@ public class BatteryUsageStatsTest { addUidBatteryConsumer(builder, batteryStats, APP_UID1, null, 4321, 5400, 32, - 123, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_ENERGY_CONSUMPTION, + 123, 345, 456, 567, 678, 1777, 7771, 1888, 8881, 1999, 9991, 321, 654); addUidBatteryConsumer(builder, batteryStats, APP_UID2, "bar", 1111, 2220, 2, - 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444, - BatteryConsumer.POWER_MODEL_POWER_PROFILE, 555, 666, 777, + 333, 444, + 555, 666, 777, 1777, 7771, 1888, 8881, 1999, 9991, 321, 654); addAggregateBatteryConsumer(builder, @@ -409,7 +405,7 @@ public class BatteryUsageStatsTest { MockBatteryStatsImpl batteryStats, int uid, String packageWithHighestDrain, int timeInProcessStateForeground, int timeInProcessStateBackground, int timeInProcessStateForegroundService, double screenPower, - int screenPowerModel, double cpuPower, int cpuPowerModel, double customComponentPower, + double cpuPower, double customComponentPower, int cpuDuration, int customComponentDuration, double cpuPowerForeground, int cpuDurationForeground, double cpuPowerBackground, int cpuDurationBackground, double cpuPowerFgs, int cpuDurationFgs, double cpuPowerCached, long cpuDurationCached) { @@ -423,9 +419,9 @@ public class BatteryUsageStatsTest { .setTimeInProcessStateMs(PROCESS_STATE_FOREGROUND_SERVICE, timeInProcessStateForegroundService) .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel) + BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower) .addConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU, cpuPower, cpuPowerModel) + BatteryConsumer.POWER_COMPONENT_CPU, cpuPower) .addConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower) .addUsageDurationMillis( @@ -460,21 +456,15 @@ public class BatteryUsageStatsTest { : uidBuilder.getKey( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, BatteryConsumer.PROCESS_STATE_BACKGROUND); - uidBuilder - .addConsumedPower(cpuFgKey, cpuPowerForeground, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + uidBuilder.addConsumedPower(cpuFgKey, cpuPowerForeground) .addUsageDurationMillis(cpuFgKey, cpuDurationForeground) - .addConsumedPower(cpuBgKey, cpuPowerBackground, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cpuBgKey, cpuPowerBackground) .addUsageDurationMillis(cpuBgKey, cpuDurationBackground) - .addConsumedPower(cpuFgsKey, cpuPowerFgs, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cpuFgsKey, cpuPowerFgs) .addUsageDurationMillis(cpuFgsKey, cpuDurationFgs) - .addConsumedPower(cachedKey, cpuPowerCached, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cachedKey, cpuPowerCached) .addUsageDurationMillis(cachedKey, cpuDurationCached) - .addConsumedPower(customBgKey, customComponentPower, - BatteryConsumer.POWER_MODEL_UNDEFINED) + .addConsumedPower(customBgKey, customComponentPower) .addUsageDurationMillis(customBgKey, customComponentDuration); } } @@ -518,18 +508,13 @@ public class BatteryUsageStatsTest { BatteryConsumer.PROCESS_STATE_UNSPECIFIED, BatteryConsumer.SCREEN_STATE_OTHER, BatteryConsumer.POWER_STATE_OTHER); - aggBuilder - .addConsumedPower(cpuBatScrOn, cpuPowerBatScrOn, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + aggBuilder.addConsumedPower(cpuBatScrOn, cpuPowerBatScrOn) .addUsageDurationMillis(cpuBatScrOn, cpuDurationBatScrOn) - .addConsumedPower(cpuBatScrOff, cpuPowerBatScrOff, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cpuBatScrOff, cpuPowerBatScrOff) .addUsageDurationMillis(cpuBatScrOff, cpuDurationBatScrOff) - .addConsumedPower(cpuChgScrOn, cpuPowerChgScrOn, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cpuChgScrOn, cpuPowerChgScrOn) .addUsageDurationMillis(cpuChgScrOn, cpuDurationChgScrOn) - .addConsumedPower(cpuChgScrOff, cpuPowerChgScrOff, - BatteryConsumer.POWER_MODEL_POWER_PROFILE) + .addConsumedPower(cpuChgScrOff, cpuPowerChgScrOff) .addUsageDurationMillis(cpuChgScrOff, cpuDurationChgScrOff); } } @@ -544,8 +529,7 @@ public class BatteryUsageStatsTest { for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) { if (uidBatteryConsumer.getUid() == APP_UID1) { assertUidBatteryConsumer(uidBatteryConsumer, 1200, "foo", - 1000, 1500, 500, 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400, - BatteryConsumer.POWER_MODEL_POWER_PROFILE, + 1000, 1500, 500, 300, 400, 500, 600, 800, true, 1777, 2388, 1999, 123, 1777, 7771, 1888, 8881, 1999, 9991, 123, 456); } else { @@ -596,8 +580,8 @@ public class BatteryUsageStatsTest { private void assertUidBatteryConsumer(UidBatteryConsumer uidBatteryConsumer, double consumedPower, String packageWithHighestDrain, int timeInProcessStateForeground, int timeInProcessStateBackground, int timeInProcessStateForegroundService, - int screenPower, int screenPowerModel, double cpuPower, - int cpuPowerModel, double customComponentPower, int cpuDuration, + int screenPower, double cpuPower, + double customComponentPower, int cpuDuration, int customComponentDuration, boolean processStateDataIncluded, double totalPowerForeground, double totalPowerBackground, double totalPowerFgs, double totalPowerCached, double cpuPowerForeground, int cpuDurationForeground, @@ -620,12 +604,8 @@ public class BatteryUsageStatsTest { PROCESS_STATE_FOREGROUND_SERVICE)).isEqualTo(timeInProcessStateForegroundService); assertThat(uidBatteryConsumer.getConsumedPower( BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPower); - assertThat(uidBatteryConsumer.getPowerModel( - BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPowerModel); assertThat(uidBatteryConsumer.getConsumedPower( BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower); - assertThat(uidBatteryConsumer.getPowerModel( - BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPowerModel); assertThat(uidBatteryConsumer.getConsumedPower( BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower); assertThat(uidBatteryConsumer.getUsageDurationMillis( diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java index fe6424f91d83..c9cb0dfa98e4 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java @@ -82,16 +82,16 @@ public class BluetoothPowerCalculatorTest { assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.06944, 3000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.06944, 3000); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(APP_UID), - 0.19444, 9000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.19444, 9000); assertBluetoothPowerAndDuration( mStatsRule.getDeviceBatteryConsumer(), - 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.26388, 12000); assertBluetoothPowerAndDuration( mStatsRule.getAppsBatteryConsumer(), - 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.26388, 12000); } @Test @@ -144,8 +144,6 @@ public class BluetoothPowerCalculatorTest { .isEqualTo(6166); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) .isWithin(PRECISION).of(0.1226666); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer.Key foreground = uidConsumer.getKey( BatteryConsumer.POWER_COMPONENT_BLUETOOTH, @@ -178,16 +176,16 @@ public class BluetoothPowerCalculatorTest { assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.08216, 3583); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(APP_UID), - 0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.18169, 8416); assertBluetoothPowerAndDuration( mStatsRule.getDeviceBatteryConsumer(), - 0.30030, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.30030, 12000); assertBluetoothPowerAndDuration( mStatsRule.getAppsBatteryConsumer(), - 0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.26386, 11999); } @Test @@ -202,16 +200,16 @@ public class BluetoothPowerCalculatorTest { assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.10378, 3583, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + 0.10378, 3583); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(APP_UID), - 0.22950, 8416, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + 0.22950, 8416); assertBluetoothPowerAndDuration( mStatsRule.getDeviceBatteryConsumer(), - 0.33333, 12000, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + 0.33333, 12000); assertBluetoothPowerAndDuration( mStatsRule.getAppsBatteryConsumer(), - 0.33329, 11999, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + 0.33329, 11999); } @Test @@ -264,8 +262,6 @@ public class BluetoothPowerCalculatorTest { .isEqualTo(6166); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) .isWithin(PRECISION).of(0.8220561); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer.Key foreground = uidConsumer.getKey( BatteryConsumer.POWER_COMPONENT_BLUETOOTH, @@ -299,16 +295,16 @@ public class BluetoothPowerCalculatorTest { assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID), - 0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.08216, 3583); assertBluetoothPowerAndDuration( mStatsRule.getUidBatteryConsumer(APP_UID), - 0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.18169, 8416); assertBluetoothPowerAndDuration( mStatsRule.getDeviceBatteryConsumer(), - 0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.26388, 12000); assertBluetoothPowerAndDuration( mStatsRule.getAppsBatteryConsumer(), - 0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE); + 0.26386, 11999); } private void setupBluetoothEnergyInfo(long reportedEnergyUc, long consumedEnergyUc) { @@ -326,14 +322,12 @@ public class BluetoothPowerCalculatorTest { } private void assertBluetoothPowerAndDuration(@Nullable BatteryConsumer batteryConsumer, - double powerMah, int durationMs, @BatteryConsumer.PowerModel int powerModel) { + double powerMah, int durationMs) { assertThat(batteryConsumer).isNotNull(); double consumedPower = batteryConsumer.getConsumedPower( BatteryConsumer.POWER_COMPONENT_BLUETOOTH); assertThat(consumedPower).isWithin(PRECISION).of(powerMah); - assertThat(batteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) - .isEqualTo(powerModel); long usageDurationMillis = batteryConsumer.getUsageDurationMillis( BatteryConsumer.POWER_COMPONENT_BLUETOOTH); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java index 7225f2d3995c..4cd3857a80bb 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java @@ -70,16 +70,12 @@ public class CameraPowerCalculatorTest { .isEqualTo(1000); assertThat(app1Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.1); - assertThat(app1Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); UidBatteryConsumer app2Consumer = mStatsRule.getUidBatteryConsumer(APP2_UID); assertThat(app2Consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(2000); assertThat(app2Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.2); - assertThat(app2Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceBatteryConsumer @@ -87,8 +83,6 @@ public class CameraPowerCalculatorTest { .isEqualTo(3000); assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.3); - assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsBatteryConsumer @@ -96,8 +90,6 @@ public class CameraPowerCalculatorTest { .isEqualTo(3000); assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.3); - assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -122,16 +114,12 @@ public class CameraPowerCalculatorTest { .isEqualTo(1000); assertThat(app1Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.2); - assertThat(app1Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer app2Consumer = mStatsRule.getUidBatteryConsumer(APP2_UID); assertThat(app2Consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(2000); assertThat(app2Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.3); - assertThat(app2Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceBatteryConsumer @@ -139,8 +127,6 @@ public class CameraPowerCalculatorTest { .isEqualTo(3000); assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.5); - assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsBatteryConsumer @@ -148,7 +134,5 @@ public class CameraPowerCalculatorTest { .isEqualTo(3000); assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.5); - assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java index 4cea72835fba..527db67a84ae 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java @@ -192,8 +192,6 @@ public class CpuPowerCalculatorTest { .isEqualTo(3333); assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(1.031677); - assertThat(uidConsumer1.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar"); UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); @@ -201,21 +199,15 @@ public class CpuPowerCalculatorTest { .isEqualTo(7777); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(2.489544); - assertThat(uidConsumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull(); final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(3.52122); - assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(3.52122); - assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -264,8 +256,6 @@ public class CpuPowerCalculatorTest { .isEqualTo(3333); assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(3.18877); - assertThat(uidConsumer1.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar"); UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); @@ -273,21 +263,15 @@ public class CpuPowerCalculatorTest { .isEqualTo(7777); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(7.44072); - assertThat(uidConsumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull(); final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(10.62949); - assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer appsBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) .isWithin(PRECISION).of(10.62949); - assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java index 3b5658c2e40a..506bab4b3600 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java @@ -68,20 +68,14 @@ public class GnssPowerCalculatorTest { .isEqualTo(1000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(0.1); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(0.1); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(0.1); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -107,27 +101,19 @@ public class GnssPowerCalculatorTest { .isEqualTo(1000); assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(2.77777); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer consumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(consumer2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_GNSS)) .isEqualTo(2000); assertThat(consumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(5.55555); - assertThat(consumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(8.333333); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS)) .isWithin(PRECISION).of(8.333333); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java index 9b810bc01b4d..eba820e95783 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java @@ -164,8 +164,6 @@ public class MobileRadioPowerCalculatorTest { // = 4604000 mA-ms or 1.27888 mA-h assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.27888); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration) // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration) @@ -178,22 +176,16 @@ public class MobileRadioPowerCalculatorTest { BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.94); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 3/4 of total packets were sent by APP_UID so 75% of total UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.705); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // Rest should go to the other app UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.235); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -321,8 +313,6 @@ public class MobileRadioPowerCalculatorTest { // = 5177753 mA-ms or 1.43826 mA-h total consumption assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.43826); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration) // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration) @@ -335,22 +325,16 @@ public class MobileRadioPowerCalculatorTest { BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.09938); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 3/4 of total packets were sent by APP_UID so 75% of total UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.82453); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // Rest should go to the other app UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.27484); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -441,8 +425,6 @@ public class MobileRadioPowerCalculatorTest { // = 4604000 mA-ms or 1.27888 mA-h assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.27888); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 720 mA * 100 ms (level 0 TX drain rate * level 0 TX duration) // + 1080 mA * 200 ms (level 1 TX drain rate * level 1 TX duration) @@ -455,22 +437,16 @@ public class MobileRadioPowerCalculatorTest { BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.94); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // 3/4 of total packets were sent by APP_UID so 75% of total UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.705); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); // Rest should go to the other app UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.235); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -648,24 +624,16 @@ public class MobileRadioPowerCalculatorTest { // 200ms phone on duration / 2000 total duration * 2.77778 mAh = 0.27777 assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(2.5); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE)) .isWithin(PRECISION).of(0.27778); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_PHONE)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.38541); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.38541); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test @@ -782,12 +750,8 @@ public class MobileRadioPowerCalculatorTest { // 1000ms phone on duration / 10000 total duration * 2.77778 mAh = 0.27777 assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(2.5); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE)) .isWithin(PRECISION).of(0.27778); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_PHONE)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); // CDMA2000 [Tx0, Tx1, Tx2, Tx3, Tx4, Rx] drain * duration // [720, 1080, 1440, 1800, 2160, 1440] mA . [10, 11, 12, 13, 14, 15] ms = 111600 mA-ms @@ -817,8 +781,6 @@ public class MobileRadioPowerCalculatorTest { BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.91094); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); // 240 ms Rx Time, 1110 ms Tx Time, 1350 ms active time // 150 App 1 Rx Packets, 10 App 1 Tx packets @@ -841,15 +803,11 @@ public class MobileRadioPowerCalculatorTest { UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.27574); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); // Rest should go to the other app UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(0.63520); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test @@ -936,12 +894,8 @@ public class MobileRadioPowerCalculatorTest { // 1000ms phone on duration / 10000 total duration * 2.77778 mAh = 0.27777 assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(2.5); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE)) .isWithin(PRECISION).of(0.27778); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_PHONE)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); // Estimated Rx/Tx modem consumption = 0.94 mAh @@ -949,14 +903,10 @@ public class MobileRadioPowerCalculatorTest { // 2.5 * 0.94 / 1.27888 = 1.83754 mAh assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.83754); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) .isWithin(PRECISION).of(1.83754); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java index 2da98e8b9a61..7f20035253f0 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java @@ -104,8 +104,6 @@ public class ScreenPowerCalculatorTest { // Uid1 charge = 200000000 + 5 / 45 * 300000000 mAs = 64.81 mAh assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(64.81481); - assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -116,8 +114,6 @@ public class ScreenPowerCalculatorTest { // Uid2 charge = 40 / 45 * 300000000 + 100000000 mAs = 101.85 mAh assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(101.85185); - assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -126,8 +122,6 @@ public class ScreenPowerCalculatorTest { // 600000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 166.66666 mAh assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(166.66666); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -135,11 +129,8 @@ public class ScreenPowerCalculatorTest { assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(166.66666); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } - @Test public void testMeasuredEnergyBasedModel_multiDisplay() { mStatsRule.initMeasuredEnergyStatsLocked() @@ -202,8 +193,6 @@ public class ScreenPowerCalculatorTest { // (600000000 + 800000000) uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 166.66666 mAh assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(388.88888); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1); assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -214,8 +203,6 @@ public class ScreenPowerCalculatorTest { // Uid1 charge = 20 / 80 * 600000000 mAs = 41.66666 mAh assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(41.66666); - assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -226,17 +213,12 @@ public class ScreenPowerCalculatorTest { // Uid1 charge = 60 / 80 * 600000000 + 800000000 mAs = 347.22222 mAh assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(347.22222); - assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(110 * MINUTE_IN_MS); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(388.88888); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); - } @Test @@ -277,8 +259,6 @@ public class ScreenPowerCalculatorTest { // Uid1 charge = 20 / 80 * 92.0 = 23.0 mAh assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(23.0); - assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -288,27 +268,20 @@ public class ScreenPowerCalculatorTest { // Uid2 charge = 60 / 80 * 92.0 = 69.0 mAh assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(69.0); - assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(80 * MINUTE_IN_MS); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(92); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(80 * MINUTE_IN_MS); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(92); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } - @Test public void testPowerProfileBasedModel_multiDisplay() { mStatsRule.setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 1, 60.0) @@ -364,8 +337,6 @@ public class ScreenPowerCalculatorTest { // 92 + 60 * 0.5 + 10 * 0.1 + 90 * 0.2 + 30 * 0.2 = 147 assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(147); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1); assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -375,8 +346,6 @@ public class ScreenPowerCalculatorTest { // Uid1 charge = 20 / 110 * 147.0 = 23.0 mAh assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(26.72727); - assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2); assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) @@ -386,17 +355,12 @@ public class ScreenPowerCalculatorTest { // Uid2 charge = 90 / 110 * 92.0 = 69.0 mAh assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(120.272727); - assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(110 * MINUTE_IN_MS); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(147); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); - } private void setProcState(int uid, int procState, boolean resumed, long realtimeMs, diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java index 8e221be261e9..827d2f8f04c8 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java @@ -159,22 +159,16 @@ public class WifiPowerCalculatorTest { .isEqualTo(2473); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.3964); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(4001); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.86666); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); BatteryConsumer appsConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.866666); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -214,8 +208,6 @@ public class WifiPowerCalculatorTest { .isEqualTo(12423); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(2.0214666); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer.Key foreground = uidConsumer.getKey( BatteryConsumer.POWER_COMPONENT_WIFI, @@ -248,22 +240,16 @@ public class WifiPowerCalculatorTest { /* Same ratio as in testPowerControllerBasedModel_nonMeasured but scaled by 1_000_000uC. */ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.2214666 / (0.2214666 + 0.645200) * 1_000_000 / 3600000); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI)) .isEqualTo(4002); assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.27777); - assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); BatteryConsumer appsConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.277777); - assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } @Test @@ -302,8 +288,6 @@ public class WifiPowerCalculatorTest { .isEqualTo(12423); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(1.0325211); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer.Key foreground = uidConsumer.getKey( BatteryConsumer.POWER_COMPONENT_WIFI, @@ -349,8 +333,6 @@ public class WifiPowerCalculatorTest { .isEqualTo(1000); assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.8231573); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @Test @@ -371,8 +353,6 @@ public class WifiPowerCalculatorTest { /* Same ratio as in testTimerBasedModel_nonMeasured but scaled by 1_000_000uC. */ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) .isWithin(PRECISION).of(0.8231573 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000); - assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI)) - .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } private WifiActivityEnergyInfo buildWifiActivityEnergyInfo(long timeSinceBoot, diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java index f7a16383974d..cca60339acf7 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java @@ -176,7 +176,6 @@ public class BasePowerStatsProcessorTest { powerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0); BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0], - /* includePowerModels */ false, /* includeProcessStateData */ true, /* includeScreenStateData */ true, /* includesPowerStateData */ true, diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java index 4643dddb1a33..38fe6134d992 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java @@ -342,7 +342,7 @@ public class PowerStatsExporterTest { PowerStatsExporter exporter = new PowerStatsExporter(mPowerStatsStore, mPowerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0); - BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0], false, + BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0], includeProcessStateData, includeScreenStateData, includesPowerStateData, 0); exporter.populateBatteryUsageStatsBuilder(builder, aps); return builder.build(); @@ -361,7 +361,7 @@ public class PowerStatsExporterTest { private void breakdownByProcState_fullRange(boolean includeScreenStateData, boolean includePowerStateData) throws Exception { BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - new String[]{"cu570m"}, /* includePowerModels */ false, + new String[]{"cu570m"}, /* includeProcessStateData */ true, includeScreenStateData, includePowerStateData, /* powerThreshold */ 0); exportAggregatedPowerStats(builder, 1000, 10000); @@ -406,7 +406,7 @@ public class PowerStatsExporterTest { @Test public void breakdownByProcState_subRange() throws Exception { BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - new String[]{"cu570m"}, /* includePowerModels */ false, + new String[]{"cu570m"}, /* includeProcessStateData */ true, true, true, /* powerThreshold */ 0); exportAggregatedPowerStats(builder, 3700, 6700); @@ -438,7 +438,7 @@ public class PowerStatsExporterTest { @Test public void combinedProcessStates() throws Exception { BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - new String[]{"cu570m"}, /* includePowerModels */ false, + new String[]{"cu570m"}, /* includeProcessStateData */ false, true, true, /* powerThreshold */ 0); exportAggregatedPowerStats(builder, 1000, 10000); diff --git a/services/tests/security/intrusiondetection/AndroidManifest.xml b/services/tests/security/intrusiondetection/AndroidManifest.xml index f388e7ea8590..39e41cddb662 100644 --- a/services/tests/security/intrusiondetection/AndroidManifest.xml +++ b/services/tests/security/intrusiondetection/AndroidManifest.xml @@ -18,10 +18,19 @@ package="com.android.server.security.intrusiondetection.tests"> <uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING" /> - <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" /> + <uses-permission android:name="android.permission.INTERNET"/> - <application android:testOnly="true"> + <application android:testOnly="true" android:debuggable="true" android:usesCleartextTraffic="true"> <uses-library android:name="android.test.runner"/> + <receiver android:name="com.android.server.security.intrusiondetection.IntrusionDetectionAdminReceiver" + android:permission="android.permission.BIND_DEVICE_ADMIN" + android:exported="true"> + <meta-data android:name="android.app.device_admin" + android:resource="@xml/device_admin"/> + <intent-filter> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> + </intent-filter> + </receiver> </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" diff --git a/services/tests/security/intrusiondetection/res/xml/device_admin.xml b/services/tests/security/intrusiondetection/res/xml/device_admin.xml new file mode 100644 index 000000000000..f8cd8f0b9b44 --- /dev/null +++ b/services/tests/security/intrusiondetection/res/xml/device_admin.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<device-admin xmlns:android="http://schemas.android.com/apk/res/android"> +</device-admin> diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java index bc854cf6488b..c185ad5ffd61 100644 --- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java +++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java @@ -16,14 +16,19 @@ package com.android.server.security.intrusiondetection; +import static android.Manifest.permission.INTERNET; import static android.Manifest.permission.MANAGE_INTRUSION_DETECTION_STATE; import static android.Manifest.permission.READ_INTRUSION_DETECTION_STATE; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @@ -33,7 +38,9 @@ import static org.mockito.Mockito.verify; import android.annotation.SuppressLint; import android.app.admin.ConnectEvent; import android.app.admin.DnsEvent; +import android.app.admin.SecurityLog; import android.app.admin.SecurityLog.SecurityEvent; +import android.content.ComponentName; import android.content.Context; import android.os.Looper; import android.os.PermissionEnforcer; @@ -43,19 +50,44 @@ import android.os.test.TestLooper; import android.security.intrusiondetection.IIntrusionDetectionServiceCommandCallback; import android.security.intrusiondetection.IIntrusionDetectionServiceStateCallback; import android.security.intrusiondetection.IntrusionDetectionEvent; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; import androidx.test.core.app.ApplicationProvider; +import com.android.bedstead.harrier.BedsteadJUnit4; +import com.android.bedstead.harrier.annotations.AfterClass; +import com.android.bedstead.harrier.annotations.BeforeClass; +import com.android.bedstead.multiuser.annotations.RequireRunOnSystemUser; +import com.android.bedstead.nene.TestApis; +import com.android.bedstead.nene.devicepolicy.DeviceOwner; +import com.android.bedstead.nene.exceptions.NeneException; +import com.android.bedstead.permissions.CommonPermissions; +import com.android.bedstead.permissions.PermissionContext; +import com.android.bedstead.permissions.annotations.EnsureHasPermission; import com.android.server.ServiceThread; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; - +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +@RunWith(BedsteadJUnit4.class) public class IntrusionDetectionServiceTest { private static final int STATE_UNKNOWN = IIntrusionDetectionServiceStateCallback.State.UNKNOWN; @@ -73,6 +105,8 @@ public class IntrusionDetectionServiceTest { private static final int ERROR_DATA_SOURCE_UNAVAILABLE = IIntrusionDetectionServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE; + private static DeviceOwner sDeviceOwner; + private Context mContext; private IntrusionDetectionEventTransportConnection mIntrusionDetectionEventTransportConnection; private DataAggregator mDataAggregator; @@ -83,6 +117,28 @@ public class IntrusionDetectionServiceTest { private Looper mLooperOfDataAggregator; private FakePermissionEnforcer mPermissionEnforcer; + @BeforeClass + public static void setDeviceOwner() { + ComponentName admin = + new ComponentName( + ApplicationProvider.getApplicationContext(), + IntrusionDetectionAdminReceiver.class); + try { + sDeviceOwner = TestApis.devicePolicy().setDeviceOwner(admin); + } catch (NeneException e) { + fail("Failed to set device owner " + admin.flattenToString() + ": " + e); + } + } + + @AfterClass + public static void removeDeviceOwner() { + try { + sDeviceOwner.remove(); + } catch (NeneException e) { + fail("Failed to remove device owner : " + e); + } + } + @SuppressLint("VisibleForTests") @Before public void setUp() { @@ -343,6 +399,172 @@ public class IntrusionDetectionServiceTest { assertNotNull(receivedEvents.get(2).getDnsEvent()); } + @Test + @RequireRunOnSystemUser + public void testDataSources_Initialize_HasDeviceOwner() throws Exception { + NetworkLogSource networkLogSource = new NetworkLogSource(mContext, mDataAggregator); + SecurityLogSource securityLogSource = new SecurityLogSource(mContext, mDataAggregator); + + assertTrue(networkLogSource.initialize()); + assertTrue(securityLogSource.initialize()); + } + + @Test + @RequireRunOnSystemUser + public void testDataSources_Initialize_NoDeviceOwner() throws Exception { + NetworkLogSource networkLogSource = new NetworkLogSource(mContext, mDataAggregator); + SecurityLogSource securityLogSource = new SecurityLogSource(mContext, mDataAggregator); + ComponentName admin = sDeviceOwner.componentName(); + + try { + sDeviceOwner.remove(); + assertFalse(networkLogSource.initialize()); + assertFalse(securityLogSource.initialize()); + } finally { + sDeviceOwner = TestApis.devicePolicy().setDeviceOwner(admin); + } + } + + @Test + @RequireRunOnSystemUser + @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) + public void testDataAggregator_AddSecurityEvent() throws Exception { + mIntrusionDetectionService.setState(STATE_ENABLED); + ServiceThread mockThread = spy(ServiceThread.class); + mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread); + assertTrue(mDataAggregator.initialize()); + + // SecurityLogging generates a number of events and callbacks, so create a latch to wait for + // the given event. + String eventString = this.getClass().getName() + ".testSecurityEvent"; + + final CountDownLatch latch = new CountDownLatch(1); + // TODO: Replace this mock when the IntrusionDetectionEventTransportConnection is ready. + doAnswer( + new Answer<Boolean>() { + @Override + public Boolean answer(InvocationOnMock input) { + List<IntrusionDetectionEvent> receivedEvents = + (List<IntrusionDetectionEvent>) input.getArguments()[0]; + for (IntrusionDetectionEvent event : receivedEvents) { + if (event.getType() == IntrusionDetectionEvent.SECURITY_EVENT) { + SecurityEvent securityEvent = event.getSecurityEvent(); + Object[] eventData = (Object[]) securityEvent.getData(); + if (securityEvent.getTag() == SecurityLog.TAG_KEY_GENERATED + && eventData[1].equals(eventString)) { + latch.countDown(); + } + } + } + return true; + } + }) + .when(mIntrusionDetectionEventTransportConnection).addData(any()); + mDataAggregator.enable(); + + // Generate the security event. + generateSecurityEvent(eventString); + TestApis.devicePolicy().forceSecurityLogs(); + + // Verify the event is received. + mTestLooper.startAutoDispatch(); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + mTestLooper.stopAutoDispatch(); + + mDataAggregator.disable(); + } + + @Test + @RequireRunOnSystemUser + @EnsureHasPermission(CommonPermissions.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) + public void testDataAggregator_AddNetworkEvent() throws Exception { + mIntrusionDetectionService.setState(STATE_ENABLED); + ServiceThread mockThread = spy(ServiceThread.class); + mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread); + assertTrue(mDataAggregator.initialize()); + + // Network logging may log multiple and callbacks, so create a latch to wait for + // the given event. + // eventServer must be a valid domain to generate a network log event. + String eventServer = "google.com"; + final CountDownLatch latch = new CountDownLatch(1); + // TODO: Replace this mock when the IntrusionDetectionEventTransportConnection is ready. + doAnswer( + new Answer<Boolean>() { + @Override + public Boolean answer(InvocationOnMock input) { + List<IntrusionDetectionEvent> receivedEvents = + (List<IntrusionDetectionEvent>) input.getArguments()[0]; + for (IntrusionDetectionEvent event : receivedEvents) { + if (event.getType() + == IntrusionDetectionEvent.NETWORK_EVENT_DNS) { + DnsEvent dnsEvent = event.getDnsEvent(); + if (dnsEvent.getHostname().equals(eventServer)) { + latch.countDown(); + } + } + } + return true; + } + }) + .when(mIntrusionDetectionEventTransportConnection).addData(any()); + mDataAggregator.enable(); + + // Generate the network event. + generateNetworkEvent(eventServer); + TestApis.devicePolicy().forceNetworkLogs(); + + // Verify the event is received. + mTestLooper.startAutoDispatch(); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + mTestLooper.stopAutoDispatch(); + + mDataAggregator.disable(); + } + + /** Emits a given string into security log (if enabled). */ + private void generateSecurityEvent(String eventString) + throws IllegalArgumentException, GeneralSecurityException, IOException { + if (eventString == null || eventString.isEmpty()) { + throw new IllegalArgumentException( + "Error generating security event: eventString must not be empty"); + } + + final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore"); + keyGen.initialize( + new KeyGenParameterSpec.Builder(eventString, KeyProperties.PURPOSE_SIGN).build()); + // Emit key generation event. + final KeyPair keyPair = keyGen.generateKeyPair(); + assertNotNull(keyPair); + + final KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); + ks.load(null); + // Emit key destruction event. + ks.deleteEntry(eventString); + } + + /** Emits a given string into network log (if enabled). */ + private void generateNetworkEvent(String server) throws IllegalArgumentException, IOException { + if (server == null || server.isEmpty()) { + throw new IllegalArgumentException( + "Error generating network event: server must not be empty"); + } + + HttpURLConnection urlConnection = null; + int connectionTimeoutMS = 2_000; + try (PermissionContext p = TestApis.permissions().withPermission(INTERNET)) { + final URL url = new URL("http://" + server); + urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setConnectTimeout(connectionTimeoutMS); + urlConnection.setReadTimeout(connectionTimeoutMS); + urlConnection.getResponseCode(); + } finally { + if (urlConnection != null) { + urlConnection.disconnect(); + } + } + } + private class MockInjector implements IntrusionDetectionService.Injector { private final Context mContext; diff --git a/services/tests/servicestests/jni/Android.bp b/services/tests/servicestests/jni/Android.bp index 0a3103722796..e738c19cc545 100644 --- a/services/tests/servicestests/jni/Android.bp +++ b/services/tests/servicestests/jni/Android.bp @@ -22,9 +22,9 @@ cc_library_shared { srcs: [ ":lib_cachedAppOptimizer_native", ":lib_freezer_native", - ":lib_gameManagerService_native", ":lib_oomConnection_native", ":lib_anrTimer_native", + ":lib_lazilyRegisteredServices_native", "onload.cpp", ], @@ -55,6 +55,8 @@ cc_library_shared { "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.common@1.2", "android.hardware.graphics.mapper@4.0", + "android.hardware.ir@1.0", + "android.hardware.vr@1.0", "android.hidl.token@1.0-utils", ], } diff --git a/services/tests/servicestests/jni/onload.cpp b/services/tests/servicestests/jni/onload.cpp index 25487c5aabbe..ad979c62f40e 100644 --- a/services/tests/servicestests/jni/onload.cpp +++ b/services/tests/servicestests/jni/onload.cpp @@ -25,9 +25,9 @@ namespace android { int register_android_server_am_CachedAppOptimizer(JNIEnv* env); -int register_android_server_app_GameManagerService(JNIEnv* env); int register_android_server_am_OomConnection(JNIEnv* env); int register_android_server_utils_AnrTimer(JNIEnv *env); +int register_android_server_utils_LazyJniRegistrar(JNIEnv* env); }; using namespace android; @@ -43,8 +43,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) } ALOG_ASSERT(env, "Could not retrieve the env!"); register_android_server_am_CachedAppOptimizer(env); - register_android_server_app_GameManagerService(env); register_android_server_am_OomConnection(env); register_android_server_utils_AnrTimer(env); + register_android_server_utils_LazyJniRegistrar(env); return JNI_VERSION_1_4; } 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 492838e9b4fb..27de7644f6b2 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -38,7 +38,6 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity.EXTRA_TYPE_TO_CHOOSE; import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG; -import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; import static com.google.common.truth.Truth.assertThat; @@ -601,7 +600,6 @@ public class AccessibilityManagerServiceTest { } @Test - @EnableFlags(FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) public void testSetConnectionNull_borderFlagEnabled_unregisterFullScreenMagnification() throws RemoteException { mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java index 403930d96a12..2ae31ad618d6 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java @@ -18,20 +18,24 @@ package com.android.server.accessibility; import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT; import static com.android.server.accessibility.AccessibilityWindowManagerTest.DisplayIdMatcher.displayId; +import static com.android.server.accessibility.AccessibilityWindowManagerTest.EventWindowIdMatcher.eventWindowId; import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowChangesMatcher.a11yWindowChanges; -import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.a11yWindowId; +import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.windowId; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -42,14 +46,13 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.Nullable; +import android.graphics.Point; +import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; import android.os.LocaleList; import android.os.RemoteException; import android.os.UserHandle; -import android.platform.test.annotations.RequiresFlagsDisabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.SparseArray; import android.view.Display; import android.view.IWindow; @@ -63,6 +66,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection; import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; import com.android.server.accessibility.test.MessageCapturingHandler; +import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow; import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; @@ -70,7 +74,6 @@ import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; @@ -81,17 +84,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -// This test verifies deprecated codepath. Probably changing this file means -// AccessibilityWindowManagerWithAccessibilityWindowTest also needs to be updated. -// LINT.IfChange - /** - * Tests for the AccessibilityWindowManager with Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2 - * enabled. - * TODO(b/322444245): Merge with AccessibilityWindowManagerWithAccessibilityWindowTest - * after completing the flag migration. + * Tests for the AccessibilityWindowManager. */ -@RequiresFlagsDisabled(Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2) public class AccessibilityWindowManagerTest { private static final String PACKAGE_NAME = "com.android.server.accessibility"; private static final boolean FORCE_SEND = true; @@ -122,9 +117,8 @@ public class AccessibilityWindowManagerTest { // List of window token, mapping from windowId -> window token. private final SparseArray<IWindow> mA11yWindowTokens = new SparseArray<>(); - // List of window info lists, mapping from displayId -> window info lists. - private final SparseArray<ArrayList<WindowInfo>> mWindowInfos = - new SparseArray<>(); + // List of window info lists, mapping from displayId -> a11y window lists. + private final SparseArray<ArrayList<AccessibilityWindow>> mWindows = new SparseArray<>(); // List of callback, mapping from displayId -> callback. private final SparseArray<WindowsForAccessibilityCallback> mCallbackOfWindows = new SparseArray<>(); @@ -134,6 +128,13 @@ public class AccessibilityWindowManagerTest { private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null); + // This maps displayId -> next region offset. + // Touchable region must have un-occluded area so that it's exposed to a11y services. + // This offset can be used as left and top of new region so that top-left of each region are + // kept visible. + // It's expected to be incremented by some amount everytime the value is used. + private final SparseArray<Integer> mNextRegionOffsets = new SparseArray<>(); + @Mock private WindowManagerInternal mMockWindowManagerInternal; @Mock private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender; @Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy; @@ -144,9 +145,6 @@ public class AccessibilityWindowManagerTest { @Mock private IBinder mMockEmbeddedToken; @Mock private IBinder mMockInvalidToken; - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); @@ -159,7 +157,7 @@ public class AccessibilityWindowManagerTest { anyString(), anyInt(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME); doAnswer((invocation) -> { - onWindowsForAccessibilityChanged(invocation.getArgument(0), false); + onAccessibilityWindowsChanged(invocation.getArgument(0), false); return null; }).when(mMockWindowManagerInternal).computeWindowsForAccessibility(anyInt()); @@ -173,7 +171,7 @@ public class AccessibilityWindowManagerTest { // as top focused display before each testing starts. startTrackingPerDisplay(Display.DEFAULT_DISPLAY); - // AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged. + // AccessibilityEventSender is invoked during onAccessibilityWindowsChanged. // Resets it for mockito verify of further test case. Mockito.reset(mMockA11yEventSender); @@ -237,19 +235,18 @@ public class AccessibilityWindowManagerTest { @Test public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() { final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - WindowInfo focusedWindowInfo = - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX); + final WindowInfo focusedWindowInfo = + mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo(); assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, focusedWindowInfo.token)); focusedWindowInfo.focused = false; - focusedWindowInfo = - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1); - focusedWindowInfo.focused = true; + mWindows.get(Display.DEFAULT_DISPLAY).get( + DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().focused = true; mA11yWindowManager.onTouchInteractionStart(); setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); } @@ -273,7 +270,7 @@ public class AccessibilityWindowManagerTest { changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, DEFAULT_FOCUSED_INDEX + 1, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); - onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); // The active window should not be changed. assertEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); // The top focused window should not be changed. @@ -301,8 +298,8 @@ public class AccessibilityWindowManagerTest { changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, DEFAULT_FOCUSED_INDEX, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - onWindowsForAccessibilityChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); // The active window should be changed. assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); // The top focused window should be changed. @@ -312,53 +309,181 @@ public class AccessibilityWindowManagerTest { @Test public void onWindowsChanged_shouldReportCorrectLayer() { - // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup. + // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup. List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); for (int i = 0; i < a11yWindows.size(); i++) { final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); - final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i); - assertThat(mWindowInfos.get(Display.DEFAULT_DISPLAY).size() - windowInfo.layer - 1, + assertThat(mWindows.get(Display.DEFAULT_DISPLAY).size() - i - 1, is(a11yWindow.getLayer())); } } @Test public void onWindowsChanged_shouldReportCorrectOrder() { - // AccessibilityWindowManager#onWindowsForAccessibilityChanged already invoked in setup. + // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup. List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); for (int i = 0; i < a11yWindows.size(); i++) { final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); final IBinder windowToken = mA11yWindowManager .getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId()); - final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(i); + final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY) + .get(i).getWindowInfo(); assertThat(windowToken, is(windowInfo.token)); } } @Test - public void onWindowsChangedAndForceSend_shouldUpdateWindows() { - final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); - final int correctLayer = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer(); - windowInfo.layer += 1; + public void onWindowsChanged_shouldNotReportNonTouchableWindow() { + final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + when(window.isTouchable()).thenReturn(false); + final int windowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, window.getWindowInfo().token); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List<AccessibilityWindowInfo> a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, not(hasItem(windowId(windowId)))); + } + + @Test + public void onWindowsChanged_shouldReportFocusedNonTouchableWindow() { + final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get( + DEFAULT_FOCUSED_INDEX); + when(window.isTouchable()).thenReturn(false); + final int windowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, window.getWindowInfo().token); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List<AccessibilityWindowInfo> a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasItem(windowId(windowId))); + } + + @Test + public void onWindowsChanged_trustedFocusedNonTouchableWindow_shouldNotHideWindowsBelow() { + // Make the focused trusted un-touchable window fullscreen. + final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get( + DEFAULT_FOCUSED_INDEX); + setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + when(window.isTouchable()).thenReturn(false); + when(window.isTrustedOverlay()).thenReturn(true); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List<AccessibilityWindowInfo> a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS)); + } + + @Test + public void onWindowsChanged_accessibilityOverlay_shouldNotHideWindowsBelow() { + // Make the a11y overlay window fullscreen. + final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + when(window.getType()).thenReturn(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List<AccessibilityWindowInfo> a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS)); + } + + @Test + public void onWindowsChanged_shouldReportFocusedWindowEvenIfOccluded() { + // Make the front window fullscreen. + final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(frontWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + final int frontWindowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, frontWindow.getWindowInfo().token); + + final AccessibilityWindow focusedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get( + DEFAULT_FOCUSED_INDEX); + final int focusedWindowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, focusedWindow.getWindowInfo().token); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List<AccessibilityWindowInfo> a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(2)); + assertThat(a11yWindows.get(0), windowId(frontWindowId)); + assertThat(a11yWindows.get(1), windowId(focusedWindowId)); + } + + @Test + public void onWindowsChanged_embeddedWindows_shouldOnlyReportHost() throws RemoteException { + final Rect embeddingBounds = new Rect(0, 0, 200, 100); + + // The embedded window comes front of the host window. + final IBinder embeddedWindowLeashToken = Mockito.mock(IBinder.class); + final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, + false, embeddedWindowLeashToken, USER_SYSTEM_ID); + final AccessibilityWindow embeddedWindow = createMockAccessibilityWindow( + mA11yWindowTokens.get(embeddedWindowId), Display.DEFAULT_DISPLAY); + setRegionForMockAccessibilityWindow(embeddedWindow, new Region(embeddingBounds)); + mWindows.get(Display.DEFAULT_DISPLAY).set(0, embeddedWindow); + + final IBinder hostWindowLeashToken = Mockito.mock(IBinder.class); + final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, + false, hostWindowLeashToken, USER_SYSTEM_ID); + final AccessibilityWindow hostWindow = createMockAccessibilityWindow( + mA11yWindowTokens.get(hostWindowId), Display.DEFAULT_DISPLAY); + setRegionForMockAccessibilityWindow(hostWindow, new Region(embeddingBounds)); + mWindows.get(Display.DEFAULT_DISPLAY).set(1, hostWindow); + + mA11yWindowManager.associateEmbeddedHierarchyLocked( + hostWindowLeashToken, embeddedWindowLeashToken); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List<AccessibilityWindowInfo> a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, not(hasItem(windowId(embeddedWindowId)))); + assertThat(a11yWindows.get(0), windowId(hostWindowId)); + final Rect bounds = new Rect(); + a11yWindows.get(0).getBoundsInScreen(bounds); + assertEquals(bounds, embeddingBounds); + } - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); - assertNotEquals(correctLayer, - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer()); + @Test + public void onWindowsChanged_shouldNotReportfullyOccludedWindow() { + final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(frontWindow, new Region(100, 100, 300, 300)); + final int frontWindowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, frontWindow.getWindowInfo().token); + + // index 1 is focused. Let's use the next one for this test. + final AccessibilityWindow occludedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(2); + setRegionForMockAccessibilityWindow(occludedWindow, new Region(150, 150, 250, 250)); + final int occludedWindowId = mA11yWindowManager.findWindowIdLocked( + USER_SYSTEM_ID, occludedWindow.getWindowInfo().token); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + + final List<AccessibilityWindowInfo> a11yWindows = + mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasItem(windowId(frontWindowId))); + assertThat(a11yWindows, not(hasItem(windowId(occludedWindowId)))); } @Test - public void onWindowsChangedNoForceSend_layerChanged_shouldNotUpdateWindows() { - final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); - final int correctLayer = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer(); - windowInfo.layer += 1; + public void onWindowsChangedAndForceSend_shouldUpdateWindows() { + assertNotEquals("new title", + toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) + .get(0).getTitle())); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - assertEquals(correctLayer, - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0).getLayer()); + mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().title = "new title"; + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + assertEquals("new title", + toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) + .get(0).getTitle())); } @Test @@ -368,14 +493,10 @@ public class AccessibilityWindowManagerTest { mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0); final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, true, USER_SYSTEM_ID); - final WindowInfo windowInfo = WindowInfo.obtain(); - windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; - windowInfo.token = token.asBinder(); - windowInfo.layer = 0; - windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - mWindowInfos.get(Display.DEFAULT_DISPLAY).set(0, windowInfo); + mWindows.get(Display.DEFAULT_DISPLAY).set(0, + createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY)); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); assertNotEquals(oldWindow, mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0)); } @@ -383,12 +504,12 @@ public class AccessibilityWindowManagerTest { @Test public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() { final WindowInfo focusedWindowInfo = - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX); - final WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); + mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo(); + final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo(); focusedWindowInfo.focused = false; windowInfo.focused = true; - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); assertTrue(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0) .isFocused()); } @@ -497,15 +618,18 @@ public class AccessibilityWindowManagerTest { @Test public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() { // Updates top 2 z-order WindowInfo are whole visible. - WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); - windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); - windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1); - windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2, - SCREEN_WIDTH, SCREEN_HEIGHT); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(firstWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2)); + final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); + setRegionForMockAccessibilityWindow(secondWindow, + new Region(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT)); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(2)); final Region outBounds = new Region(); int windowId = a11yWindows.get(0).getId(); @@ -523,12 +647,17 @@ public class AccessibilityWindowManagerTest { @Test public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() { // Updates z-order #1 WindowInfo is half visible. - WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); - windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); - - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(firstWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2)); + final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); + setRegionForMockAccessibilityWindow(secondWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(2)); final Region outBounds = new Region(); int windowId = a11yWindows.get(1).getId(); @@ -539,9 +668,17 @@ public class AccessibilityWindowManagerTest { @Test public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() { - // Since z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible. + // z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible. + final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(firstWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + // Note that the second window is also exposed even if region is empty because it's focused. + assertThat(a11yWindows, hasSize(2)); final Region outBounds = new Region(); int windowId = a11yWindows.get(1).getId(); @@ -552,16 +689,21 @@ public class AccessibilityWindowManagerTest { @Test public void computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion() { // Updates z-order #0 WindowInfo to have two interact-able areas. - Region region = new Region(0, 0, SCREEN_WIDTH, 200); + final Region region = new Region(0, 0, SCREEN_WIDTH, 200); region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION); - WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0); - windowInfo.regionInScreen.set(region); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); + setRegionForMockAccessibilityWindow(firstWindow, region); + final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); + setRegionForMockAccessibilityWindow(secondWindow, + new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); + + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); + assertThat(a11yWindows, hasSize(2)); final Region outBounds = new Region(); - int windowId = a11yWindows.get(1).getId(); + final int windowId = a11yWindows.get(1).getId(); mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); assertFalse(outBounds.getBounds().isEmpty()); @@ -572,7 +714,8 @@ public class AccessibilityWindowManagerTest { @Test public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() { final IBinder eventWindowToken = - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX + 1).token; + mWindows.get(Display.DEFAULT_DISPLAY) + .get(DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().token; final int eventWindowId = mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, eventWindowToken); when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates()) @@ -611,11 +754,11 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(currentActiveWindowId), + eventWindowId(currentActiveWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); assertThat(captor.getAllValues().get(1), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(eventWindowId), + eventWindowId(eventWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); } @@ -641,7 +784,7 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(eventWindowId), + eventWindowId(eventWindowId), a11yWindowChanges( AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); } @@ -690,12 +833,12 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(initialDisplayId), - a11yWindowId(initialWindowId), + eventWindowId(initialWindowId), a11yWindowChanges( AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); assertThat(captor.getAllValues().get(1), allOf(displayId(eventDisplayId), - a11yWindowId(eventWindowId), + eventWindowId(eventWindowId), a11yWindowChanges( AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); } @@ -722,7 +865,7 @@ public class AccessibilityWindowManagerTest { AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, noUse); assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), + AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)); } @@ -751,11 +894,11 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(eventWindowId), + eventWindowId(eventWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); assertThat(captor.getAllValues().get(1), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(currentActiveWindowId), + eventWindowId(currentActiveWindowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); } @@ -763,7 +906,8 @@ public class AccessibilityWindowManagerTest { public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus() throws RemoteException { final IBinder defaultFocusWinToken = - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).token; + mWindows.get(Display.DEFAULT_DISPLAY).get( + DEFAULT_FOCUSED_INDEX).getWindowInfo().token; final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, defaultFocusWinToken); when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates()) @@ -808,8 +952,8 @@ public class AccessibilityWindowManagerTest { @Test public void getPictureInPictureWindow_shouldNotNull() { assertNull(mA11yWindowManager.getPictureInPictureWindowLocked()); - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(1).inPictureInPicture = true; - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + mWindows.get(Display.DEFAULT_DISPLAY).get(1).getWindowInfo().inPictureInPicture = true; + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked()); } @@ -823,8 +967,9 @@ public class AccessibilityWindowManagerTest { final IAccessibilityInteractionConnection mockRemoteConnection = mA11yWindowManager.getConnectionLocked( USER_SYSTEM_ID, outsideWindowId).getRemote(); - mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0).hasFlagWatchOutsideTouch = true; - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); + mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().hasFlagWatchOutsideTouch = + true; + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId); verify(mockRemoteConnection).notifyOutsideTouch(); @@ -942,18 +1087,14 @@ public class AccessibilityWindowManagerTest { @Test public void sendAccessibilityEventOnWindowRemoval() { - final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); + final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY); // Removing index 0 because it's not focused, and avoids unnecessary layer change. final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - infos.remove(0); - for (WindowInfo info : infos) { - // Adjust layer number because it should start from 0. - info.layer--; - } + windows.remove(0); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); final ArgumentCaptor<AccessibilityEvent> captor = ArgumentCaptor.forClass(AccessibilityEvent.class); @@ -961,27 +1102,21 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(windowId), + eventWindowId(windowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED))); } @Test public void sendAccessibilityEventOnWindowAddition() throws RemoteException { - final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); - - for (WindowInfo info : infos) { - // Adjust layer number because new window will have 0 so that layer number in - // A11yWindowInfo in window won't be changed. - info.layer++; - } + final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY); final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, false, USER_SYSTEM_ID); - addWindowInfo(infos, token, 0); - final int windowId = - getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, infos.size() - 1); + // Adding window to the front so that other windows' layer won't change. + windows.add(0, createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY)); + final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); final ArgumentCaptor<AccessibilityEvent> captor = ArgumentCaptor.forClass(AccessibilityEvent.class); @@ -989,17 +1124,17 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(windowId), + eventWindowId(windowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ADDED))); } @Test public void sendAccessibilityEventOnWindowChange() { - final ArrayList<WindowInfo> infos = mWindowInfos.get(Display.DEFAULT_DISPLAY); - infos.get(0).title = "new title"; + final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY); + windows.get(0).getWindowInfo().title = "new title"; final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - onWindowsForAccessibilityChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); + onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); final ArgumentCaptor<AccessibilityEvent> captor = ArgumentCaptor.forClass(AccessibilityEvent.class); @@ -1007,7 +1142,7 @@ public class AccessibilityWindowManagerTest { .sendAccessibilityEventForCurrentUserLocked(captor.capture()); assertThat(captor.getAllValues().get(0), allOf(displayId(Display.DEFAULT_DISPLAY), - a11yWindowId(windowId), + eventWindowId(windowId), a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_TITLE))); } @@ -1017,48 +1152,47 @@ public class AccessibilityWindowManagerTest { } private void startTrackingPerDisplay(int displayId) throws RemoteException { - ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>(); + ArrayList<AccessibilityWindow> windowsForDisplay = new ArrayList<>(); // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos // for the test. - int layer = 0; for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) { final IWindow token = addAccessibilityInteractionConnection(displayId, true, USER_SYSTEM_ID); - addWindowInfo(windowInfosForDisplay, token, layer++); + windowsForDisplay.add(createMockAccessibilityWindow(token, displayId)); } for (int i = 0; i < NUM_APP_WINDOWS; i++) { final IWindow token = addAccessibilityInteractionConnection(displayId, false, USER_SYSTEM_ID); - addWindowInfo(windowInfosForDisplay, token, layer++); + windowsForDisplay.add(createMockAccessibilityWindow(token, displayId)); } // Sets up current focused window of display. // Each display has its own current focused window if config_perDisplayFocusEnabled is true. // Otherwise only default display needs to current focused window. if (mSupportPerDisplayFocus || displayId == Display.DEFAULT_DISPLAY) { - windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true; + windowsForDisplay.get(DEFAULT_FOCUSED_INDEX).getWindowInfo().focused = true; } // Turns on windows tracking, and update window info. mA11yWindowManager.startTrackingWindows(displayId, false); // Puts window lists into array. - mWindowInfos.put(displayId, windowInfosForDisplay); + mWindows.put(displayId, windowsForDisplay); // Sets the default display is the top focused display and // its current focused window is the top focused window. if (displayId == Display.DEFAULT_DISPLAY) { setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX); } // Invokes callback for sending window lists to A11y framework. - onWindowsForAccessibilityChanged(displayId, FORCE_SEND); + onAccessibilityWindowsChanged(displayId, FORCE_SEND); assertEquals(mA11yWindowManager.getWindowListLocked(displayId).size(), - windowInfosForDisplay.size()); + windowsForDisplay.size()); } private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) { ArgumentCaptor<WindowsForAccessibilityCallback> windowsForAccessibilityCallbacksCaptor = ArgumentCaptor.forClass( - WindowManagerInternal.WindowsForAccessibilityCallback.class); + WindowsForAccessibilityCallback.class); verify(mMockWindowManagerInternal) .setWindowsForAccessibilityCallback(eq(displayId), windowsForAccessibilityCallbacksCaptor.capture()); @@ -1106,36 +1240,28 @@ public class AccessibilityWindowManagerTest { return windowId; } - private void addWindowInfo(ArrayList<WindowInfo> windowInfos, IWindow windowToken, int layer) { - final WindowInfo windowInfo = WindowInfo.obtain(); - windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; - windowInfo.token = windowToken.asBinder(); - windowInfo.layer = layer; - windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - windowInfos.add(windowInfo); - } - private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) { - final IBinder windowToken = mWindowInfos.get(displayId).get(index).token; + final IBinder windowToken = mWindows.get(displayId).get(index).getWindowInfo().token; return mA11yWindowManager.findWindowIdLocked( USER_SYSTEM_ID, windowToken); } private void setTopFocusedWindowAndDisplay(int displayId, int index) { // Sets the top focus window. - mTopFocusedWindowToken = mWindowInfos.get(displayId).get(index).token; + mTopFocusedWindowToken = mWindows.get(displayId).get(index).getWindowInfo().token; // Sets the top focused display. mTopFocusedDisplayId = displayId; } - private void onWindowsForAccessibilityChanged(int displayId, boolean forceSend) { + private void onAccessibilityWindowsChanged(int displayId, boolean forceSend) { WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId); if (callbacks == null) { callbacks = getWindowsForAccessibilityCallbacks(displayId); mCallbackOfWindows.put(displayId, callbacks); } - callbacks.onWindowsForAccessibilityChanged(forceSend, mTopFocusedDisplayId, - mTopFocusedWindowToken, mWindowInfos.get(displayId)); + callbacks.onAccessibilityWindowsChanged(forceSend, mTopFocusedDisplayId, + mTopFocusedWindowToken, new Point(SCREEN_WIDTH, SCREEN_HEIGHT), + mWindows.get(displayId)); } private void changeFocusedWindowOnDisplayPerDisplayFocusConfig( @@ -1144,23 +1270,23 @@ public class AccessibilityWindowManagerTest { if (mSupportPerDisplayFocus) { // Gets the old focused window of display which wants to change focused window. WindowInfo focusedWindowInfo = - mWindowInfos.get(changeFocusedDisplayId).get(oldFocusedWindowIndex); + mWindows.get(changeFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo(); // Resets the focus of old focused window. focusedWindowInfo.focused = false; // Gets the new window of display which wants to change focused window. focusedWindowInfo = - mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex); + mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo(); // Sets the focus of new focused window. focusedWindowInfo.focused = true; } else { // Gets the window of display which wants to change focused window. WindowInfo focusedWindowInfo = - mWindowInfos.get(changeFocusedDisplayId).get(newFocusedWindowIndex); + mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo(); // Sets the focus of new focused window. focusedWindowInfo.focused = true; // Gets the old focused window of old top focused display. focusedWindowInfo = - mWindowInfos.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex); + mWindows.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo(); // Resets the focus of old focused window. focusedWindowInfo.focused = false; // Changes the top focused display and window. @@ -1168,6 +1294,39 @@ public class AccessibilityWindowManagerTest { } } + private AccessibilityWindow createMockAccessibilityWindow(IWindow windowToken, int displayId) { + final WindowInfo windowInfo = WindowInfo.obtain(); + windowInfo.type = WindowManager.LayoutParams.TYPE_APPLICATION; + windowInfo.token = windowToken.asBinder(); + + final AccessibilityWindow window = Mockito.mock(AccessibilityWindow.class); + when(window.getWindowInfo()).thenReturn(windowInfo); + when(window.isFocused()).thenAnswer(invocation -> windowInfo.focused); + when(window.isTouchable()).thenReturn(true); + when(window.getType()).thenReturn(windowInfo.type); + + setRegionForMockAccessibilityWindow(window, nextToucableRegion(displayId)); + return window; + } + + private void setRegionForMockAccessibilityWindow(AccessibilityWindow window, Region region) { + doAnswer(invocation -> { + ((Region) invocation.getArgument(0)).set(region); + return null; + }).when(window).getTouchableRegionInScreen(any(Region.class)); + doAnswer(invocation -> { + ((Region) invocation.getArgument(0)).set(region); + return null; + }).when(window).getTouchableRegionInWindow(any(Region.class)); + } + + private Region nextToucableRegion(int displayId) { + final int topLeft = mNextRegionOffsets.get(displayId, 0); + final int bottomRight = topLeft + 100; + mNextRegionOffsets.put(displayId, topLeft + 10); + return new Region(topLeft, topLeft, bottomRight, bottomRight); + } + @Nullable private static String toString(@Nullable CharSequence cs) { return cs == null ? null : cs.toString(); @@ -1196,16 +1355,16 @@ public class AccessibilityWindowManagerTest { } } - static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> { + static class EventWindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> { private int mWindowId; - WindowIdMatcher(int windowId) { + EventWindowIdMatcher(int windowId) { super(); mWindowId = windowId; } - static WindowIdMatcher a11yWindowId(int windowId) { - return new WindowIdMatcher(windowId); + static EventWindowIdMatcher eventWindowId(int windowId) { + return new EventWindowIdMatcher(windowId); } @Override @@ -1241,5 +1400,27 @@ public class AccessibilityWindowManagerTest { description.appendText("Matching to window changes " + mWindowChanges); } } + + static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityWindowInfo> { + private final int mWindowId; + + WindowIdMatcher(int windowId) { + super(); + mWindowId = windowId; + } + + static WindowIdMatcher windowId(int windowId) { + return new WindowIdMatcher(windowId); + } + + @Override + protected boolean matchesSafely(AccessibilityWindowInfo window) { + return window.getId() == mWindowId; + } + + @Override + public void describeTo(Description description) { + description.appendText("Matching to windowId " + mWindowId); + } + } } -// LINT.ThenChange(/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java) diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java deleted file mode 100644 index 19041451c8eb..000000000000 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java +++ /dev/null @@ -1,1444 +0,0 @@ -/* - * 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 com.android.server.accessibility; - -import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT; -import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.DisplayIdMatcher.displayId; -import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.WindowIdMatcher.windowId; -import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.WindowChangesMatcher.a11yWindowChanges; -import static com.android.server.accessibility.AccessibilityWindowManagerWithAccessibilityWindowTest.EventWindowIdMatcher.eventWindowId; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.assertTrue; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.annotation.Nullable; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.Region; -import android.os.IBinder; -import android.os.LocaleList; -import android.os.RemoteException; -import android.os.UserHandle; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; -import android.util.SparseArray; -import android.view.Display; -import android.view.IWindow; -import android.view.WindowInfo; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.AccessibilityWindowAttributes; -import android.view.accessibility.AccessibilityWindowInfo; -import android.view.accessibility.IAccessibilityInteractionConnection; - -import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; -import com.android.server.accessibility.test.MessageCapturingHandler; -import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow; -import com.android.server.wm.WindowManagerInternal; -import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; - -import org.hamcrest.Description; -import org.hamcrest.TypeSafeMatcher; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Tests for the AccessibilityWindowManager with Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2 - * TODO(b/322444245): Merge with AccessibilityWindowManagerTest - * after completing the flag migration. - */ -@RequiresFlagsEnabled(Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y_V2) -public class AccessibilityWindowManagerWithAccessibilityWindowTest { - private static final String PACKAGE_NAME = "com.android.server.accessibility"; - private static final boolean FORCE_SEND = true; - private static final boolean SEND_ON_WINDOW_CHANGES = false; - private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM; - private static final int USER_PROFILE = 11; - private static final int USER_PROFILE_PARENT = 1; - private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1; - private static final int NUM_GLOBAL_WINDOWS = 4; - private static final int NUM_APP_WINDOWS = 4; - private static final int NUM_OF_WINDOWS = (NUM_GLOBAL_WINDOWS + NUM_APP_WINDOWS); - private static final int DEFAULT_FOCUSED_INDEX = 1; - private static final int SCREEN_WIDTH = 1080; - private static final int SCREEN_HEIGHT = 1920; - private static final int INVALID_ID = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; - private static final int HOST_WINDOW_ID = 10; - private static final int EMBEDDED_WINDOW_ID = 11; - private static final int OTHER_WINDOW_ID = 12; - - private AccessibilityWindowManager mA11yWindowManager; - // Window manager will support multiple focused window if config_perDisplayFocusEnabled is true, - // i.e., each display would have its current focused window, and one of all focused windows - // would be top focused window. Otherwise, window manager only supports one focused window - // at all displays, and that focused window would be top focused window. - private boolean mSupportPerDisplayFocus = false; - private int mTopFocusedDisplayId = Display.INVALID_DISPLAY; - private IBinder mTopFocusedWindowToken = null; - - // List of window token, mapping from windowId -> window token. - private final SparseArray<IWindow> mA11yWindowTokens = new SparseArray<>(); - // List of window info lists, mapping from displayId -> a11y window lists. - private final SparseArray<ArrayList<AccessibilityWindow>> mWindows = new SparseArray<>(); - // List of callback, mapping from displayId -> callback. - private final SparseArray<WindowsForAccessibilityCallback> mCallbackOfWindows = - new SparseArray<>(); - // List of display ID. - private final ArrayList<Integer> mExpectedDisplayList = new ArrayList<>(Arrays.asList( - Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID)); - - private final MessageCapturingHandler mHandler = new MessageCapturingHandler(null); - - // This maps displayId -> next region offset. - // Touchable region must have un-occluded area so that it's exposed to a11y services. - // This offset can be used as left and top of new region so that top-left of each region are - // kept visible. - // It's expected to be incremented by some amount everytime the value is used. - private final SparseArray<Integer> mNextRegionOffsets = new SparseArray<>(); - - @Mock - private WindowManagerInternal mMockWindowManagerInternal; - @Mock - private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender; - @Mock - private AccessibilitySecurityPolicy mMockA11ySecurityPolicy; - @Mock - private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager; - @Mock - private AccessibilityTraceManager mMockA11yTraceManager; - - @Mock - private IBinder mMockHostToken; - @Mock - private IBinder mMockEmbeddedToken; - @Mock - private IBinder mMockInvalidToken; - - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - when(mMockA11yUserManager.getCurrentUserIdLocked()).thenReturn(USER_SYSTEM_ID); - when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( - USER_PROFILE)).thenReturn(USER_PROFILE_PARENT); - when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked( - USER_SYSTEM_ID)).thenReturn(USER_SYSTEM_ID); - when(mMockA11ySecurityPolicy.resolveValidReportedPackageLocked( - anyString(), anyInt(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME); - - doAnswer((invocation) -> { - onAccessibilityWindowsChanged(invocation.getArgument(0), false); - return null; - }).when(mMockWindowManagerInternal).computeWindowsForAccessibility(anyInt()); - - mA11yWindowManager = new AccessibilityWindowManager(new Object(), mHandler, - mMockWindowManagerInternal, - mMockA11yEventSender, - mMockA11ySecurityPolicy, - mMockA11yUserManager, - mMockA11yTraceManager); - // Starts tracking window of default display and sets the default display - // as top focused display before each testing starts. - startTrackingPerDisplay(Display.DEFAULT_DISPLAY); - - // AccessibilityEventSender is invoked during onAccessibilityWindowsChanged. - // Resets it for mockito verify of further test case. - Mockito.reset(mMockA11yEventSender); - - registerLeashedTokenAndWindowId(); - } - - @After - public void tearDown() { - mHandler.removeAllMessages(); - } - - @Test - public void startTrackingWindows_shouldEnableWindowManagerCallback() { - // AccessibilityWindowManager#startTrackingWindows already invoked in setup. - assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); - final WindowsForAccessibilityCallback callbacks = - mCallbackOfWindows.get(Display.DEFAULT_DISPLAY); - verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( - eq(Display.DEFAULT_DISPLAY), eq(callbacks)); - } - - @Test - public void stopTrackingWindows_shouldDisableWindowManagerCallback() { - assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); - Mockito.reset(mMockWindowManagerInternal); - - mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); - assertFalse(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); - verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( - eq(Display.DEFAULT_DISPLAY), isNull()); - - } - - @Test - public void stopTrackingWindows_shouldClearWindows() { - assertTrue(mA11yWindowManager.isTrackingWindowsLocked(Display.DEFAULT_DISPLAY)); - final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - - mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); - assertNull(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY)); - assertEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), - AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); - assertEquals(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), - activeWindowId); - } - - @Test - public void stopTrackingWindows_onNonTopFocusedDisplay_shouldNotResetTopFocusWindow() - throws RemoteException { - // At setup, the default display sets be the top focused display and - // its current focused window sets be the top focused window. - // Starts tracking window of second display. - startTrackingPerDisplay(SECONDARY_DISPLAY_ID); - assertTrue(mA11yWindowManager.isTrackingWindowsLocked(SECONDARY_DISPLAY_ID)); - // Stops tracking windows of second display. - mA11yWindowManager.stopTrackingWindows(SECONDARY_DISPLAY_ID); - assertNotEquals(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), - AccessibilityWindowInfo.UNDEFINED_WINDOW_ID); - } - - @Test - public void onWindowsChanged_duringTouchInteractAndFocusChange_shouldChangeActiveWindow() { - final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - final WindowInfo focusedWindowInfo = - mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo(); - assertEquals(activeWindowId, mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, focusedWindowInfo.token)); - - focusedWindowInfo.focused = false; - mWindows.get(Display.DEFAULT_DISPLAY).get( - DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().focused = true; - - mA11yWindowManager.onTouchInteractionStart(); - setTopFocusedWindowAndDisplay(Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX + 1); - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); - } - - @Test - public void - onWindowsChanged_focusChangeOnNonTopFocusedDisplay_perDisplayFocusOn_notChangeWindow() - throws RemoteException { - // At setup, the default display sets be the top focused display and - // its current focused window sets be the top focused window. - // Sets supporting multiple focused window, i.e., config_perDisplayFocusEnabled is true. - mSupportPerDisplayFocus = true; - // Starts tracking window of second display. - startTrackingPerDisplay(SECONDARY_DISPLAY_ID); - // Gets the active window. - final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - // Gets the top focused window. - final int topFocusedWindowId = - mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT); - // Changes the current focused window at second display. - changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, - DEFAULT_FOCUSED_INDEX + 1, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); - - onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); - // The active window should not be changed. - assertEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); - // The top focused window should not be changed. - assertEquals(topFocusedWindowId, - mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT)); - } - - @Test - public void - onWindowChange_focusChangeToNonTopFocusedDisplay_perDisplayFocusOff_shouldChangeWindow() - throws RemoteException { - // At setup, the default display sets be the top focused display and - // its current focused window sets be the top focused window. - // Sets not supporting multiple focused window, i.e., config_perDisplayFocusEnabled is - // false. - mSupportPerDisplayFocus = false; - // Starts tracking window of second display. - startTrackingPerDisplay(SECONDARY_DISPLAY_ID); - // Gets the active window. - final int activeWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - // Gets the top focused window. - final int topFocusedWindowId = - mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT); - // Changes the current focused window from default display to second display. - changeFocusedWindowOnDisplayPerDisplayFocusConfig(SECONDARY_DISPLAY_ID, - DEFAULT_FOCUSED_INDEX, Display.DEFAULT_DISPLAY, DEFAULT_FOCUSED_INDEX); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - onAccessibilityWindowsChanged(SECONDARY_DISPLAY_ID, SEND_ON_WINDOW_CHANGES); - // The active window should be changed. - assertNotEquals(activeWindowId, mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID)); - // The top focused window should be changed. - assertNotEquals(topFocusedWindowId, - mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT)); - } - - @Test - public void onWindowsChanged_shouldReportCorrectLayer() { - // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup. - List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - for (int i = 0; i < a11yWindows.size(); i++) { - final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); - assertThat(mWindows.get(Display.DEFAULT_DISPLAY).size() - i - 1, - is(a11yWindow.getLayer())); - } - } - - @Test - public void onWindowsChanged_shouldReportCorrectOrder() { - // AccessibilityWindowManager#onAccessibilityWindowsChanged already invoked in setup. - List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - for (int i = 0; i < a11yWindows.size(); i++) { - final AccessibilityWindowInfo a11yWindow = a11yWindows.get(i); - final IBinder windowToken = mA11yWindowManager - .getWindowTokenForUserAndWindowIdLocked(USER_SYSTEM_ID, a11yWindow.getId()); - final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY) - .get(i).getWindowInfo(); - assertThat(windowToken, is(windowInfo.token)); - } - } - - @Test - public void onWindowsChanged_shouldNotReportNonTouchableWindow() { - final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - when(window.isTouchable()).thenReturn(false); - final int windowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, window.getWindowInfo().token); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, not(hasItem(windowId(windowId)))); - } - - @Test - public void onWindowsChanged_shouldReportFocusedNonTouchableWindow() { - final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get( - DEFAULT_FOCUSED_INDEX); - when(window.isTouchable()).thenReturn(false); - final int windowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, window.getWindowInfo().token); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasItem(windowId(windowId))); - } - - @Test - public void onWindowsChanged_trustedFocusedNonTouchableWindow_shouldNotHideWindowsBelow() { - // Make the focused trusted un-touchable window fullscreen. - final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get( - DEFAULT_FOCUSED_INDEX); - setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - when(window.isTouchable()).thenReturn(false); - when(window.isTrustedOverlay()).thenReturn(true); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS)); - } - - @Test - public void onWindowsChanged_accessibilityOverlay_shouldNotHideWindowsBelow() { - // Make the a11y overlay window fullscreen. - final AccessibilityWindow window = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(window, new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - when(window.getType()).thenReturn(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(NUM_OF_WINDOWS)); - } - - @Test - public void onWindowsChanged_shouldReportFocusedWindowEvenIfOccluded() { - // Make the front window fullscreen. - final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(frontWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - final int frontWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, frontWindow.getWindowInfo().token); - - final AccessibilityWindow focusedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get( - DEFAULT_FOCUSED_INDEX); - final int focusedWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, focusedWindow.getWindowInfo().token); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(2)); - assertThat(a11yWindows.get(0), windowId(frontWindowId)); - assertThat(a11yWindows.get(1), windowId(focusedWindowId)); - } - - @Test - public void onWindowsChanged_embeddedWindows_shouldOnlyReportHost() throws RemoteException { - final Rect embeddingBounds = new Rect(0, 0, 200, 100); - - // The embedded window comes front of the host window. - final IBinder embeddedWindowLeashToken = Mockito.mock(IBinder.class); - final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, embeddedWindowLeashToken, USER_SYSTEM_ID); - final AccessibilityWindow embeddedWindow = createMockAccessibilityWindow( - mA11yWindowTokens.get(embeddedWindowId), Display.DEFAULT_DISPLAY); - setRegionForMockAccessibilityWindow(embeddedWindow, new Region(embeddingBounds)); - mWindows.get(Display.DEFAULT_DISPLAY).set(0, embeddedWindow); - - final IBinder hostWindowLeashToken = Mockito.mock(IBinder.class); - final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, hostWindowLeashToken, USER_SYSTEM_ID); - final AccessibilityWindow hostWindow = createMockAccessibilityWindow( - mA11yWindowTokens.get(hostWindowId), Display.DEFAULT_DISPLAY); - setRegionForMockAccessibilityWindow(hostWindow, new Region(embeddingBounds)); - mWindows.get(Display.DEFAULT_DISPLAY).set(1, hostWindow); - - mA11yWindowManager.associateEmbeddedHierarchyLocked( - hostWindowLeashToken, embeddedWindowLeashToken); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, not(hasItem(windowId(embeddedWindowId)))); - assertThat(a11yWindows.get(0), windowId(hostWindowId)); - final Rect bounds = new Rect(); - a11yWindows.get(0).getBoundsInScreen(bounds); - assertEquals(bounds, embeddingBounds); - } - - @Test - public void onWindowsChanged_shouldNotReportfullyOccludedWindow() { - final AccessibilityWindow frontWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(frontWindow, new Region(100, 100, 300, 300)); - final int frontWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, frontWindow.getWindowInfo().token); - - // index 1 is focused. Let's use the next one for this test. - final AccessibilityWindow occludedWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(2); - setRegionForMockAccessibilityWindow(occludedWindow, new Region(150, 150, 250, 250)); - final int occludedWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, occludedWindow.getWindowInfo().token); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasItem(windowId(frontWindowId))); - assertThat(a11yWindows, not(hasItem(windowId(occludedWindowId)))); - } - - @Test - public void onWindowsChangedAndForceSend_shouldUpdateWindows() { - assertNotEquals("new title", - toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) - .get(0).getTitle())); - - mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().title = "new title"; - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); - assertEquals("new title", - toString(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY) - .get(0).getTitle())); - } - - @Test - public void onWindowsChangedNoForceSend_windowChanged_shouldUpdateWindows() - throws RemoteException { - final AccessibilityWindowInfo oldWindow = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0); - final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - true, USER_SYSTEM_ID); - mWindows.get(Display.DEFAULT_DISPLAY).set(0, - createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY)); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - assertNotEquals(oldWindow, - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0)); - } - - @Test - public void onWindowsChangedNoForceSend_focusChanged_shouldUpdateWindows() { - final WindowInfo focusedWindowInfo = - mWindows.get(Display.DEFAULT_DISPLAY).get(DEFAULT_FOCUSED_INDEX).getWindowInfo(); - final WindowInfo windowInfo = mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo(); - focusedWindowInfo.focused = false; - windowInfo.focused = true; - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - assertTrue(mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY).get(0) - .isFocused()); - } - - @Test - public void removeAccessibilityInteractionConnection_byWindowToken_shouldRemoved() { - for (int i = 0; i < NUM_OF_WINDOWS; i++) { - final int windowId = mA11yWindowTokens.keyAt(i); - final IWindow windowToken = mA11yWindowTokens.valueAt(i); - assertNotNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); - - mA11yWindowManager.removeAccessibilityInteractionConnection(windowToken); - assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); - } - } - - @Test - public void remoteAccessibilityConnection_binderDied_shouldRemoveConnection() { - for (int i = 0; i < NUM_OF_WINDOWS; i++) { - final int windowId = mA11yWindowTokens.keyAt(i); - final RemoteAccessibilityConnection remoteA11yConnection = - mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId); - assertNotNull(remoteA11yConnection); - - remoteA11yConnection.binderDied(); - assertNull(mA11yWindowManager.getConnectionLocked(USER_SYSTEM_ID, windowId)); - } - } - - @Test - public void getWindowTokenForUserAndWindowId_shouldNotNull() { - final List<AccessibilityWindowInfo> windows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - for (int i = 0; i < windows.size(); i++) { - final int windowId = windows.get(i).getId(); - - assertNotNull(mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked( - USER_SYSTEM_ID, windowId)); - } - } - - @Test - public void findWindowId() { - final List<AccessibilityWindowInfo> windows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - for (int i = 0; i < windows.size(); i++) { - final int windowId = windows.get(i).getId(); - final IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked( - USER_SYSTEM_ID, windowId); - - assertEquals(mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, windowToken), windowId); - } - } - - @Test - public void resolveParentWindowId_windowIsNotEmbedded_shouldReturnGivenId() - throws RemoteException { - final int windowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, false, - Mockito.mock(IBinder.class), USER_SYSTEM_ID); - assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId)); - } - - @Test - public void resolveParentWindowId_windowIsNotRegistered_shouldReturnGivenId() { - final int windowId = -1; - assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId)); - } - - @Test - public void resolveParentWindowId_windowIsAssociated_shouldReturnParentWindowId() - throws RemoteException { - final IBinder mockHostToken = Mockito.mock(IBinder.class); - final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class); - final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, mockHostToken, USER_SYSTEM_ID); - final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, mockEmbeddedToken, USER_SYSTEM_ID); - - mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken); - - final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked( - embeddedWindowId); - assertEquals(hostWindowId, resolvedWindowId); - } - - @Test - public void resolveParentWindowId_windowIsDisassociated_shouldReturnGivenId() - throws RemoteException { - final IBinder mockHostToken = Mockito.mock(IBinder.class); - final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class); - final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, mockHostToken, USER_SYSTEM_ID); - final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, mockEmbeddedToken, USER_SYSTEM_ID); - - mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken); - mA11yWindowManager.disassociateEmbeddedHierarchyLocked(mockEmbeddedToken); - - final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked( - embeddedWindowId); - assertNotEquals(hostWindowId, resolvedWindowId); - assertEquals(embeddedWindowId, resolvedWindowId); - } - - @Test - public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() { - // Updates top 2 z-order WindowInfo are whole visible. - final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(firstWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2)); - final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); - setRegionForMockAccessibilityWindow(secondWindow, - new Region(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT)); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(2)); - final Region outBounds = new Region(); - int windowId = a11yWindows.get(0).getId(); - - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); - assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); - assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); - - windowId = a11yWindows.get(1).getId(); - - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); - assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); - assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); - } - - @Test - public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() { - // Updates z-order #1 WindowInfo is half visible. - final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(firstWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2)); - final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); - setRegionForMockAccessibilityWindow(secondWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - final List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(2)); - final Region outBounds = new Region(); - int windowId = a11yWindows.get(1).getId(); - - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); - assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); - assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); - } - - @Test - public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() { - // z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible. - final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(firstWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - // Note that the second window is also exposed even if region is empty because it's focused. - assertThat(a11yWindows, hasSize(2)); - final Region outBounds = new Region(); - int windowId = a11yWindows.get(1).getId(); - - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); - assertTrue(outBounds.getBounds().isEmpty()); - } - - @Test - public void computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion() { - // Updates z-order #0 WindowInfo to have two interact-able areas. - final Region region = new Region(0, 0, SCREEN_WIDTH, 200); - region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION); - final AccessibilityWindow firstWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(0); - setRegionForMockAccessibilityWindow(firstWindow, region); - final AccessibilityWindow secondWindow = mWindows.get(Display.DEFAULT_DISPLAY).get(1); - setRegionForMockAccessibilityWindow(secondWindow, - new Region(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - final List<AccessibilityWindowInfo> a11yWindows = - mA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY); - assertThat(a11yWindows, hasSize(2)); - final Region outBounds = new Region(); - final int windowId = a11yWindows.get(1).getId(); - - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); - assertFalse(outBounds.getBounds().isEmpty()); - assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); - assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT - 400)); - } - - @Test - public void updateActiveAndA11yFocusedWindow_windowStateChangedEvent_noTracking_shouldUpdate() { - final IBinder eventWindowToken = - mWindows.get(Display.DEFAULT_DISPLAY) - .get(DEFAULT_FOCUSED_INDEX + 1).getWindowInfo().token; - final int eventWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, eventWindowToken); - when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates()) - .thenReturn(eventWindowToken); - - final int noUse = 0; - mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - noUse, - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, - noUse); - assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); - assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), - is(eventWindowId)); - } - - @Test - public void updateActiveAndA11yFocusedWindow_hoverEvent_touchInteract_shouldSetActiveWindow() { - final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, - DEFAULT_FOCUSED_INDEX + 1); - final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - assertThat(currentActiveWindowId, is(not(eventWindowId))); - - final int noUse = 0; - mA11yWindowManager.onTouchInteractionStart(); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - noUse, - AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, - noUse); - assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); - final ArgumentCaptor<AccessibilityEvent> captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(2)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(currentActiveWindowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); - assertThat(captor.getAllValues().get(1), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(eventWindowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); - } - - @Test - public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_shouldUpdateA11yFocus() { - final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, - DEFAULT_FOCUSED_INDEX); - final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); - assertThat(currentA11yFocusedWindowId, is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)); - - final int noUse = 0; - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, - noUse); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); - final ArgumentCaptor<AccessibilityEvent> captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(1)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(eventWindowId), - a11yWindowChanges( - AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); - } - - @Test - public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_defaultToSecondary() - throws RemoteException { - runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( - Display.DEFAULT_DISPLAY, SECONDARY_DISPLAY_ID); - } - - @Test - public void updateActiveAndA11yFocusedWindow_a11yFocusEvent_multiDisplay_SecondaryToDefault() - throws RemoteException { - runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( - SECONDARY_DISPLAY_ID, Display.DEFAULT_DISPLAY); - } - - private void runUpdateActiveAndA11yFocusedWindow_MultiDisplayTest( - int initialDisplayId, int eventDisplayId) throws RemoteException { - startTrackingPerDisplay(SECONDARY_DISPLAY_ID); - final int initialWindowId = getWindowIdFromWindowInfosForDisplay( - initialDisplayId, DEFAULT_FOCUSED_INDEX); - final int noUse = 0; - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - initialWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, - noUse); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(initialWindowId)); - Mockito.reset(mMockA11yEventSender); - - final int eventWindowId = getWindowIdFromWindowInfosForDisplay( - eventDisplayId, DEFAULT_FOCUSED_INDEX); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, - noUse); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); - final ArgumentCaptor<AccessibilityEvent> captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(2)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(initialDisplayId), - eventWindowId(initialWindowId), - a11yWindowChanges( - AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); - assertThat(captor.getAllValues().get(1), - allOf(displayId(eventDisplayId), - eventWindowId(eventWindowId), - a11yWindowChanges( - AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED))); - } - - @Test - public void updateActiveAndA11yFocusedWindow_clearA11yFocusEvent_shouldClearA11yFocus() { - final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, - DEFAULT_FOCUSED_INDEX); - final int currentA11yFocusedWindowId = mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); - assertThat(currentA11yFocusedWindowId, is(not(eventWindowId))); - - final int noUse = 0; - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, - noUse); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(eventWindowId)); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, - noUse); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), - is(AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)); - } - - @Test - public void onTouchInteractionEnd_shouldRollbackActiveWindow() { - final int eventWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, - DEFAULT_FOCUSED_INDEX + 1); - final int currentActiveWindowId = mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID); - assertThat(currentActiveWindowId, is(not(eventWindowId))); - - final int noUse = 0; - mA11yWindowManager.onTouchInteractionStart(); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - eventWindowId, - noUse, - AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, - noUse); - assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(eventWindowId)); - // AccessibilityEventSender is invoked after active window changed. Reset it. - Mockito.reset(mMockA11yEventSender); - - mA11yWindowManager.onTouchInteractionEnd(); - final ArgumentCaptor<AccessibilityEvent> captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(2)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(eventWindowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); - assertThat(captor.getAllValues().get(1), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(currentActiveWindowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ACTIVE))); - } - - @Test - public void onTouchInteractionEnd_noServiceInteractiveWindow_shouldClearA11yFocus() - throws RemoteException { - final IBinder defaultFocusWinToken = - mWindows.get(Display.DEFAULT_DISPLAY).get( - DEFAULT_FOCUSED_INDEX).getWindowInfo().token; - final int defaultFocusWindowId = mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, defaultFocusWinToken); - when(mMockWindowManagerInternal.getFocusedWindowTokenFromWindowStates()) - .thenReturn(defaultFocusWinToken); - final int newFocusWindowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, - DEFAULT_FOCUSED_INDEX + 1); - final IAccessibilityInteractionConnection mockNewFocusConnection = - mA11yWindowManager.getConnectionLocked( - USER_SYSTEM_ID, newFocusWindowId).getRemote(); - - mA11yWindowManager.stopTrackingWindows(Display.DEFAULT_DISPLAY); - final int noUse = 0; - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - defaultFocusWindowId, - noUse, - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, - noUse); - assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(defaultFocusWindowId)); - assertThat(mA11yWindowManager.getFocusedWindowId(AccessibilityNodeInfo.FOCUS_INPUT), - is(defaultFocusWindowId)); - - mA11yWindowManager.onTouchInteractionStart(); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - newFocusWindowId, - noUse, - AccessibilityEvent.TYPE_VIEW_HOVER_ENTER, - noUse); - mA11yWindowManager.updateActiveAndAccessibilityFocusedWindowLocked(USER_SYSTEM_ID, - newFocusWindowId, - AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, - noUse); - assertThat(mA11yWindowManager.getActiveWindowId(USER_SYSTEM_ID), is(newFocusWindowId)); - assertThat(mA11yWindowManager.getFocusedWindowId( - AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), is(newFocusWindowId)); - - mA11yWindowManager.onTouchInteractionEnd(); - mHandler.sendLastMessage(); - verify(mockNewFocusConnection).clearAccessibilityFocus(); - } - - @Test - public void getPictureInPictureWindow_shouldNotNull() { - assertNull(mA11yWindowManager.getPictureInPictureWindowLocked()); - mWindows.get(Display.DEFAULT_DISPLAY).get(1).getWindowInfo().inPictureInPicture = true; - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - assertNotNull(mA11yWindowManager.getPictureInPictureWindowLocked()); - } - - @Test - public void notifyOutsideTouch() throws RemoteException { - final int targetWindowId = - getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 1); - final int outsideWindowId = - getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - final IAccessibilityInteractionConnection mockRemoteConnection = - mA11yWindowManager.getConnectionLocked( - USER_SYSTEM_ID, outsideWindowId).getRemote(); - mWindows.get(Display.DEFAULT_DISPLAY).get(0).getWindowInfo().hasFlagWatchOutsideTouch = - true; - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, SEND_ON_WINDOW_CHANGES); - - mA11yWindowManager.notifyOutsideTouch(USER_SYSTEM_ID, targetWindowId); - verify(mockRemoteConnection).notifyOutsideTouch(); - } - - @Test - public void addAccessibilityInteractionConnection_profileUser_findInParentUser() - throws RemoteException { - final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, USER_PROFILE); - final int windowId = mA11yWindowManager.findWindowIdLocked( - USER_PROFILE_PARENT, token.asBinder()); - assertTrue(windowId >= 0); - } - - @Test - public void getDisplayList() throws RemoteException { - // Starts tracking window of second display. - startTrackingPerDisplay(SECONDARY_DISPLAY_ID); - - final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked( - DISPLAY_TYPE_DEFAULT); - assertTrue(displayList.equals(mExpectedDisplayList)); - } - - @Test - public void setAccessibilityWindowIdToSurfaceMetadata() - throws RemoteException { - final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - true, USER_SYSTEM_ID); - int windowId = -1; - for (int i = 0; i < mA11yWindowTokens.size(); i++) { - if (mA11yWindowTokens.valueAt(i).equals(token)) { - windowId = mA11yWindowTokens.keyAt(i); - } - } - assertNotEquals("Returned token is not found in mA11yWindowTokens", -1, windowId); - verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata( - token.asBinder(), windowId); - - mA11yWindowManager.removeAccessibilityInteractionConnection(token); - verify(mMockWindowManagerInternal, times(1)).setAccessibilityIdToSurfaceMetadata( - token.asBinder(), -1); - } - - @Test - public void getHostTokenLocked_hierarchiesAreAssociated_shouldReturnHostToken() { - mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken); - final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken); - assertEquals(hostToken, mMockHostToken); - } - - @Test - public void getHostTokenLocked_hierarchiesAreNotAssociated_shouldReturnNull() { - final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken); - assertNull(hostToken); - } - - @Test - public void getHostTokenLocked_embeddedHierarchiesAreDisassociated_shouldReturnNull() { - mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken); - mA11yWindowManager.disassociateLocked(mMockEmbeddedToken); - final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken); - assertNull(hostToken); - } - - @Test - public void getHostTokenLocked_hostHierarchiesAreDisassociated_shouldReturnNull() { - mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken); - mA11yWindowManager.disassociateLocked(mMockHostToken); - final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockHostToken); - assertNull(hostToken); - } - - @Test - public void getWindowIdLocked_windowIsRegistered_shouldReturnWindowId() { - final int windowId = mA11yWindowManager.getWindowIdLocked(mMockHostToken); - assertEquals(windowId, HOST_WINDOW_ID); - } - - @Test - public void getWindowIdLocked_windowIsNotRegistered_shouldReturnInvalidWindowId() { - final int windowId = mA11yWindowManager.getWindowIdLocked(mMockInvalidToken); - assertEquals(windowId, INVALID_ID); - } - - @Test - public void getTokenLocked_windowIsRegistered_shouldReturnToken() { - final IBinder token = mA11yWindowManager.getLeashTokenLocked(HOST_WINDOW_ID); - assertEquals(token, mMockHostToken); - } - - @Test - public void getTokenLocked_windowIsNotRegistered_shouldReturnNull() { - final IBinder token = mA11yWindowManager.getLeashTokenLocked(OTHER_WINDOW_ID); - assertNull(token); - } - - @Test - public void setAccessibilityWindowAttributes_windowIsNotRegistered_titleIsChanged() { - final int windowId = - getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); - layoutParams.accessibilityTitle = "accessibility window title"; - final AccessibilityWindowAttributes attributes = new AccessibilityWindowAttributes( - layoutParams, new LocaleList()); - - mA11yWindowManager.setAccessibilityWindowAttributes(Display.DEFAULT_DISPLAY, windowId, - USER_SYSTEM_ID, attributes); - - final AccessibilityWindowInfo a11yWindow = mA11yWindowManager.findA11yWindowInfoByIdLocked( - windowId); - assertEquals(toString(layoutParams.accessibilityTitle), toString(a11yWindow.getTitle())); - } - - @Test - public void sendAccessibilityEventOnWindowRemoval() { - final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY); - - // Removing index 0 because it's not focused, and avoids unnecessary layer change. - final int windowId = - getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - windows.remove(0); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); - - final ArgumentCaptor<AccessibilityEvent> captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(1)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(windowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_REMOVED))); - } - - @Test - public void sendAccessibilityEventOnWindowAddition() throws RemoteException { - final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY); - - final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, - false, USER_SYSTEM_ID); - // Adding window to the front so that other windows' layer won't change. - windows.add(0, createMockAccessibilityWindow(token, Display.DEFAULT_DISPLAY)); - final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); - - final ArgumentCaptor<AccessibilityEvent> captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(1)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(windowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_ADDED))); - } - - @Test - public void sendAccessibilityEventOnWindowChange() { - final ArrayList<AccessibilityWindow> windows = mWindows.get(Display.DEFAULT_DISPLAY); - windows.get(0).getWindowInfo().title = "new title"; - final int windowId = getWindowIdFromWindowInfosForDisplay(Display.DEFAULT_DISPLAY, 0); - - onAccessibilityWindowsChanged(Display.DEFAULT_DISPLAY, FORCE_SEND); - - final ArgumentCaptor<AccessibilityEvent> captor = - ArgumentCaptor.forClass(AccessibilityEvent.class); - verify(mMockA11yEventSender, times(1)) - .sendAccessibilityEventForCurrentUserLocked(captor.capture()); - assertThat(captor.getAllValues().get(0), - allOf(displayId(Display.DEFAULT_DISPLAY), - eventWindowId(windowId), - a11yWindowChanges(AccessibilityEvent.WINDOWS_CHANGE_TITLE))); - } - - private void registerLeashedTokenAndWindowId() { - mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID); - mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID); - } - - private void startTrackingPerDisplay(int displayId) throws RemoteException { - ArrayList<AccessibilityWindow> windowsForDisplay = new ArrayList<>(); - // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy - // mock window token into mA11yWindowTokens. Also, preparing WindowInfo mWindowInfos - // for the test. - for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) { - final IWindow token = addAccessibilityInteractionConnection(displayId, - true, USER_SYSTEM_ID); - windowsForDisplay.add(createMockAccessibilityWindow(token, displayId)); - - } - for (int i = 0; i < NUM_APP_WINDOWS; i++) { - final IWindow token = addAccessibilityInteractionConnection(displayId, - false, USER_SYSTEM_ID); - windowsForDisplay.add(createMockAccessibilityWindow(token, displayId)); - } - // Sets up current focused window of display. - // Each display has its own current focused window if config_perDisplayFocusEnabled is true. - // Otherwise only default display needs to current focused window. - if (mSupportPerDisplayFocus || displayId == Display.DEFAULT_DISPLAY) { - windowsForDisplay.get(DEFAULT_FOCUSED_INDEX).getWindowInfo().focused = true; - } - // Turns on windows tracking, and update window info. - mA11yWindowManager.startTrackingWindows(displayId, false); - // Puts window lists into array. - mWindows.put(displayId, windowsForDisplay); - // Sets the default display is the top focused display and - // its current focused window is the top focused window. - if (displayId == Display.DEFAULT_DISPLAY) { - setTopFocusedWindowAndDisplay(displayId, DEFAULT_FOCUSED_INDEX); - } - // Invokes callback for sending window lists to A11y framework. - onAccessibilityWindowsChanged(displayId, FORCE_SEND); - - assertEquals(mA11yWindowManager.getWindowListLocked(displayId).size(), - windowsForDisplay.size()); - } - - private WindowsForAccessibilityCallback getWindowsForAccessibilityCallbacks(int displayId) { - ArgumentCaptor<WindowsForAccessibilityCallback> windowsForAccessibilityCallbacksCaptor = - ArgumentCaptor.forClass( - WindowsForAccessibilityCallback.class); - verify(mMockWindowManagerInternal) - .setWindowsForAccessibilityCallback(eq(displayId), - windowsForAccessibilityCallbacksCaptor.capture()); - return windowsForAccessibilityCallbacksCaptor.getValue(); - } - - private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal, - int userId) throws RemoteException { - final IWindow mockWindowToken = Mockito.mock(IWindow.class); - final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock( - IAccessibilityInteractionConnection.class); - final IBinder mockConnectionBinder = Mockito.mock(IBinder.class); - final IBinder mockWindowBinder = Mockito.mock(IBinder.class); - final IBinder mockLeashToken = Mockito.mock(IBinder.class); - when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder); - when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder); - when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId)) - .thenReturn(bGlobal); - when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder)) - .thenReturn(displayId); - - int windowId = mA11yWindowManager.addAccessibilityInteractionConnection( - mockWindowToken, mockLeashToken, mockA11yConnection, PACKAGE_NAME, userId); - mA11yWindowTokens.put(windowId, mockWindowToken); - return mockWindowToken; - } - - private int addAccessibilityInteractionConnection(int displayId, boolean bGlobal, - IBinder leashToken, int userId) throws RemoteException { - final IWindow mockWindowToken = Mockito.mock(IWindow.class); - final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock( - IAccessibilityInteractionConnection.class); - final IBinder mockConnectionBinder = Mockito.mock(IBinder.class); - final IBinder mockWindowBinder = Mockito.mock(IBinder.class); - when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder); - when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder); - when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId)) - .thenReturn(bGlobal); - when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder)) - .thenReturn(displayId); - - int windowId = mA11yWindowManager.addAccessibilityInteractionConnection( - mockWindowToken, leashToken, mockA11yConnection, PACKAGE_NAME, userId); - mA11yWindowTokens.put(windowId, mockWindowToken); - return windowId; - } - - private int getWindowIdFromWindowInfosForDisplay(int displayId, int index) { - final IBinder windowToken = mWindows.get(displayId).get(index).getWindowInfo().token; - return mA11yWindowManager.findWindowIdLocked( - USER_SYSTEM_ID, windowToken); - } - - private void setTopFocusedWindowAndDisplay(int displayId, int index) { - // Sets the top focus window. - mTopFocusedWindowToken = mWindows.get(displayId).get(index).getWindowInfo().token; - // Sets the top focused display. - mTopFocusedDisplayId = displayId; - } - - private void onAccessibilityWindowsChanged(int displayId, boolean forceSend) { - WindowsForAccessibilityCallback callbacks = mCallbackOfWindows.get(displayId); - if (callbacks == null) { - callbacks = getWindowsForAccessibilityCallbacks(displayId); - mCallbackOfWindows.put(displayId, callbacks); - } - callbacks.onAccessibilityWindowsChanged(forceSend, mTopFocusedDisplayId, - mTopFocusedWindowToken, new Point(SCREEN_WIDTH, SCREEN_HEIGHT), - mWindows.get(displayId)); - } - - private void changeFocusedWindowOnDisplayPerDisplayFocusConfig( - int changeFocusedDisplayId, int newFocusedWindowIndex, int oldTopFocusedDisplayId, - int oldFocusedWindowIndex) { - if (mSupportPerDisplayFocus) { - // Gets the old focused window of display which wants to change focused window. - WindowInfo focusedWindowInfo = - mWindows.get(changeFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo(); - // Resets the focus of old focused window. - focusedWindowInfo.focused = false; - // Gets the new window of display which wants to change focused window. - focusedWindowInfo = - mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo(); - // Sets the focus of new focused window. - focusedWindowInfo.focused = true; - } else { - // Gets the window of display which wants to change focused window. - WindowInfo focusedWindowInfo = - mWindows.get(changeFocusedDisplayId).get(newFocusedWindowIndex).getWindowInfo(); - // Sets the focus of new focused window. - focusedWindowInfo.focused = true; - // Gets the old focused window of old top focused display. - focusedWindowInfo = - mWindows.get(oldTopFocusedDisplayId).get(oldFocusedWindowIndex).getWindowInfo(); - // Resets the focus of old focused window. - focusedWindowInfo.focused = false; - // Changes the top focused display and window. - setTopFocusedWindowAndDisplay(changeFocusedDisplayId, newFocusedWindowIndex); - } - } - - private AccessibilityWindow createMockAccessibilityWindow(IWindow windowToken, int displayId) { - final WindowInfo windowInfo = WindowInfo.obtain(); - windowInfo.type = WindowManager.LayoutParams.TYPE_APPLICATION; - windowInfo.token = windowToken.asBinder(); - - final AccessibilityWindow window = Mockito.mock(AccessibilityWindow.class); - when(window.getWindowInfo()).thenReturn(windowInfo); - when(window.isFocused()).thenAnswer(invocation -> windowInfo.focused); - when(window.isTouchable()).thenReturn(true); - when(window.getType()).thenReturn(windowInfo.type); - - setRegionForMockAccessibilityWindow(window, nextToucableRegion(displayId)); - return window; - } - - private void setRegionForMockAccessibilityWindow(AccessibilityWindow window, Region region) { - doAnswer(invocation -> { - ((Region) invocation.getArgument(0)).set(region); - return null; - }).when(window).getTouchableRegionInScreen(any(Region.class)); - doAnswer(invocation -> { - ((Region) invocation.getArgument(0)).set(region); - return null; - }).when(window).getTouchableRegionInWindow(any(Region.class)); - } - - private Region nextToucableRegion(int displayId) { - final int topLeft = mNextRegionOffsets.get(displayId, 0); - final int bottomRight = topLeft + 100; - mNextRegionOffsets.put(displayId, topLeft + 10); - return new Region(topLeft, topLeft, bottomRight, bottomRight); - } - - @Nullable - private static String toString(@Nullable CharSequence cs) { - return cs == null ? null : cs.toString(); - } - - static class DisplayIdMatcher extends TypeSafeMatcher<AccessibilityEvent> { - private final int mDisplayId; - - DisplayIdMatcher(int displayId) { - super(); - mDisplayId = displayId; - } - - static DisplayIdMatcher displayId(int displayId) { - return new DisplayIdMatcher(displayId); - } - - @Override - protected boolean matchesSafely(AccessibilityEvent event) { - return event.getDisplayId() == mDisplayId; - } - - @Override - public void describeTo(Description description) { - description.appendText("Matching to displayId " + mDisplayId); - } - } - - static class EventWindowIdMatcher extends TypeSafeMatcher<AccessibilityEvent> { - private int mWindowId; - - EventWindowIdMatcher(int windowId) { - super(); - mWindowId = windowId; - } - - static EventWindowIdMatcher eventWindowId(int windowId) { - return new EventWindowIdMatcher(windowId); - } - - @Override - protected boolean matchesSafely(AccessibilityEvent event) { - return event.getWindowId() == mWindowId; - } - - @Override - public void describeTo(Description description) { - description.appendText("Matching to windowId " + mWindowId); - } - } - - static class WindowChangesMatcher extends TypeSafeMatcher<AccessibilityEvent> { - private int mWindowChanges; - - WindowChangesMatcher(int windowChanges) { - super(); - mWindowChanges = windowChanges; - } - - static WindowChangesMatcher a11yWindowChanges(int windowChanges) { - return new WindowChangesMatcher(windowChanges); - } - - @Override - protected boolean matchesSafely(AccessibilityEvent event) { - return event.getWindowChanges() == mWindowChanges; - } - - @Override - public void describeTo(Description description) { - description.appendText("Matching to window changes " + mWindowChanges); - } - } - - static class WindowIdMatcher extends TypeSafeMatcher<AccessibilityWindowInfo> { - private final int mWindowId; - - WindowIdMatcher(int windowId) { - super(); - mWindowId = windowId; - } - - static WindowIdMatcher windowId(int windowId) { - return new WindowIdMatcher(windowId); - } - - @Override - protected boolean matchesSafely(AccessibilityWindowInfo window) { - return window.getId() == mWindowId; - } - - @Override - public void describeTo(Description description) { - description.appendText("Matching to windowId " + mWindowId); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index 7e0c12a5a545..ac27a971102a 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -21,7 +21,6 @@ import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MOD import static com.android.server.accessibility.Flags.FLAG_MAGNIFICATION_ENLARGE_POINTER_BUGFIX; import static com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback; import static com.android.server.accessibility.magnification.MockMagnificationConnection.TEST_DISPLAY; -import static com.android.window.flags.Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -308,7 +307,6 @@ public class FullScreenMagnificationControllerTest { } @Test - @RequiresFlagsEnabled(FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) public void testSetScale_noConnection_doNothing() { register(TEST_DISPLAY); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index f0d3456a39de..c878799109dc 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -57,10 +57,6 @@ import android.hardware.display.DisplayManagerInternal; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; -import android.platform.test.annotations.RequiresFlagsDisabled; -import android.platform.test.annotations.RequiresFlagsEnabled; -import android.platform.test.flag.junit.CheckFlagsRule; -import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.test.mock.MockContentResolver; import android.testing.DexmakerShareClassLoaderRule; @@ -82,7 +78,6 @@ import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.test.MessageCapturingHandler; import com.android.server.input.InputManagerInternal; import com.android.server.wm.WindowManagerInternal; -import com.android.window.flags.Flags; import org.junit.After; import org.junit.Before; @@ -101,9 +96,6 @@ import org.mockito.stubbing.Answer; @RunWith(AndroidJUnit4.class) public class MagnificationControllerTest { - @Rule - public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; private static final int TEST_SERVICE_ID = 1; private static final Region INITIAL_SCREEN_MAGNIFICATION_REGION = @@ -1365,8 +1357,7 @@ public class MagnificationControllerTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) - public void onFullscreenMagnificationActivationState_systemUiBorderFlagOn_notifyConnection() { + public void onFullscreenMagnificationActivationState_notifyConnection() { mMagnificationController.onFullScreenMagnificationActivationState( TEST_DISPLAY, /* activated= */ true); @@ -1374,17 +1365,6 @@ public class MagnificationControllerTest { .onFullscreenMagnificationActivationChanged(TEST_DISPLAY, /* activated= */ true); } - @Test - @RequiresFlagsDisabled(Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) - public void - onFullscreenMagnificationActivationState_systemUiBorderFlagOff_neverNotifyConnection() { - mMagnificationController.onFullScreenMagnificationActivationState( - TEST_DISPLAY, /* activated= */ true); - - verify(mMagnificationConnectionManager, never()) - .onFullscreenMagnificationActivationChanged(TEST_DISPLAY, /* activated= */ true); - } - private void setMagnificationEnabled(int mode) throws RemoteException { setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y); } 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 cf5dc4bec71c..30aa8cebdff6 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -4404,6 +4404,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAutoTimeEnabledModifiesSetting() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); @@ -4415,6 +4416,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetAutoTimeEnabledWithPOOnUser0() throws Exception { mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; setupProfileOwnerOnUser0(); @@ -4434,7 +4436,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test - @Ignore("b/359188869") + @Ignore("b/277916462") public void testSetAutoTimeEnabledWithPOOfOrganizationOwnedDevice() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); @@ -5034,8 +5036,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { @Test @RequiresFlagsEnabled(Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED) public void testSetSecondaryLockscreenEnabled() throws Exception { - mContext.binder.callingUid = DpmMockContext.CALLER_UID; - verifySetSecondaryLockscreenEnabled(false); verifySetSecondaryLockscreenEnabled(true); } @@ -5043,6 +5043,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { private void verifySetSecondaryLockscreenEnabled(boolean enabled) throws Exception { reset(getServices().supervisionManagerInternal); + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + doReturn(DpmMockContext.CALLER_UID).when(getServices().packageManagerInternal) + .getPackageUid(any(), anyLong(), anyInt()); + dpm.setSecondaryLockscreenEnabled(admin1, enabled); verify(getServices().supervisionManagerInternal).setSupervisionLockscreenEnabledForUser( CALLER_USER_HANDLE, enabled, null); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 0816e7b61165..5be4490e67ef 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -118,6 +118,7 @@ public class HdmiCecLocalDeviceTvTest { private boolean mDisableCecOnStandbyByLowEnergyMode; private boolean mWasCecDisabledOnStandbyByLowEnergyMode; private boolean mUseHdmiCecPowerStatusController; + private boolean mUserEnabledCecInOfflineMode; private class DeviceEventListener { private HdmiDeviceInfo mDevice; @@ -250,6 +251,11 @@ public class HdmiCecLocalDeviceTvTest { protected void setWasCecDisabledOnStandbyByLowEnergyMode(boolean value) { mWasCecDisabledOnStandbyByLowEnergyMode = value; } + + @Override + protected boolean userEnabledCecInOfflineMode() { + return mUserEnabledCecInOfflineMode; + } }; mHdmiControlService.setIoLooper(mMyLooper); @@ -298,6 +304,7 @@ public class HdmiCecLocalDeviceTvTest { mWasCecDisabledOnStandbyByLowEnergyMode = false; mDisableCecOnStandbyByLowEnergyMode = false; mUseHdmiCecPowerStatusController = false; + mUserEnabledCecInOfflineMode = false; mNativeWrapper.clearResultMessages(); } @@ -2400,6 +2407,32 @@ public class HdmiCecLocalDeviceTvTest { assertTrue(mVendorCommandListeners.contains(vendorCommandListenerInvocationSettingChange)); } + @Test + public void lowEnergyMode_userEnabledCecInOfflineMode_onStandby_cecStaysEnabled() { + mDisableCecOnStandbyByLowEnergyMode = true; + mUseHdmiCecPowerStatusController = true; + mUserEnabledCecInOfflineMode = true; + mPowerManager.setIsLowPowerStandbyEnabled(true); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + assertFalse(mWasCecDisabledOnStandbyByLowEnergyMode); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + assertFalse(mWasCecDisabledOnStandbyByLowEnergyMode); + } + protected static class MockTvDevice extends HdmiCecLocalDeviceTv { MockTvDevice(HdmiControlService service) { super(service); diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java index b2a7d20fb948..3ced56a04138 100644 --- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java @@ -67,10 +67,12 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.Rect; import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionCallback; import android.media.projection.IMediaProjectionWatcherCallback; import android.media.projection.ReviewGrantedConsentResult; +import android.media.projection.StopReason; import android.os.Binder; import android.os.IBinder; import android.os.Looper; @@ -549,7 +551,7 @@ public class MediaProjectionManagerServiceTest { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(service); - projection.stop(); + projection.stop(StopReason.STOP_UNKNOWN); verifyZeroInteractions(mMediaProjectionMetricsLogger); } @@ -562,10 +564,10 @@ public class MediaProjectionManagerServiceTest { startProjectionPreconditions(service); projection.start(mIMediaProjectionCallback); - projection.stop(); + final @StopReason int stopReason = StopReason.STOP_UNKNOWN; + projection.stop(stopReason); - verify(mMediaProjectionMetricsLogger) - .logStopped(UID, TARGET_UID_UNKNOWN); + verify(mMediaProjectionMetricsLogger).logStopped(UID, TARGET_UID_UNKNOWN, stopReason); } @Test @@ -580,15 +582,16 @@ public class MediaProjectionManagerServiceTest { .setContentRecordingSession(any(ContentRecordingSession.class)); service.setContentRecordingSession(DISPLAY_SESSION); - projection.stop(); + final @StopReason int stopReason = StopReason.STOP_UNKNOWN; + projection.stop(stopReason); - verify(mMediaProjectionMetricsLogger) - .logStopped(UID, TARGET_UID_FULL_SCREEN); + verify(mMediaProjectionMetricsLogger).logStopped(UID, TARGET_UID_FULL_SCREEN, stopReason); } @Test public void stop_taskSession_logsHostUidAndTargetUid() throws Exception { int targetUid = 1234; + int stopReason = StopReason.STOP_UNKNOWN; MediaProjectionManagerService service = new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector); MediaProjectionManagerService.MediaProjection projection = @@ -601,9 +604,9 @@ public class MediaProjectionManagerServiceTest { taskSession.setTargetUid(targetUid); service.setContentRecordingSession(taskSession); - projection.stop(); + projection.stop(stopReason); - verify(mMediaProjectionMetricsLogger).logStopped(UID, targetUid); + verify(mMediaProjectionMetricsLogger).logStopped(UID, targetUid, stopReason); } @Test @@ -638,7 +641,7 @@ public class MediaProjectionManagerServiceTest { projection.start(mIMediaProjectionCallback); assertThat(projection.isValid()).isTrue(); - projection.stop(); + projection.stop(StopReason.STOP_UNKNOWN); // Second start - so not valid. projection.start(mIMediaProjectionCallback); @@ -692,7 +695,7 @@ public class MediaProjectionManagerServiceTest { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions( service); projection.start(mIMediaProjectionCallback); - projection.stop(); + projection.stop(StopReason.STOP_UNKNOWN); // Second start - so not valid. projection.start(mIMediaProjectionCallback); @@ -708,7 +711,7 @@ public class MediaProjectionManagerServiceTest { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions( service); projection.start(mIMediaProjectionCallback); - projection.stop(); + projection.stop(StopReason.STOP_UNKNOWN); // Second start - so not valid. projection.start(mIMediaProjectionCallback); @@ -947,6 +950,26 @@ public class MediaProjectionManagerServiceTest { projection.uid, targetUid, WINDOWING_MODE_MULTI_WINDOW); } + @Test + public void notifyCaptureBoundsChanged_forwardsToLoggerAndResizeCallbacks() throws Exception { + int targetUid = 123; + mService = + new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector); + + ContentRecordingSession taskSession = createTaskSession(mock(IBinder.class)); + taskSession.setTargetUid(targetUid); + mService.setContentRecordingSession(taskSession); + + MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); + projection.start(mIMediaProjectionCallback); + + Rect newBounds = new Rect(0, 0, 1000, 2000); + mService.notifyCaptureBoundsChanged(RECORD_CONTENT_TASK, targetUid, newBounds); + + verify(mMediaProjectionMetricsLogger) + .logChangedCaptureBounds(RECORD_CONTENT_TASK, projection.uid, targetUid, newBounds); + } + /** * Executes and validates scenario where the consent result indicates the projection ends. */ diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java index 72ce9fe9a23f..c727bb6bbf7d 100644 --- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java @@ -32,6 +32,17 @@ import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_DEVICE_LOCK; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_ERROR; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_FOREGROUND_SERVICE_CHANGE; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_HOST_APP_STOP; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_MEDIA_ROUTE; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_PROJECTION; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_QS_TILE; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_STATUS_BAR_CHIP_STOP; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_TASK_APP_CLOSE; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_USER_SWITCH; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN; @@ -46,6 +57,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.media.projection.StopReason; import android.platform.test.annotations.Presubmit; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -81,6 +93,7 @@ public class MediaProjectionMetricsLoggerTest { private static final int TEST_WINDOWING_MODE = 987; private static final int TEST_CONTENT_TO_RECORD = 654; + private static final int TEST_STOP_SOURCE = 321; @Mock private FrameworkStatsLogWrapper mFrameworkStatsLogWrapper; @Mock private MediaProjectionSessionIdGenerator mSessionIdGenerator; @@ -136,6 +149,14 @@ public class MediaProjectionMetricsLoggerTest { } @Test + public void logInitiated_logsUnknownStopSource() { + mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE); + + verifyStopSourceLogged( + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); + } + + @Test public void logInitiated_noPreviousSession_logsUnknownTimeSinceLastActive() { when(mTimestampStore.timeSinceLastActiveSession()).thenReturn(null); @@ -177,7 +198,7 @@ public class MediaProjectionMetricsLoggerTest { @Test public void logStopped_logsStateChangedAtomId() { - mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); + mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE); verifyStateChangedAtomIdLogged(); } @@ -187,42 +208,49 @@ public class MediaProjectionMetricsLoggerTest { int currentSessionId = 987; when(mSessionIdGenerator.getCurrentSessionId()).thenReturn(currentSessionId); - mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); + mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE); verifySessionIdLogged(currentSessionId); } @Test public void logStopped_logsStateStopped() { - mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); + mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE); verifyStateLogged(MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED); } @Test public void logStopped_logsHostUid() { - mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); + mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE); verifyStateChangedHostUidLogged(TEST_HOST_UID); } @Test public void logStopped_logsTargetUid() { - mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); + mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE); verifyStageChangedTargetUidLogged(TEST_TARGET_UID); } @Test + public void logStopped_logsStopSource() { + mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, StopReason.STOP_UNKNOWN); + + verifyStopSourceLogged(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); + } + + @Test public void logStopped_logsUnknownTimeSinceLastActive() { - mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); + mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE); verifyTimeSinceLastActiveSessionLogged(-1); } @Test public void logStopped_logsUnknownSessionCreationSource() { - mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); + mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE); verifyCreationSourceLogged( MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN); @@ -230,7 +258,7 @@ public class MediaProjectionMetricsLoggerTest { @Test public void logStopped_logsPreviousState() { - mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); + mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE); verifyPreviousStateLogged( MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN); @@ -238,7 +266,7 @@ public class MediaProjectionMetricsLoggerTest { verifyPreviousStateLogged( MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED); - mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE); + mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE, TEST_STOP_SOURCE); verifyPreviousStateLogged( MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED); } @@ -247,14 +275,14 @@ public class MediaProjectionMetricsLoggerTest { public void logStopped_capturingWasInProgress_registersActiveSessionEnded() { mLogger.logInProgress(TEST_HOST_UID, TEST_TARGET_UID); - mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); + mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE); verify(mTimestampStore).registerActiveSessionEnded(); } @Test public void logStopped_capturingWasNotInProgress_doesNotRegistersActiveSessionEnded() { - mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); + mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID, TEST_STOP_SOURCE); verify(mTimestampStore, never()).registerActiveSessionEnded(); } @@ -314,6 +342,14 @@ public class MediaProjectionMetricsLoggerTest { } @Test + public void logInProgress_logsUnknownSessionStopSource() { + mLogger.logInProgress(TEST_HOST_UID, TEST_TARGET_UID); + + verifyStopSourceLogged( + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); + } + + @Test public void logInProgress_logsPreviousState() { mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE); verifyPreviousStateLogged( @@ -323,7 +359,7 @@ public class MediaProjectionMetricsLoggerTest { verifyPreviousStateLogged( MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED); - mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE); + mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE, TEST_STOP_SOURCE); verifyPreviousStateLogged( MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS); @@ -387,6 +423,14 @@ public class MediaProjectionMetricsLoggerTest { } @Test + public void logPermissionRequestDisplayed_logsUnknownSessionStopSource() { + mLogger.logPermissionRequestDisplayed(TEST_HOST_UID); + + verifyStopSourceLogged( + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); + } + + @Test public void logPermissionRequestDisplayed_logsPreviousState() { mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE); verifyPreviousStateLogged( @@ -396,7 +440,7 @@ public class MediaProjectionMetricsLoggerTest { verifyPreviousStateLogged( MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED); - mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE); + mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE, TEST_STOP_SOURCE); verifyPreviousStateLogged( MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED); @@ -460,6 +504,14 @@ public class MediaProjectionMetricsLoggerTest { } @Test + public void logAppSelectorDisplayed_logsUnknownSessionStopSource() { + mLogger.logAppSelectorDisplayed(TEST_HOST_UID); + + verifyStopSourceLogged( + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); + } + + @Test public void logAppSelectorDisplayed_logsPreviousState() { mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE); verifyPreviousStateLogged( @@ -469,7 +521,7 @@ public class MediaProjectionMetricsLoggerTest { verifyPreviousStateLogged( MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED); - mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE); + mLogger.logStopped(TEST_HOST_UID, TEST_CREATION_SOURCE, TEST_STOP_SOURCE); verifyPreviousStateLogged( MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED); @@ -536,6 +588,14 @@ public class MediaProjectionMetricsLoggerTest { } @Test + public void logProjectionPermissionRequestCancelled_logsUnknownStopSource() { + mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID); + + verifyStopSourceLogged( + MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); + } + + @Test public void logWindowingModeChanged_logsTargetChangedAtomId() { mLogger.logChangedWindowingMode( TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE); @@ -614,6 +674,42 @@ public class MediaProjectionMetricsLoggerTest { .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN); } + @Test + public void testStopReasonToSessionStopSource() { + mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_HOST_APP)) + .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_HOST_APP_STOP); + + mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_TARGET_REMOVED)) + .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_TASK_APP_CLOSE); + + mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_DEVICE_LOCKED)) + .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_DEVICE_LOCK); + + mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_PRIVACY_CHIP)) + .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_STATUS_BAR_CHIP_STOP); + + mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_QS_TILE)) + .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_QS_TILE); + + mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_USER_SWITCH)) + .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_USER_SWITCH); + + mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_FOREGROUND_SERVICE_CHANGE)) + .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_FOREGROUND_SERVICE_CHANGE); + + mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_NEW_PROJECTION)) + .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_PROJECTION); + + mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_NEW_MEDIA_ROUTE)) + .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_NEW_MEDIA_ROUTE); + + mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_ERROR)) + .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_ERROR); + + mExpect.that(mLogger.stopReasonToSessionStopSource(StopReason.STOP_UNKNOWN)) + .isEqualTo(MEDIA_PROJECTION_STATE_CHANGED__STOP_SOURCE__STOP_SOURCE_UNKNOWN); + } + private void verifyStateChangedAtomIdLogged() { verify(mFrameworkStatsLogWrapper) .writeStateChanged( @@ -624,7 +720,8 @@ public class MediaProjectionMetricsLoggerTest { /* hostUid= */ anyInt(), /* targetUid= */ anyInt(), /* timeSinceLastActive= */ anyInt(), - /* creationSource= */ anyInt()); + /* creationSource= */ anyInt(), + /* stopSource= */ anyInt()); } private void verifyStateLogged(int state) { @@ -637,7 +734,8 @@ public class MediaProjectionMetricsLoggerTest { /* hostUid= */ anyInt(), /* targetUid= */ anyInt(), /* timeSinceLastActive= */ anyInt(), - /* creationSource= */ anyInt()); + /* creationSource= */ anyInt(), + /* stopSource= */ anyInt()); } private void verifyStateChangedHostUidLogged(int hostUid) { @@ -650,7 +748,8 @@ public class MediaProjectionMetricsLoggerTest { eq(hostUid), /* targetUid= */ anyInt(), /* timeSinceLastActive= */ anyInt(), - /* creationSource= */ anyInt()); + /* creationSource= */ anyInt(), + /* stopSource= */ anyInt()); } private void verifyCreationSourceLogged(int creationSource) { @@ -663,7 +762,22 @@ public class MediaProjectionMetricsLoggerTest { /* hostUid= */ anyInt(), /* targetUid= */ anyInt(), /* timeSinceLastActive= */ anyInt(), - eq(creationSource)); + eq(creationSource), + /* stopSource= */ anyInt()); + } + + private void verifyStopSourceLogged(int stopSource) { + verify(mFrameworkStatsLogWrapper) + .writeStateChanged( + /* code= */ anyInt(), + /* sessionId= */ anyInt(), + /* state= */ anyInt(), + /* previousState= */ anyInt(), + /* hostUid= */ anyInt(), + /* targetUid= */ anyInt(), + /* timeSinceLastActive= */ anyInt(), + /* stopSource= */ anyInt(), + eq(stopSource)); } private void verifyStageChangedTargetUidLogged(int targetUid) { @@ -676,7 +790,8 @@ public class MediaProjectionMetricsLoggerTest { /* hostUid= */ anyInt(), eq(targetUid), /* timeSinceLastActive= */ anyInt(), - /* creationSource= */ anyInt()); + /* creationSource= */ anyInt(), + /* stopSource= */ anyInt()); } private void verifyTimeSinceLastActiveSessionLogged(int timeSinceLastActiveSession) { @@ -689,7 +804,8 @@ public class MediaProjectionMetricsLoggerTest { /* hostUid= */ anyInt(), /* targetUid= */ anyInt(), /* timeSinceLastActive= */ eq(timeSinceLastActiveSession), - /* creationSource= */ anyInt()); + /* creationSource= */ anyInt(), + /* stopSource= */ anyInt()); } private void verifySessionIdLogged(int newSessionId) { @@ -702,7 +818,8 @@ public class MediaProjectionMetricsLoggerTest { /* hostUid= */ anyInt(), /* targetUid= */ anyInt(), /* timeSinceLastActive= */ anyInt(), - /* creationSource= */ anyInt()); + /* creationSource= */ anyInt(), + /* stopSource= */ anyInt()); } private void verifyPreviousStateLogged(int previousState) { @@ -715,7 +832,8 @@ public class MediaProjectionMetricsLoggerTest { /* hostUid= */ anyInt(), /* targetUid= */ anyInt(), /* timeSinceLastActive= */ anyInt(), - /* creationSource= */ anyInt()); + /* creationSource= */ anyInt(), + /* stopSource= */ anyInt()); } private void verifyTargetChangedAtomIdLogged() { @@ -726,7 +844,12 @@ public class MediaProjectionMetricsLoggerTest { /* targetType= */ anyInt(), /* hostUid= */ anyInt(), /* targetUid= */ anyInt(), - /* targetWindowingMode= */ anyInt()); + /* targetWindowingMode= */ anyInt(), + /* width= */ anyInt(), + /* height= */ anyInt(), + /* centerX= */ anyInt(), + /* centerY= */ anyInt(), + /* targetChangeType= */ anyInt()); } private void verifyTargetTypeLogged(int targetType) { @@ -737,7 +860,12 @@ public class MediaProjectionMetricsLoggerTest { eq(targetType), /* hostUid= */ anyInt(), /* targetUid= */ anyInt(), - /* targetWindowingMode= */ anyInt()); + /* targetWindowingMode= */ anyInt(), + /* width= */ anyInt(), + /* height= */ anyInt(), + /* centerX= */ anyInt(), + /* centerY= */ anyInt(), + /* targetChangeType= */ anyInt()); } private void verifyTargetChangedHostUidLogged(int hostUid) { @@ -748,7 +876,12 @@ public class MediaProjectionMetricsLoggerTest { /* targetType= */ anyInt(), eq(hostUid), /* targetUid= */ anyInt(), - /* targetWindowingMode= */ anyInt()); + /* targetWindowingMode= */ anyInt(), + /* width= */ anyInt(), + /* height= */ anyInt(), + /* centerX= */ anyInt(), + /* centerY= */ anyInt(), + /* targetChangeType= */ anyInt()); } private void verifyTargetChangedTargetUidLogged(int targetUid) { @@ -759,7 +892,12 @@ public class MediaProjectionMetricsLoggerTest { /* targetType= */ anyInt(), /* hostUid= */ anyInt(), eq(targetUid), - /* targetWindowingMode= */ anyInt()); + /* targetWindowingMode= */ anyInt(), + /* width= */ anyInt(), + /* height= */ anyInt(), + /* centerX= */ anyInt(), + /* centerY= */ anyInt(), + /* targetChangeType= */ anyInt()); } private void verifyWindowingModeLogged(int targetWindowingMode) { @@ -770,6 +908,11 @@ public class MediaProjectionMetricsLoggerTest { /* targetType= */ anyInt(), /* hostUid= */ anyInt(), /* targetUid= */ anyInt(), - eq(targetWindowingMode)); + eq(targetWindowingMode), + /* width= */ anyInt(), + /* height= */ anyInt(), + /* centerX= */ anyInt(), + /* centerY= */ anyInt(), + /* targetChangeType= */ anyInt()); } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java new file mode 100644 index 000000000000..d69e47684f8b --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerCacheTest.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2011 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.pm; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assume.assumeTrue; + +import android.app.ActivityManager; +import android.app.LocaleManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.multiuser.Flags; +import android.os.LocaleList; +import android.os.SystemClock; +import android.os.UserHandle; +import android.os.UserManager; +import android.platform.test.annotations.Postsubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.util.ArraySet; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; + +/** + * Test {@link UserManager Cache} functionality. + * + * atest com.android.server.pm.UserManagerCacheTest + */ +@Postsubmit +@RunWith(AndroidJUnit4.class) +public final class UserManagerCacheTest { + + private static final LocaleList TEST_LOCALE_LIST = LocaleList.forLanguageTags("pl-PL"); + private static final long SLEEP_TIMEOUT = 5_000; + private static final int REMOVE_USER_TIMEOUT_SECONDS = 180; // 180 seconds + private static final String TAG = UserManagerCacheTest.class.getSimpleName(); + + private final Context mContext = + InstrumentationRegistry.getInstrumentation().getTargetContext(); + + private UserManager mUserManager = null; + private PackageManager mPackageManager; + private LocaleManager mLocaleManager; + private ArraySet<Integer> mUsersToRemove; + private UserRemovalWaiter mUserRemovalWaiter; + private int mOriginalCurrentUserId; + private LocaleList mSystemLocales; + + @Before + public void setUp() throws Exception { + mOriginalCurrentUserId = ActivityManager.getCurrentUser(); + mUserManager = UserManager.get(mContext); + mPackageManager = mContext.getPackageManager(); + mLocaleManager = mContext.getSystemService(LocaleManager.class); + mSystemLocales = mLocaleManager.getSystemLocales(); + mUserRemovalWaiter = new UserRemovalWaiter(mContext, TAG, REMOVE_USER_TIMEOUT_SECONDS); + mUsersToRemove = new ArraySet<>(); + removeExistingUsers(); + } + + @After + public void tearDown() throws Exception { + // Making a copy of mUsersToRemove to avoid ConcurrentModificationException + mUsersToRemove.stream().toList().forEach(this::removeUser); + mUserRemovalWaiter.close(); + mUserManager.setUserRestriction(UserManager.DISALLOW_GRANT_ADMIN, false, + mContext.getUser()); + mLocaleManager.setSystemLocales(mSystemLocales); + } + + private void removeExistingUsers() { + int currentUser = ActivityManager.getCurrentUser(); + + UserHandle communalProfile = mUserManager.getCommunalProfile(); + int communalProfileId = communalProfile != null + ? communalProfile.getIdentifier() : UserHandle.USER_NULL; + + List<UserInfo> list = mUserManager.getUsers(); + for (UserInfo user : list) { + // Keep system and current user + if (user.id != UserHandle.USER_SYSTEM + && user.id != currentUser + && user.id != communalProfileId + && !user.isMain()) { + removeUser(user.id); + } + } + } + + @MediumTest + @Test + @RequiresFlagsEnabled(Flags.FLAG_CACHE_USER_INFO_READ_ONLY) + public void testUserInfoAfterLocaleChange() throws Exception { + UserInfo userInfo = mUserManager.createGuest(mContext); + mUsersToRemove.add(userInfo.id); + assertThat(userInfo).isNotNull(); + + UserInfo guestUserInfo = mUserManager.getUserInfo(userInfo.id); + assertThat(guestUserInfo).isNotNull(); + assertThat(guestUserInfo.name).isNotEqualTo("Gość"); + + UserInfo ownerUserInfo = mUserManager.getUserInfo(mOriginalCurrentUserId); + assertThat(ownerUserInfo).isNotNull(); + assertThat(ownerUserInfo.name).isNotEqualTo("Właściciel"); + + mLocaleManager.setSystemLocales(TEST_LOCALE_LIST); + SystemClock.sleep(SLEEP_TIMEOUT); + UserInfo guestUserInfoPl = mUserManager.getUserInfo(userInfo.id); + UserInfo ownerUserInfoPl = mUserManager.getUserInfo(mOriginalCurrentUserId); + + assertThat(guestUserInfoPl).isNotNull(); + assertThat(guestUserInfoPl.name).isEqualTo("Gość"); + + assertThat(ownerUserInfoPl).isNotNull(); + assertThat(ownerUserInfoPl.name).isEqualTo("Właściciel"); + } + + + @MediumTest + @Test + @RequiresFlagsEnabled(Flags.FLAG_CACHE_USER_INFO_READ_ONLY) + public void testGetUserInfo10kSpam() throws Exception { + UserInfo cachedUserInfo = mUserManager.getUserInfo(mOriginalCurrentUserId); + for (int i = 0; i < 10000; i++) { + // Control how often cache is calling the API + UserInfo ownerUserInfo = mUserManager.getUserInfo(mOriginalCurrentUserId); + assertThat(ownerUserInfo).isNotNull(); + // If indeed it was chached then objects should stay the same. We use == to compare + // object addresses to make sure UserInfo is not new copy of the same UserInfo. + assertThat(cachedUserInfo.toFullString()).isEqualTo(ownerUserInfo.toFullString()); + } + } + + + @MediumTest + @Test + @RequiresFlagsEnabled(Flags.FLAG_CACHE_USER_INFO_READ_ONLY) + public void testSetUserAdmin() throws Exception { + UserInfo userInfo = mUserManager.createUser("SecondaryUser", + UserManager.USER_TYPE_FULL_SECONDARY, /*flags=*/ 0); + mUsersToRemove.add(userInfo.id); + // cache user + UserInfo cachedUserInfo = mUserManager.getUserInfo(userInfo.id); + + assertThat(userInfo.isAdmin()).isFalse(); + assertThat(cachedUserInfo.isAdmin()).isFalse(); + + // invalidate cache + mUserManager.setUserAdmin(userInfo.id); + + // updated UserInfo should be returned + cachedUserInfo = mUserManager.getUserInfo(userInfo.id); + assertThat(cachedUserInfo.isAdmin()).isTrue(); + } + + + @MediumTest + @Test + @RequiresFlagsEnabled(Flags.FLAG_CACHE_USER_INFO_READ_ONLY) + public void testRevokeUserAdmin() throws Exception { + UserInfo userInfo = mUserManager.createUser("Admin", + UserManager.USER_TYPE_FULL_SECONDARY, /*flags=*/ UserInfo.FLAG_ADMIN); + mUsersToRemove.add(userInfo.id); + // cache user + UserInfo cachedUserInfo = mUserManager.getUserInfo(userInfo.id); + assertThat(userInfo.isAdmin()).isTrue(); + assertThat(cachedUserInfo.isAdmin()).isTrue(); + + // invalidate cache + mUserManager.revokeUserAdmin(userInfo.id); + + // updated UserInfo should be returned + cachedUserInfo = mUserManager.getUserInfo(userInfo.id); + assertThat(cachedUserInfo.isAdmin()).isFalse(); + } + + @MediumTest + @Test + @RequiresFlagsEnabled(Flags.FLAG_CACHE_USER_INFO_READ_ONLY) + public void testRevokeUserAdminFromNonAdmin() throws Exception { + UserInfo userInfo = mUserManager.createUser("NonAdmin", + UserManager.USER_TYPE_FULL_SECONDARY, /*flags=*/ 0); + mUsersToRemove.add(userInfo.id); + // cache user + UserInfo cachedUserInfo = mUserManager.getUserInfo(userInfo.id); + assertThat(userInfo.isAdmin()).isFalse(); + assertThat(cachedUserInfo.isAdmin()).isFalse(); + + // invalidate cache + mUserManager.revokeUserAdmin(userInfo.id); + + // updated UserInfo should be returned + cachedUserInfo = mUserManager.getUserInfo(userInfo.id); + assertThat(cachedUserInfo.isAdmin()).isFalse(); + } + + + @MediumTest + @Test + @RequiresFlagsEnabled(Flags.FLAG_CACHE_USER_INFO_READ_ONLY) + public void testSetUserName_withContextUserId() throws Exception { + assumeManagedUsersSupported(); + final String newName = "Managed_user 1"; + final int mainUserId = mUserManager.getMainUser().getIdentifier(); + + // cache main user + UserInfo mainUserInfo = mUserManager.getUserInfo(mainUserId); + + assertThat(mainUserInfo).isNotNull(); + + // invalidate cache + UserInfo userInfo = mUserManager.createProfileForUser("Managed 1", + UserManager.USER_TYPE_PROFILE_MANAGED, 0, mainUserId, null); + mUsersToRemove.add(userInfo.id); + // cache user + UserInfo cachedUserInfo = mUserManager.getUserInfo(userInfo.id); + // updated cache for main user + mainUserInfo = mUserManager.getUserInfo(mainUserId); + + assertThat(userInfo).isNotNull(); + assertThat(cachedUserInfo).isNotNull(); + assertThat(mainUserInfo).isNotNull(); + // profileGroupId are the same after adding profile to user. + assertThat(mainUserInfo.profileGroupId).isEqualTo(cachedUserInfo.profileGroupId); + + UserManager um = (UserManager) mContext.createPackageContextAsUser( + "android", 0, userInfo.getUserHandle()) + .getSystemService(Context.USER_SERVICE); + // invalidate cache + um.setUserName(newName); + + // updated UserInfo should be returned + cachedUserInfo = mUserManager.getUserInfo(userInfo.id); + assertThat(cachedUserInfo.name).isEqualTo(newName); + + // get user name from getUserName using context.getUserId + assertThat(um.getUserName()).isEqualTo(newName); + } + + private void assumeManagedUsersSupported() { + // In Automotive, if headless system user is enabled, a managed user cannot be created + // under a primary user. + assumeTrue("device doesn't support managed users", + mPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS) + && (!isAutomotive() || !UserManager.isHeadlessSystemUserMode())); + } + + private void removeUser(int userId) { + mUserManager.removeUser(userId); + mUserRemovalWaiter.waitFor(userId); + mUsersToRemove.remove(userId); + } + + private boolean isAutomotive() { + return mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java index 6af65423415b..dd278fccad14 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java @@ -2170,6 +2170,174 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS}) + public void testRemoveChildNotification_summaryForceGrouped() { + // Check that removing all child notifications from a group will trigger empty summary + // force grouping re-evaluation + final List<NotificationRecord> notificationList = new ArrayList<>(); + final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>(); + final String pkg = "package"; + // Post summaries without children, below the force grouping limit + for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) { + NotificationRecord summary = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42), + UserHandle.SYSTEM, "testGrp " + i, true); + notificationList.add(summary); + mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup); + } + // Post a valid (full) group + final int summaryId = 4242; + final int numChildren = 3; + final ArrayList<NotificationRecord> childrenToRemove = new ArrayList<>(); + NotificationRecord summary = getNotificationRecord(pkg, summaryId, + String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp " + summaryId, true); + notificationList.add(summary); + summaryByGroup.put(summary.getGroupKey(), summary); + for (int i = 0; i < numChildren; i++) { + NotificationRecord child = getNotificationRecord(pkg, summaryId + 42, + String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + summaryId, false); + notificationList.add(child); + // schedule all children for removal + childrenToRemove.add(child); + } + mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup); + verifyZeroInteractions(mCallback); + + // Remove all child notifications from the valid group => summary without children + Mockito.reset(mCallback); + for (NotificationRecord r: childrenToRemove) { + notificationList.remove(r); + mGroupHelper.onNotificationRemoved(r, notificationList); + } + // Only call onGroupedNotificationRemovedWithDelay with the summary notification + mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList, + summaryByGroup); + + // Check that the summaries were force grouped + final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg, + AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier()); + verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), + eq(expectedGroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS))); + verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), + eq(expectedGroupKey), eq(true)); + verify(mCallback, never()).removeAutoGroup(anyString()); + verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), + any()); + } + + @Test + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS}) + public void testRemoveChildNotification_groupBecomesSingleton() { + // Check that removing child notifications from a group will trigger singleton force + // grouping re-evaluation + final List<NotificationRecord> notificationList = new ArrayList<>(); + final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>(); + final String pkg = "package"; + // Post singleton groups, under forced group limit + for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT - 1; i++) { + NotificationRecord summary = getNotificationRecord(pkg, i, + String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true); + notificationList.add(summary); + NotificationRecord child = getNotificationRecord(pkg, i + 42, + String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + i, false); + notificationList.add(child); + summaryByGroup.put(summary.getGroupKey(), summary); + mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup); + mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup); + } + // Post a valid (full) group + final int summaryId = 4242; + final int numChildren = 3; + final ArrayList<NotificationRecord> childrenToRemove = new ArrayList<>(); + NotificationRecord summary = getNotificationRecord(pkg, summaryId, + String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp " + summaryId, true); + notificationList.add(summary); + summaryByGroup.put(summary.getGroupKey(), summary); + for (int i = 0; i < numChildren; i++) { + NotificationRecord child = getNotificationRecord(pkg, summaryId + 42, + String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + summaryId, false); + notificationList.add(child); + + // schedule all children except one for removal + if (i < numChildren - 1) { + childrenToRemove.add(child); + } + } + mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup); + verifyZeroInteractions(mCallback); + + // Remove some child notifications from the valid group, transform into a singleton group + Mockito.reset(mCallback); + for (NotificationRecord r: childrenToRemove) { + notificationList.remove(r); + mGroupHelper.onNotificationRemoved(r, notificationList); + } + // Only call onGroupedNotificationRemovedWithDelay with the summary notification + mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList, + summaryByGroup); + + // Check that the singleton groups were force grouped + final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg, + AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier()); + verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), + eq(expectedGroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS))); + verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(), + eq(expectedGroupKey), eq(true)); + verify(mCallback, never()).removeAutoGroup(anyString()); + verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), + any()); + verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).removeAppProvidedSummary( + anyString()); + } + + @Test + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS}) + public void testRemoveAllGroupNotifications_noForceGrouping() { + // Check that removing all notifications from a group will not trigger any force grouping + // re-evaluation + final List<NotificationRecord> notificationList = new ArrayList<>(); + final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>(); + final String pkg = "package"; + // Post summaries without children, below the force grouping limit + for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) { + NotificationRecord summary = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42), + UserHandle.SYSTEM, "testGrp " + i, true); + notificationList.add(summary); + mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup); + } + // Post a valid (full) group + final int summaryId = 4242; + final int numChildren = 3; + final String groupToRemove = "testRemoveGrp"; + NotificationRecord summary = getNotificationRecord(pkg, summaryId, + String.valueOf(summaryId), UserHandle.SYSTEM, groupToRemove + summaryId, true); + notificationList.add(summary); + summaryByGroup.put(summary.getGroupKey(), summary); + for (int i = 0; i < numChildren; i++) { + NotificationRecord child = getNotificationRecord(pkg, summaryId + 42, + String.valueOf(i + 42), UserHandle.SYSTEM, groupToRemove + summaryId, false); + notificationList.add(child); + } + mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup); + verifyZeroInteractions(mCallback); + + // Remove all child notifications from the valid group => summary without children + Mockito.reset(mCallback); + for (NotificationRecord r: notificationList) { + if (r.getGroupKey().contains(groupToRemove)) { + r.isCanceled = true; + mGroupHelper.onNotificationRemoved(r, notificationList); + } + } + // Only call onGroupedNotificationRemovedWithDelay with the summary notification + mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList, + summaryByGroup); + // Check that nothing was force grouped + verifyZeroInteractions(mCallback); + } + + @Test @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) public void testMoveAggregateGroups_updateChannel() { final String pkg = "package"; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index cd745befaf2d..863f42f3d1c1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3093,6 +3093,92 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST}) + public void testScheduleGroupHelperWithDelay_onChildNotificationCanceled() throws Exception { + // Post summary + 2 child notification + final String originalGroupName = "originalGroup"; + final int summaryId = 0; + final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel, + summaryId + 1, originalGroupName, false); + mService.addNotification(r1); + final NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel, + summaryId + 2, originalGroupName, false); + mService.addNotification(r2); + final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel, + summaryId, originalGroupName, true); + mService.addNotification(summary); + final String originalGroupKey = summary.getGroupKey(); + assertThat(mService.mSummaryByGroupKey).containsEntry(originalGroupKey, summary); + + // Cancel the child notifications + mBinderService.cancelNotificationWithTag(r1.getSbn().getPackageName(), + r1.getSbn().getPackageName(), r1.getSbn().getTag(), + r1.getSbn().getId(), r1.getSbn().getUserId()); + waitForIdle(); + + mBinderService.cancelNotificationWithTag(r2.getSbn().getPackageName(), + r2.getSbn().getPackageName(), r2.getSbn().getTag(), + r2.getSbn().getId(), r2.getSbn().getUserId()); + waitForIdle(); + + mTestableLooper.moveTimeForward(DELAY_FORCE_REGROUP_TIME); + waitForIdle(); + + // Check that onGroupedNotificationRemovedWithDelay was called only once + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r1), any()); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r2), any()); + verify(mGroupHelper, times(1)).onGroupedNotificationRemovedWithDelay(eq(summary), any(), + any()); + } + + @Test + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST}) + public void testCleanupScheduleGroupHelperWithDelay_onAllNotificationCanceled() + throws Exception { + // Post summary + 2 child notification + final String originalGroupName = "originalGroup"; + final int summaryId = 0; + final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel, + summaryId + 1, originalGroupName, false); + mService.addNotification(r1); + final NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel, + summaryId + 2, originalGroupName, false); + mService.addNotification(r2); + final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel, + summaryId, originalGroupName, true); + mService.addNotification(summary); + final String originalGroupKey = summary.getGroupKey(); + assertThat(mService.mSummaryByGroupKey).containsEntry(originalGroupKey, summary); + + // Cancel all notifications: children + summary + mBinderService.cancelNotificationWithTag(r1.getSbn().getPackageName(), + r1.getSbn().getPackageName(), r1.getSbn().getTag(), + r1.getSbn().getId(), r1.getSbn().getUserId()); + waitForIdle(); + + mBinderService.cancelNotificationWithTag(r2.getSbn().getPackageName(), + r2.getSbn().getPackageName(), r2.getSbn().getTag(), + r2.getSbn().getId(), r2.getSbn().getUserId()); + waitForIdle(); + + mBinderService.cancelNotificationWithTag(summary.getSbn().getPackageName(), + summary.getSbn().getPackageName(), summary.getSbn().getTag(), + summary.getSbn().getId(), summary.getSbn().getUserId()); + waitForIdle(); + + mTestableLooper.moveTimeForward(DELAY_FORCE_REGROUP_TIME); + waitForIdle(); + + // Check that onGroupedNotificationRemovedWithDelay was never called: summary was canceled + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r1), any()); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r2), any()); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(summary), any()); + verify(mGroupHelper, never()).onGroupedNotificationRemovedWithDelay(any(), any(), any()); + } + + @Test public void testCancelAllNotifications_IgnoreForegroundService() throws Exception { when(mAmi.applyForegroundServiceNotification( any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java index 4d82c3ccf4a1..949c5e2e1b0a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SystemZenRulesTest.java @@ -69,6 +69,8 @@ public class SystemZenRulesTest extends UiServiceTestCase { R.string.zen_mode_trigger_summary_range_symbol_combination, "%1$s-%2$s"); mContext.getOrCreateTestableResources().addOverride( R.string.zen_mode_trigger_summary_divider_text, ","); + mContext.getOrCreateTestableResources().addOverride( + R.string.zen_mode_trigger_summary_combined, "%1$s,%2$s"); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index bf61d06a80a7..09da0156eb82 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -7363,6 +7363,20 @@ public class ZenModeHelperTest extends UiServiceTestCase { verify(callback, never()).onZenModeChanged(); } + @Test + @EnableFlags(FLAG_MODES_MULTIUSER) + public void getNotificationPolicy_fromUserWithoutZenConfig_returnsDefaultPolicy() { + // Set a custom policy for the current user to double check we return a default one below. + mZenModeHelper.setNotificationPolicy(UserHandle.CURRENT, new Policy(0, 0, 0), ORIGIN_SYSTEM, + SYSTEM_UID); + + Policy ghostPolicy = mZenModeHelper.getNotificationPolicy(UserHandle.of(5552368)); + + assertThat(ghostPolicy).isNotNull(); + assertThat(ZenAdapters.notificationPolicyToZenPolicy(ghostPolicy)) + .isEqualTo(mZenModeHelper.getDefaultZenPolicy()); + } + private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode, @Nullable ZenPolicy zenPolicy) { ZenRule rule = new ZenRule(); 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 23ee893e309a..da1c1ae76d2d 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -449,6 +449,18 @@ public class VibrationSettingsTest { } } + @Test + public void shouldIgnoreVibration_withoutAudioManager_allowsAllVibrations() { + mVibrationSettings = new VibrationSettings(mContextSpy, + new Handler(mTestLooper.getLooper()), mVibrationConfigMock); + mVibrationSettings.onSystemReady(mPackageManagerInternalMock, + mPowerManagerInternalMock, mActivityManagerMock, mVirtualDeviceManagerInternalMock, + /* audioManager= */ null); + + for (int usage : ALL_USAGES) { + assertVibrationNotIgnoredForUsage(usage); + } + } @Test public void shouldIgnoreVibration_vibrateOnDisabled_ignoresUsagesNotAccessibility() { diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java index d5ed048032e7..1d138e4a48d9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java @@ -249,6 +249,18 @@ public class AppCompatCameraOverridesTest extends WindowTestsBase { } @Test + @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + public void testShouldApplyCameraCompatFreeformTreatment_enabledByShellCommand_returnsTrue() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponentInNewTask(); + + robot.setCameraCompatTreatmentEnabledViaShellCommand(true); + + robot.checkShouldApplyFreeformTreatmentForCameraCompat(true); + }); + } + + @Test @EnableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA, OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA, OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) @@ -350,6 +362,11 @@ public class AppCompatCameraOverridesTest extends WindowTestsBase { spyOn(displayContent.mAppCompatCameraPolicy); } + void setCameraCompatTreatmentEnabledViaShellCommand(boolean enabled) { + activity().top().mWmService.mAppCompatConfiguration + .setIsCameraCompatFreeformWindowingTreatmentEnabled(enabled); + } + void checkShouldRefreshActivityForCameraCompat(boolean expected) { Assert.assertEquals(getAppCompatCameraOverrides() .shouldRefreshActivityForCameraCompat(), expected); diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java index c51261f40ed5..76b994d013f3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java @@ -49,6 +49,7 @@ import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; +import android.media.projection.StopReason; import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.view.ContentRecordingSession; @@ -213,7 +214,7 @@ public class ContentRecorderTests extends WindowTestsBase { mContentRecorder.setContentRecordingSession(session); mContentRecorder.updateRecording(); assertThat(mContentRecorder.isCurrentlyRecording()).isFalse(); - verify(mMediaProjectionManagerWrapper).stopActiveProjection(); + verify(mMediaProjectionManagerWrapper).stopActiveProjection(StopReason.STOP_ERROR); } @Test @@ -225,7 +226,7 @@ public class ContentRecorderTests extends WindowTestsBase { mContentRecorder.setContentRecordingSession(invalidTaskSession); mContentRecorder.updateRecording(); assertThat(mContentRecorder.isCurrentlyRecording()).isFalse(); - verify(mMediaProjectionManagerWrapper).stopActiveProjection(); + verify(mMediaProjectionManagerWrapper).stopActiveProjection(StopReason.STOP_ERROR); } @Test @@ -310,8 +311,7 @@ public class ContentRecorderTests extends WindowTestsBase { mVirtualDisplayContent.getConfiguration().orientation, WINDOWING_MODE_FULLSCREEN); // No resize is issued, only the initial transformations when we started recording. - verify(mTransaction).setPosition(eq(mRecordedSurface), anyFloat(), - anyFloat()); + verify(mTransaction).setPosition(eq(mRecordedSurface), anyFloat(), anyFloat()); verify(mTransaction).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(), anyFloat(), anyFloat()); } @@ -386,19 +386,18 @@ public class ContentRecorderTests extends WindowTestsBase { // WHEN a configuration change arrives, and the recorded content is a different size. Configuration configuration = mTask.getConfiguration(); - configuration.windowConfiguration.setBounds(new Rect(0, 0, recordedWidth, recordedHeight)); - configuration.windowConfiguration.setAppBounds( - new Rect(0, 0, recordedWidth, recordedHeight)); + Rect newBounds = new Rect(0, 0, recordedWidth, recordedHeight); + configuration.windowConfiguration.setBounds(newBounds); + configuration.windowConfiguration.setAppBounds(newBounds); mTask.onConfigurationChanged(configuration); assertThat(mContentRecorder.isCurrentlyRecording()).isTrue(); // THEN content in the captured DisplayArea is scaled to fit the surface size. - verify(mTransaction, atLeastOnce()).setMatrix(eq(mRecordedSurface), anyFloat(), eq(0f), - eq(0f), - anyFloat()); + verify(mTransaction, atLeastOnce()).setMatrix( + eq(mRecordedSurface), anyFloat(), eq(0f), eq(0f), anyFloat()); // THEN the resize callback is notified. - verify(mMediaProjectionManagerWrapper).notifyActiveProjectionCapturedContentResized( - recordedWidth, recordedHeight); + verify(mMediaProjectionManagerWrapper).notifyCaptureBoundsChanged( + mTaskSession.getContentToRecord(), mTaskSession.getTargetUid(), newBounds); } @Test @@ -649,7 +648,7 @@ public class ContentRecorderTests extends WindowTestsBase { mTask.removeImmediately(); - verify(mMediaProjectionManagerWrapper).stopActiveProjection(); + verify(mMediaProjectionManagerWrapper).stopActiveProjection(StopReason.STOP_TARGET_REMOVED); } @Test @@ -684,8 +683,8 @@ public class ContentRecorderTests extends WindowTestsBase { int xInset = (mSurfaceSize.x - scaledWidth) / 2; verify(mTransaction, atLeastOnce()).setPosition(mRecordedSurface, xInset, 0); // THEN the resize callback is notified. - verify(mMediaProjectionManagerWrapper).notifyActiveProjectionCapturedContentResized( - displayAreaBounds.width(), displayAreaBounds.height()); + verify(mMediaProjectionManagerWrapper).notifyCaptureBoundsChanged( + mDisplaySession.getContentToRecord(), mDisplaySession.getTargetUid(), displayAreaBounds); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java index c9c31dfe5307..a0f4ae77452c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DeferredDisplayUpdaterDiffTest.java @@ -239,6 +239,9 @@ public class DeferredDisplayUpdaterDiffTest { } else if (type.equals(FrameRateCategoryRate.class)) { field.set(first, new FrameRateCategoryRate(16666667, 11111111)); field.set(second, new FrameRateCategoryRate(11111111, 8333333)); + } else if (type.isArray() && type.getComponentType().equals(float.class)) { + field.set(first, new float[]{60.0f}); + field.set(second, new float[]{120.0f}); } else { throw new IllegalArgumentException("Field " + field + " is not supported by this test, please add implementation of setting " diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java index c8fc4822259e..63973345b5fb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java @@ -20,16 +20,19 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; import android.platform.test.annotations.Presubmit; import android.view.Surface; @@ -54,6 +57,7 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas private DisplayRotationImmersiveAppCompatPolicy mPolicy; + private DisplayRotation mMockDisplayRotation; private AppCompatConfiguration mMockAppCompatConfiguration; private ActivityRecord mMockActivityRecord; private Task mMockTask; @@ -98,6 +102,7 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas when(mockDisplayRotation.isLandscapeOrSeascape(Surface.ROTATION_90)).thenReturn(true); when(mockDisplayRotation.isLandscapeOrSeascape(Surface.ROTATION_180)).thenReturn(false); when(mockDisplayRotation.isLandscapeOrSeascape(Surface.ROTATION_270)).thenReturn(true); + mMockDisplayRotation = mockDisplayRotation; return mockDisplayRotation; } @@ -196,6 +201,24 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas } @Test + public void testDeferOrientationUpdate() { + assertFalse(mPolicy.deferOrientationUpdate()); + + doReturn(SCREEN_ORIENTATION_UNSPECIFIED).when(mMockDisplayRotation).getLastOrientation(); + final WindowOrientationListener orientationListener = mock(WindowOrientationListener.class); + doReturn(Surface.ROTATION_90).when(orientationListener).getProposedRotation(); + doReturn(orientationListener).when(mMockDisplayRotation).getOrientationListener(); + spyOn(mDisplayContent.mTransitionController); + doReturn(true).when(mDisplayContent.mTransitionController) + .hasTransientLaunch(mDisplayContent); + + assertTrue(mPolicy.deferOrientationUpdate()); + mDisplayContent.mTransitionController.mStateValidators.getFirst().run(); + + verify(mWm).updateRotation(false, false); + } + + @Test public void testRotationChoiceEnforcedOnly_nullTopRunningActivity_lockNotEnforced() { when(mDisplayContent.topRunningActivity()).thenReturn(null); diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java index a0c5b54603f9..c016c5ead23c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java @@ -69,7 +69,9 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { private static final FrameRateVote FRAME_RATE_VOTE_60_PREFERRED = new FrameRateVote(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT, SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN); - + private static final float HI_REFRESH_RATE = 90; + private static final float MID_REFRESH_RATE = 70; + private static final float LOW_REFRESH_RATE = 60; WindowState createWindow(String name) { WindowState window = createWindow(null, TYPE_APPLICATION, name); when(window.mWmService.mDisplayManagerInternal.getRefreshRateSwitchingType()) @@ -82,14 +84,16 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase { DisplayInfo di = new DisplayInfo(mDisplayInfo); Mode defaultMode = di.getDefaultMode(); Mode hiMode = new Mode(1, - defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 90); + defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), HI_REFRESH_RATE); Mode midMode = new Mode(2, - defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 70); + defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), MID_REFRESH_RATE); Mode lowMode = new Mode(LOW_MODE_ID, - defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 60); + defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), LOW_REFRESH_RATE); di.supportedModes = new Mode[] { hiMode, midMode }; di.appsSupportedModes = new Mode[] { hiMode, midMode, lowMode }; + di.supportedRefreshRates = new float[] {HI_REFRESH_RATE, MID_REFRESH_RATE, + LOW_REFRESH_RATE}; di.defaultModeId = 1; mRefreshRatePolicy = new RefreshRatePolicy(mWm, di, mDenylist); when(mDisplayPolicy.getRefreshRatePolicy()).thenReturn(mRefreshRatePolicy); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 7ed8283efffd..aa992504f9a5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -434,6 +434,7 @@ public class RecentTasksTest extends WindowTestsBase { @Test public void testAddTaskCompatibleWindowingMode_withFreeformAndFullscreen_expectRemove() { Task task1 = createTaskBuilder(".Task1") + .setTaskId(1) .setFlags(FLAG_ACTIVITY_NEW_TASK) .build(); doReturn(WINDOWING_MODE_FREEFORM).when(task1).getWindowingMode(); @@ -452,6 +453,10 @@ public class RecentTasksTest extends WindowTestsBase { assertThat(mCallbacksRecorder.mTrimmed).isEmpty(); assertThat(mCallbacksRecorder.mRemoved).hasSize(1); assertThat(mCallbacksRecorder.mRemoved).contains(task1); + + TaskChangeNotificationController controller = + mAtm.getTaskChangeNotificationController(); + verify(controller, times(1)).notifyRecentTaskRemovedForAddTask(task1.mTaskId); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java index cc38f02ccc4c..73e5f58fa7e0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java @@ -101,6 +101,8 @@ public class RefreshRatePolicyTest extends WindowTestsBase { defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), LOW_REFRESH_RATE); mDisplayInfo.supportedModes = new Mode[] { hiMode, midMode }; mDisplayInfo.appsSupportedModes = new Mode[] { hiMode, midMode, lowMode }; + mDisplayInfo.supportedRefreshRates = new float[] {HI_REFRESH_RATE, MID_REFRESH_RATE, + LOW_REFRESH_RATE}; mDisplayInfo.defaultModeId = HI_MODE_ID; mPolicy = new RefreshRatePolicy(mWm, mDisplayInfo, mDenylist); } diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java index 791b5b59535a..a92fe3afbd78 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java @@ -162,6 +162,10 @@ public class SafeActivityOptionsTest { verifySecureExceptionThrown(activityOptions, taskSupervisor); activityOptions = ActivityOptions.makeBasic(); + activityOptions.setTaskAlwaysOnTop(true); + verifySecureExceptionThrown(activityOptions, taskSupervisor); + + activityOptions = ActivityOptions.makeBasic(); activityOptions.setLaunchDisplayId(DEFAULT_DISPLAY); verifySecureExceptionThrown(activityOptions, taskSupervisor); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 41f1e2359c88..584c4c96ae1d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -4863,7 +4863,7 @@ public class SizeCompatTests extends WindowTestsBase { @Test @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) - public void testCameraCompatAspectRatio_defualtAspectRatioAppliedWhenGreater() { + public void testCameraCompatAspectRatio_defaultAspectRatioAppliedWhenGreater() { // Needed to create camera compat policy in DisplayContent. allowDesktopMode(); // Create display that has all stable insets and does not rotate. @@ -4917,7 +4917,8 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(minAspect, aspectRatioPolicy.getMinAspectRatio(), 0 /* delta */); // User override can still take effect. - doReturn(true).when(aspectRatioOverrides).shouldApplyUserMinAspectRatioOverride(); + doReturn(USER_MIN_ASPECT_RATIO_3_2).when(aspectRatioOverrides) + .getUserMinAspectRatioOverrideCode(); assertFalse(mActivity.isResizeable()); assertEquals(maxAspect, aspectRatioPolicy.getMaxAspectRatio(), 0 /* delta */); assertNotEquals(SCREEN_ORIENTATION_UNSPECIFIED, mActivity.getOverrideOrientation()); @@ -4928,6 +4929,7 @@ public class SizeCompatTests extends WindowTestsBase { spyOn(pm); final PackageManager.Property property = new PackageManager.Property("propertyName", true /* value */, name.getPackageName(), name.getClassName()); + // Activity level. try { doReturn(property).when(pm).getPropertyAsUser( WindowManager.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, @@ -4938,6 +4940,19 @@ public class SizeCompatTests extends WindowTestsBase { final ActivityRecord optOutActivity = new ActivityBuilder(mAtm) .setComponent(name).setTask(mTask).build(); assertFalse(optOutActivity.isUniversalResizeable()); + + // Application level. + try { + doReturn(property).when(pm).getProperty( + WindowManager.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, + name.getPackageName()); + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException(e); + } + final ActivityRecord optOutAppActivity = new ActivityBuilder(mAtm) + .setComponent(getUniqueComponentName(mContext.getPackageName())) + .setTask(mTask).build(); + assertFalse(optOutAppActivity.isUniversalResizeable()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 8bbba1b17240..a425401c6238 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -42,7 +42,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 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.never; @@ -85,7 +84,6 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; -import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -97,7 +95,6 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InsetsSourceControl; import android.view.InsetsState; -import android.view.Surface; import android.view.SurfaceControl; import android.view.View; import android.view.WindowInsets; @@ -1192,55 +1189,6 @@ public class WindowManagerServiceTests extends WindowTestsBase { argThat(h -> (h.inputConfig & InputConfig.SENSITIVE_FOR_PRIVACY) != 0)); } - @RequiresFlagsDisabled(Flags.FLAG_ALWAYS_DRAW_MAGNIFICATION_FULLSCREEN_BORDER) - @Test - public void testDrawMagnifiedViewport() { - final int displayId = mDisplayContent.mDisplayId; - // Use real surface, so ViewportWindow's BlastBufferQueue can be created. - final ArrayList<SurfaceControl> surfaceControls = new ArrayList<>(); - mWm.mSurfaceControlFactory = () -> new SurfaceControl.Builder() { - @Override - public SurfaceControl build() { - final SurfaceControl sc = super.build(); - surfaceControls.add(sc); - return sc; - } - }; - mWm.mAccessibilityController.setMagnificationCallbacks(displayId, - mock(WindowManagerInternal.MagnificationCallbacks.class)); - final boolean[] lockCanvasInWmLock = { false }; - final Surface surface = mWm.mAccessibilityController.forceShowMagnifierSurface(displayId); - spyOn(surface); - doAnswer(invocationOnMock -> { - lockCanvasInWmLock[0] |= Thread.holdsLock(mWm.mGlobalLock); - invocationOnMock.callRealMethod(); - return null; - }).when(surface).lockCanvas(any()); - mWm.mAccessibilityController - .recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded(displayId); - waitUntilHandlersIdle(); - try { - verify(surface).lockCanvas(any()); - - clearInvocations(surface); - // Invalidate and redraw. - mWm.mAccessibilityController.onDisplaySizeChanged(mDisplayContent); - mWm.mAccessibilityController - .recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded(displayId); - // Turn off magnification to release surface. - mWm.mAccessibilityController.setMagnificationCallbacks(displayId, null); - waitUntilHandlersIdle(); - // lockCanvas must not be called after releasing. - verify(surface, never()).lockCanvas(any()); - verify(surface).release(); - assertFalse(lockCanvasInWmLock[0]); - } finally { - for (int i = surfaceControls.size() - 1; i >= 0; --i) { - surfaceControls.get(i).release(); - } - } - } - @Test public void testRequestKeyboardShortcuts_noWindow() { doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString()); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 9c961c1f0df0..ad5d42ad5a68 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -8825,9 +8825,6 @@ public class TelephonyManager { * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given * authType. */ - // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not - // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since - // it's not public API. @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public String getIccAuthentication(int appType, @AuthType int authType, String data) { return getIccAuthentication(getSubId(), appType, authType, data); @@ -19602,4 +19599,37 @@ public class TelephonyManager { throw ex.rethrowAsRuntimeException(); } } + + /** + * Returns carrier id maps to the passing CarrierIdentifier. + * To recognize a carrier (including MVNO) as a first-class identity, + * Android assigns each carrier with a canonical integer a.k.a. carrier id. + * The carrier ID is an Android platform-wide identifier for a carrier. + * AOSP maintains carrier ID assignments in + * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a> + * + * @param carrierIdentifier {@link CarrierIdentifier} + * + * @return Carrier id. Return {@link #UNKNOWN_CARRIER_ID} if the carrier cannot be identified. + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_CARRIER_ID_FROM_CARRIER_IDENTIFIER) + @SystemApi + @WorkerThread + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public int getCarrierIdFromCarrierIdentifier(@NonNull CarrierIdentifier carrierIdentifier) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getCarrierIdFromIdentifier(carrierIdentifier); + } + } catch (RemoteException ex) { + // This could happen if binder process crashes. + } + return UNKNOWN_CARRIER_ID; + } } diff --git a/telephony/java/android/telephony/satellite/ISelectedNbIotSatelliteSubscriptionCallback.aidl b/telephony/java/android/telephony/satellite/ISelectedNbIotSatelliteSubscriptionCallback.aidl new file mode 100644 index 000000000000..178bb3c29ea3 --- /dev/null +++ b/telephony/java/android/telephony/satellite/ISelectedNbIotSatelliteSubscriptionCallback.aidl @@ -0,0 +1,31 @@ +/* + * 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. + */ + +package android.telephony.satellite; + +/** + * Interface for selected satellite subscription change callback. + * + * @hide + */ +oneway interface ISelectedNbIotSatelliteSubscriptionCallback { + /** + * Called when the selected satellite subscription has changed. + * + * @param selectedSubId The new satellite subscription id. + */ + void onSelectedNbIotSatelliteSubscriptionChanged(in int selectedSubId); +} diff --git a/telephony/java/android/telephony/satellite/SatelliteInfo.java b/telephony/java/android/telephony/satellite/SatelliteInfo.java index 7ff231812c8a..1d5f613cc086 100644 --- a/telephony/java/android/telephony/satellite/SatelliteInfo.java +++ b/telephony/java/android/telephony/satellite/SatelliteInfo.java @@ -17,7 +17,6 @@ package android.telephony.satellite; import android.annotation.FlaggedApi; -import android.annotation.Nullable; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -47,10 +46,12 @@ public class SatelliteInfo implements Parcelable { private UUID mId; /** - * Position information of a satellite. + * Position information of a geostationary satellite. * This includes the longitude and altitude of the satellite. + * If the SatellitePosition is invalid, + * longitudeDegree and altitudeKm will be represented as DOUBLE.NaN. */ - @Nullable + @NonNull private SatellitePosition mPosition; /** @@ -89,7 +90,7 @@ public class SatelliteInfo implements Parcelable { * @param earfcnRanges The list of {@link EarfcnRange} objects representing the EARFCN * ranges supported by the satellite. */ - public SatelliteInfo(@NonNull UUID satelliteId, @Nullable SatellitePosition satellitePosition, + public SatelliteInfo(@NonNull UUID satelliteId, @NonNull SatellitePosition satellitePosition, @NonNull List<Integer> bandList, @NonNull List<EarfcnRange> earfcnRanges) { mId = satelliteId; mPosition = satellitePosition; @@ -135,10 +136,9 @@ public class SatelliteInfo implements Parcelable { /** * Returns the position of the satellite. * - * @return The {@link SatellitePosition} of the satellite, or {@code null} if the position is - * not available. + * @return The {@link SatellitePosition} of the satellite. */ - @Nullable + @NonNull public SatellitePosition getSatellitePosition() { return mPosition; } diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 23203ed65e9a..5a34b0038872 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -99,16 +99,18 @@ public final class SatelliteManager { private static final ConcurrentHashMap<SatelliteSupportedStateCallback, ISatelliteSupportedStateCallback> sSatelliteSupportedStateCallbackMap = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap<SatelliteCommunicationAllowedStateCallback, ISatelliteCommunicationAllowedStateCallback> sSatelliteCommunicationAllowedStateCallbackMap = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap<SatelliteDisallowedReasonsCallback, ISatelliteDisallowedReasonsCallback> sSatelliteDisallowedReasonsCallbackMap = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap<SelectedNbIotSatelliteSubscriptionCallback, + ISelectedNbIotSatelliteSubscriptionCallback> + sSelectedNbIotSatelliteSubscriptionCallbackMap = + new ConcurrentHashMap<>(); private final int mSubId; @@ -746,7 +748,7 @@ public final class SatelliteManager { * @hide */ public static final String ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED = - "android.telephony.action.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED"; + "android.telephony.satellite.action.SATELLITE_SUBSCRIBER_ID_LIST_CHANGED"; /** @@ -757,7 +759,7 @@ public final class SatelliteManager { * @hide */ public static final String ACTION_SATELLITE_START_NON_EMERGENCY_SESSION = - "android.telephony.action.ACTION_SATELLITE_START_NON_EMERGENCY_SESSION"; + "android.telephony.satellite.action.SATELLITE_START_NON_EMERGENCY_SESSION"; /** * Meta-data represents whether the application supports P2P SMS over carrier roaming satellite * which needs manual trigger to connect to satellite. The messaging applications that supports @@ -2541,6 +2543,89 @@ public final class SatelliteManager { } /** + * Registers for selected satellite subscription changed event from the satellite service. + * + * @param executor The executor on which the callback will be called. + * @param callback The callback to handle the selected satellite subscription changed event. + * + * @throws SecurityException if the caller doesn't have required permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * + * @hide + */ + @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) + @SatelliteResult public int registerForSelectedNbIotSatelliteSubscriptionChanged( + @NonNull @CallbackExecutor Executor executor, + @NonNull SelectedNbIotSatelliteSubscriptionCallback callback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + ISelectedNbIotSatelliteSubscriptionCallback internalCallback = + new ISelectedNbIotSatelliteSubscriptionCallback.Stub() { + @Override + public void onSelectedNbIotSatelliteSubscriptionChanged( + int selectedSubId) { + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> callback.onSelectedNbIotSatelliteSubscriptionChanged( + selectedSubId))); + } + }; + sSelectedNbIotSatelliteSubscriptionCallbackMap.put(callback, internalCallback); + return telephony.registerForSelectedNbIotSatelliteSubscriptionChanged( + internalCallback); + } else { + throw new IllegalStateException("Telephony service is null."); + } + } catch (RemoteException ex) { + loge("registerForSelectedNbIotSatelliteSubscriptionChanged() RemoteException: " + ex); + ex.rethrowFromSystemServer(); + } + return SATELLITE_RESULT_REQUEST_FAILED; + } + + /** + * Unregisters for selected satellite subscription changed event from the satellite service. If + * callback was not registered before, the request will be ignored. + * + * @param callback The callback that was passed to {@link + * #registerForSelectedNbIotSatelliteSubscriptionChanged(Executor, + * SelectedNbIotSatelliteSubscriptionCallback)}. + * + * @throws SecurityException if the caller doesn't have required permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * @hide + */ + @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) + public void unregisterForSelectedNbIotSatelliteSubscriptionChanged( + @NonNull SelectedNbIotSatelliteSubscriptionCallback callback) { + Objects.requireNonNull(callback); + ISelectedNbIotSatelliteSubscriptionCallback internalCallback = + sSelectedNbIotSatelliteSubscriptionCallbackMap.remove(callback); + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + if (internalCallback != null) { + telephony.unregisterForSelectedNbIotSatelliteSubscriptionChanged( + internalCallback); + } else { + loge("unregisterForSelectedNbIotSatelliteSubscriptionChanged: " + + "No internal callback."); + } + } else { + throw new IllegalStateException("Telephony service is null."); + } + } catch (RemoteException ex) { + loge("unregisterForSelectedNbIotSatelliteSubscriptionChanged() RemoteException: " + + ex); + ex.rethrowFromSystemServer(); + } + } + + /** * Inform whether the device is aligned with the satellite in both real and demo mode. * * In demo mode, framework will send datagram to modem only when device is aligned with diff --git a/telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java b/telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java new file mode 100644 index 000000000000..d8965547a20e --- /dev/null +++ b/telephony/java/android/telephony/satellite/SelectedNbIotSatelliteSubscriptionCallback.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.satellite; + +/** + * A callback class for selected satellite subscription changed events. + * + * @hide + */ +public interface SelectedNbIotSatelliteSubscriptionCallback { + /** + * Called when the selected satellite subscription has changed. + * + * @param selectedSubId The new satellite subscription id. + */ + void onSelectedNbIotSatelliteSubscriptionChanged(int selectedSubId); +} diff --git a/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java index 8a5e7f22888a..c2ac44f0067e 100644 --- a/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java +++ b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java @@ -17,11 +17,13 @@ package android.telephony.satellite; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.IntArray; +import java.util.Arrays; import java.util.Objects; /** @@ -39,16 +41,26 @@ public final class SystemSelectionSpecifier implements Parcelable { * The radio channels to scan as defined in 3GPP TS 25.101 and 36.101. * Maximum length of the vector is 32. */ - @NonNull private IntArray mEarfcs; + @NonNull private IntArray mEarfcns; + + /* The list of satellites configured for the current location */ + @Nullable + private SatelliteInfo[] mSatelliteInfos; + + /* The list of tag IDs associated with the current location */ + @Nullable private IntArray mTagIds; /** * @hide */ public SystemSelectionSpecifier(@NonNull String mccmnc, @NonNull IntArray bands, - @NonNull IntArray earfcs) { + @NonNull IntArray earfcns, @Nullable SatelliteInfo[] satelliteInfos, + @Nullable IntArray tagIds) { mMccMnc = mccmnc; mBands = bands; - mEarfcs = earfcs; + mEarfcns = earfcns; + mSatelliteInfos = satelliteInfos; + mTagIds = tagIds; } private SystemSelectionSpecifier(Parcel in) { @@ -74,10 +86,21 @@ public final class SystemSelectionSpecifier implements Parcelable { out.writeInt(0); } - if (mEarfcs != null && mEarfcs.size() > 0) { - out.writeInt(mEarfcs.size()); - for (int i = 0; i < mEarfcs.size(); i++) { - out.writeInt(mEarfcs.get(i)); + if (mEarfcns != null && mEarfcns.size() > 0) { + out.writeInt(mEarfcns.size()); + for (int i = 0; i < mEarfcns.size(); i++) { + out.writeInt(mEarfcns.get(i)); + } + } else { + out.writeInt(0); + } + + out.writeTypedArray(mSatelliteInfos, flags); + + if (mTagIds != null) { + out.writeInt(mTagIds.size()); + for (int i = 0; i < mTagIds.size(); i++) { + out.writeInt(mTagIds.get(i)); } } else { out.writeInt(0); @@ -115,14 +138,35 @@ public final class SystemSelectionSpecifier implements Parcelable { } sb.append("earfcs:"); - if (mEarfcs != null && mEarfcs.size() > 0) { - for (int i = 0; i < mEarfcs.size(); i++) { - sb.append(mEarfcs.get(i)); + if (mEarfcns != null && mEarfcns.size() > 0) { + for (int i = 0; i < mEarfcns.size(); i++) { + sb.append(mEarfcns.get(i)); sb.append(","); } } else { sb.append("none"); } + + sb.append("mSatelliteInfos:"); + if (mSatelliteInfos != null && mSatelliteInfos.length > 0) { + for (SatelliteInfo satelliteInfo : mSatelliteInfos) { + sb.append(satelliteInfo); + sb.append(","); + } + } else { + sb.append("none"); + } + + sb.append("mTagIds:"); + if (mTagIds != null && mTagIds.size() > 0) { + for (int i = 0; i < mTagIds.size(); i++) { + sb.append(mTagIds.get(i)); + sb.append(","); + } + } else { + sb.append("none"); + } + return sb.toString(); } @@ -133,12 +177,15 @@ public final class SystemSelectionSpecifier implements Parcelable { SystemSelectionSpecifier that = (SystemSelectionSpecifier) o; return Objects.equals(mMccMnc, that.mMccMnc) && Objects.equals(mBands, that.mBands) - && Objects.equals(mEarfcs, that.mEarfcs); + && Objects.equals(mEarfcns, that.mEarfcns) + && (mSatelliteInfos == null ? that.mSatelliteInfos == null : Arrays.equals( + mSatelliteInfos, that.mSatelliteInfos)) + && Objects.equals(mTagIds, that.mTagIds); } @Override public int hashCode() { - return Objects.hash(mMccMnc, mBands, mEarfcs); + return Objects.hash(mMccMnc, mBands, mEarfcns); } @NonNull public String getMccMnc() { @@ -149,8 +196,18 @@ public final class SystemSelectionSpecifier implements Parcelable { return mBands; } - @NonNull public IntArray getEarfcs() { - return mEarfcs; + @NonNull public IntArray getEarfcns() { + return mEarfcns; + } + + @NonNull + public SatelliteInfo[] getSatelliteInfos() { + return mSatelliteInfos; + } + + @NonNull + public IntArray getTagIds() { + return mTagIds; } private void readFromParcel(Parcel in) { @@ -164,11 +221,20 @@ public final class SystemSelectionSpecifier implements Parcelable { } } - mEarfcs = new IntArray(); - int numEarfcs = in.readInt(); - if (numEarfcs > 0) { - for (int i = 0; i < numEarfcs; i++) { - mEarfcs.add(in.readInt()); + mEarfcns = new IntArray(); + int numEarfcns = in.readInt(); + if (numEarfcns > 0) { + for (int i = 0; i < numEarfcns; i++) { + mEarfcns.add(in.readInt()); + } + } + + mSatelliteInfos = in.createTypedArray(SatelliteInfo.CREATOR); + + int numTagIds = in.readInt(); + if (numTagIds > 0) { + for (int i = 0; i < numTagIds; i++) { + mTagIds.add(in.readInt()); } } } diff --git a/telephony/java/android/telephony/satellite/stub/EarfcnRange.aidl b/telephony/java/android/telephony/satellite/stub/EarfcnRange.aidl new file mode 100644 index 000000000000..6e9d19e91fc5 --- /dev/null +++ b/telephony/java/android/telephony/satellite/stub/EarfcnRange.aidl @@ -0,0 +1,32 @@ +/* + * 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.telephony.satellite.stub; + +/** + * @hide + */ +parcelable EarfcnRange { + /** + * The start frequency of the earfcn range and is inclusive in the range + */ + int startEarfcn; + + /** + * The end frequency of the earfcn range and is inclusive in the range. + */ + int endEarfcn; +} diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteInfo.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteInfo.aidl new file mode 100644 index 000000000000..312bd8f5b2af --- /dev/null +++ b/telephony/java/android/telephony/satellite/stub/SatelliteInfo.aidl @@ -0,0 +1,52 @@ +/* + * 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.telephony.satellite.stub; + +import android.telephony.satellite.stub.UUID; +import android.telephony.satellite.stub.SatellitePosition; +import android.telephony.satellite.stub.EarfcnRange; + +/** + * @hide + */ +parcelable SatelliteInfo { + /** + * Unique identification number for the satellite. + * This ID is used to distinguish between different satellites in the network. + */ + UUID id; + + /** + * Position information of a geostationary satellite. + * This includes the longitude and altitude of the satellite. + * If the SatellitePosition is invalid, + * longitudeDegree and altitudeKm will be represented as DOUBLE.NaN. + */ + SatellitePosition position; + + /** + * The frequency bands to scan. + * Bands will be filled only if the whole band is needed. + * Maximum length of the vector is 8. + */ + int[] bands; + + /** + * The supported frequency ranges. Earfcn ranges and earfcns won't overlap. + */ + EarfcnRange[] earfcnRanges; +} diff --git a/telephony/java/android/telephony/satellite/stub/SatellitePosition.aidl b/telephony/java/android/telephony/satellite/stub/SatellitePosition.aidl new file mode 100644 index 000000000000..e87b26cddec5 --- /dev/null +++ b/telephony/java/android/telephony/satellite/stub/SatellitePosition.aidl @@ -0,0 +1,32 @@ +/* + * 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.telephony.satellite.stub; + +/** + * @hide + */ +parcelable SatellitePosition { + /** + * The longitude of the satellite in degrees, ranging from -180 to 180 degrees + */ + double longitudeDegree; + + /** + * The distance from the center of the earth to the satellite, measured in kilometers + */ + double altitudeKm; +} diff --git a/telephony/java/android/telephony/satellite/stub/SystemSelectionSpecifier.aidl b/telephony/java/android/telephony/satellite/stub/SystemSelectionSpecifier.aidl index 22240f6324c9..3aff4cb63167 100644 --- a/telephony/java/android/telephony/satellite/stub/SystemSelectionSpecifier.aidl +++ b/telephony/java/android/telephony/satellite/stub/SystemSelectionSpecifier.aidl @@ -16,6 +16,8 @@ package android.telephony.satellite.stub; +import android.telephony.satellite.stub.SatelliteInfo; + /** * {@hide} */ @@ -24,15 +26,26 @@ parcelable SystemSelectionSpecifier { String mMccMnc; /** - * The frequency bands to scan. Bands and earfcns won't overlap. + * The frequency bands to scan. * Bands will be filled only if the whole band is needed. * Maximum length of the vector is 8. + * The values are populated from the mBands array within the SatelliteInfo[] array, which is + * included in the SystemSelectionSpecifier, for backward compatibility. */ int[] mBands; /** * The radio channels to scan as defined in 3GPP TS 25.101 and 36.101. + * The values are populated from the earfcns defined in the EarfcnRange[] array inside + * SatelliteInfo[], which is included in the SystemSelectionSpecifier, for backward + * compatibility. * Maximum length of the vector is 32. */ int[] mEarfcs; + + /* The list of satellites configured for the current location */ + SatelliteInfo[] satelliteInfos; + + /* The list of tag IDs associated with the current location */ + int[] tagIds; } diff --git a/telephony/java/android/telephony/satellite/stub/UUID.aidl b/telephony/java/android/telephony/satellite/stub/UUID.aidl new file mode 100644 index 000000000000..004a507bb16e --- /dev/null +++ b/telephony/java/android/telephony/satellite/stub/UUID.aidl @@ -0,0 +1,32 @@ +/* + * 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.telephony.satellite.stub; + +/** + * @hide + */ +parcelable UUID { + /* + * The most significant 64 bits of this UUID. + */ + long mostSigBits; + + /* + * The least significant 64 bits of this UUID. + */ + long leastSigBits; +} diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index d22e9fa20101..b739666c3f1b 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -76,6 +76,7 @@ import android.telephony.satellite.ISatelliteTransmissionUpdateCallback; import android.telephony.satellite.ISatelliteProvisionStateCallback; import android.telephony.satellite.ISatelliteSupportedStateCallback; import android.telephony.satellite.ISatelliteModemStateCallback; +import android.telephony.satellite.ISelectedNbIotSatelliteSubscriptionCallback; import android.telephony.satellite.NtnSignalStrength; import android.telephony.satellite.SatelliteCapabilities; import android.telephony.satellite.SatelliteDatagram; @@ -3030,6 +3031,30 @@ interface ITelephony { void requestSelectedNbIotSatelliteSubscriptionId(in ResultReceiver receiver); /** + * Registers for selected satellite subscription changed event from the satellite service. + * + * @param executor The executor on which the callback will be called. + * @param callback The callback to handle the satellite subscription changed event. + */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + + "android.Manifest.permission.SATELLITE_COMMUNICATION)") + int registerForSelectedNbIotSatelliteSubscriptionChanged( + in ISelectedNbIotSatelliteSubscriptionCallback callback); + + /** + * Unregisters for selected satellite subscription changed event from the satellite service. If + * callback was not registered before, the request will be ignored. + * + * @param callback The callback that was passed to {@link + * #registerForSelectedNbIotSatelliteSubscriptionChanged(Executor, + * SelectedNbIotSatelliteSubscriptionCallback)}. + */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + + "android.Manifest.permission.SATELLITE_COMMUNICATION)") + void unregisterForSelectedNbIotSatelliteSubscriptionChanged( + in ISelectedNbIotSatelliteSubscriptionCallback callback); + + /** * Inform whether the device is aligned with the satellite in both real and demo mode. * * @param isAligned {@true} Device is aligned with the satellite. @@ -3519,4 +3544,15 @@ interface ITelephony { * @hide */ void setNtnSmsSupported(boolean ntnSmsSupported); + + /** + * Returns carrier id maps to the passing {@link CarrierIdentifier}. + * + * @param {@link CarrierIdentifier}. + * + * @return carrier id from passing {@link CarrierIdentifier} or {@link #UNKNOWN_CARRIER_ID} + * if the carrier cannot be identified + * @hide + */ + int getCarrierIdFromIdentifier(in CarrierIdentifier carrierIdentifier); } diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java index bfce3d276a2d..6d818d7287b0 100644 --- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java +++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java @@ -159,7 +159,7 @@ public class BatteryUsageStatsPerfTest { private static BatteryUsageStats buildBatteryUsageStats() { final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true, false, false, false, 0) + new BatteryUsageStats.Builder(new String[]{"FOO"}, false, false, false, 0) .setBatteryCapacity(4000) .setDischargePercentage(20) .setDischargedPowerRange(1000, 2000) @@ -182,8 +182,7 @@ public class BatteryUsageStatsPerfTest { .setTimeInProcessStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000); for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT; componentId++) { - consumerBuilder.addConsumedPower(componentId, componentId * 123.0, - BatteryConsumer.POWER_MODEL_POWER_PROFILE); + consumerBuilder.addConsumedPower(componentId, componentId * 123.0); consumerBuilder.addUsageDurationMillis(componentId, componentId * 1000); } diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt index 64328275085d..e0900a64c52f 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt @@ -23,6 +23,7 @@ import android.graphics.Rect import android.graphics.Region import android.os.SystemClock import android.platform.uiautomatorhelpers.DeviceHelpers +import android.tools.PlatformConsts import android.tools.device.apphelpers.IStandardAppHelper import android.tools.helpers.SYSTEMUI_PACKAGE import android.tools.traces.parsers.WindowManagerStateHelper @@ -163,7 +164,10 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : .StateSyncBuilder() .withAppTransitionIdle() .apply { - if (isPip) withPipShown() else withWindowSurfaceDisappeared(innerHelper) + if (isPip) withPipShown() + else + withWindowSurfaceDisappeared(innerHelper) + .withActivityState(innerHelper, PlatformConsts.STATE_STOPPED) } .waitForAndVerify() } diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt index 931e4f88aa8d..db4838ee6092 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt @@ -250,18 +250,13 @@ open class PipAppHelper(instrumentation: Instrumentation) : wmHelper, launchedAppComponentMatcherOverride, action, - stringExtras, - waitConditionsBuilder = - wmHelper - .StateSyncBuilder() - .add(ConditionsFactory.isWMStateComplete()) - .withAppTransitionIdle() - .add(ConditionsFactory.hasPipWindow()) + stringExtras ) wmHelper .StateSyncBuilder() .withWindowSurfaceAppeared(this) + .add(ConditionsFactory.isWMStateComplete()) .withPipShown() .waitForAndVerify() } diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java index fe974e3da3be..af87bf724a30 100644 --- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java +++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java @@ -1014,7 +1014,7 @@ public class CrashRecoveryTest { triggerFailureCount = 1; } for (int i = 0; i < triggerFailureCount; i++) { - watchdog.onPackageFailure(packages, failureReason); + watchdog.notifyPackageFailure(packages, failureReason); } mTestLooper.dispatchAll(); if (Flags.recoverabilityDetection()) { diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index c25bed21a602..5a8a6bedf9eb 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -392,7 +392,7 @@ public class PackageWatchdogTest { // Then fail APP_A below the threshold for (int i = 0; i < watchdog.getTriggerFailureCount() - 1; i++) { - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); } @@ -1025,14 +1025,14 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); // Fail APP_A below the threshold which should not trigger package failures for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) { - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); } mTestLooper.dispatchAll(); assertThat(observer.mHealthCheckFailedPackages).isEmpty(); // One more to trigger the package failure - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); assertThat(observer.mHealthCheckFailedPackages).containsExactly(APP_A); @@ -1051,10 +1051,10 @@ public class PackageWatchdogTest { TestObserver observer = new TestObserver(OBSERVER_NAME_1); watchdog.startObservingHealth(observer, Arrays.asList(APP_A, APP_B), Long.MAX_VALUE); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS + 1); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); @@ -1062,10 +1062,10 @@ public class PackageWatchdogTest { // DEFAULT_TRIGGER_FAILURE_DURATION_MS. assertThat(observer.mHealthCheckFailedPackages).isEmpty(); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)), + watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS - 1); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)), + watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); @@ -1129,17 +1129,17 @@ public class PackageWatchdogTest { watchdog.startObservingHealth(observer, Arrays.asList(APP_A), Long.MAX_VALUE); // Raise 2 failures at t=0 and t=900 respectively - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); moveTimeForwardAndDispatch(900); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); // Raise 2 failures at t=1100 moveTimeForwardAndDispatch(200); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + watchdog.notifyPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); @@ -1433,13 +1433,13 @@ public class PackageWatchdogTest { TestObserver observer = new TestObserver(OBSERVER_NAME_1); watchdog.startObservingHealth(observer, List.of(APP_A), SHORT_DURATION); for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) { - watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)), + watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); } mTestLooper.dispatchAll(); assertThat(observer.mMitigatedPackages).isEmpty(); watchdog.startObservingHealth(observer, List.of(APP_A), LONG_DURATION); - watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)), + watchdog.notifyPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); mTestLooper.dispatchAll(); assertThat(observer.mMitigatedPackages).isEqualTo(List.of(APP_A)); @@ -1737,7 +1737,7 @@ public class PackageWatchdogTest { triggerFailureCount = 1; } for (int i = 0; i < triggerFailureCount; i++) { - watchdog.onPackageFailure(packages, failureReason); + watchdog.notifyPackageFailure(packages, failureReason); } mTestLooper.dispatchAll(); if (Flags.recoverabilityDetection()) { diff --git a/tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogImplTest.java b/tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogImplTest.java index 8913e8c1996e..05308464cb9b 100644 --- a/tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogImplTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogImplTest.java @@ -89,7 +89,7 @@ public class LegacyProtoLogImplTest { //noinspection ResultOfMethodCallIgnored mFile.delete(); mProtoLog = new LegacyProtoLogImpl(mFile, mViewerConfigFilename, - 1024 * 1024, mReader, 1024, () -> {}); + 1024 * 1024, mReader, 1024, (instance) -> {}); } @After diff --git a/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java b/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java index 44641f7a1e12..ed256e72b415 100644 --- a/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/ProcessedPerfettoProtoLogImplTest.java @@ -41,6 +41,7 @@ import android.tools.traces.io.ResultWriter; import android.tools.traces.monitors.PerfettoTraceMonitor; import android.tools.traces.protolog.ProtoLogTrace; import android.tracing.perfetto.DataSource; +import android.tracing.perfetto.DataSourceParams; import androidx.test.platform.app.InstrumentationRegistry; @@ -95,14 +96,14 @@ public class ProcessedPerfettoProtoLogImplTest { ); private static ProtoLogConfigurationService sProtoLogConfigurationService; + private static ProtoLogDataSource sTestDataSource; private static PerfettoProtoLogImpl sProtoLog; private static Protolog.ProtoLogViewerConfig.Builder sViewerConfigBuilder; - private static Runnable sCacheUpdater; + private static ProtoLogCacheUpdater sCacheUpdater; private static ProtoLogViewerConfigReader sReader; - public ProcessedPerfettoProtoLogImplTest() throws IOException { - } + public ProcessedPerfettoProtoLogImplTest() throws IOException { } @BeforeClass public static void setUp() throws Exception { @@ -155,12 +156,18 @@ public class ProcessedPerfettoProtoLogImplTest { .thenAnswer(it -> new AutoClosableProtoInputStream( sViewerConfigBuilder.build().toByteArray())); - sCacheUpdater = () -> {}; + sCacheUpdater = (instance) -> {}; sReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider)); + sTestDataSource = new ProtoLogDataSource(TEST_PROTOLOG_DATASOURCE_NAME); + DataSourceParams params = + new DataSourceParams.Builder() + .setBufferExhaustedPolicy( + DataSourceParams + .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP) + .build(); + sTestDataSource.register(params); + busyWaitForDataSourceRegistration(TEST_PROTOLOG_DATASOURCE_NAME); - final ProtoLogDataSourceBuilder dataSourceBuilder = - (onStart, onFlush, onStop) -> new ProtoLogDataSource( - onStart, onFlush, onStop, TEST_PROTOLOG_DATASOURCE_NAME); final ViewerConfigFileTracer tracer = (dataSource, viewerConfigFilePath) -> { Utils.dumpViewerConfig(dataSource, () -> { if (!viewerConfigFilePath.equals(MOCK_VIEWER_CONFIG_FILE)) { @@ -171,14 +178,13 @@ public class ProcessedPerfettoProtoLogImplTest { }); }; sProtoLogConfigurationService = - new ProtoLogConfigurationServiceImpl(dataSourceBuilder, tracer); + new ProtoLogConfigurationServiceImpl(sTestDataSource, tracer); - sProtoLog = new ProcessedPerfettoProtoLogImpl( + sProtoLog = new ProcessedPerfettoProtoLogImpl(sTestDataSource, MOCK_VIEWER_CONFIG_FILE, viewerConfigInputStreamProvider, sReader, - () -> sCacheUpdater.run(), TestProtoLogGroup.values(), dataSourceBuilder, + (instance) -> sCacheUpdater.update(instance), TestProtoLogGroup.values(), sProtoLogConfigurationService); - - busyWaitForDataSourceRegistration(TEST_PROTOLOG_DATASOURCE_NAME); + sProtoLog.enable(); } @Before @@ -606,7 +612,7 @@ public class ProcessedPerfettoProtoLogImplTest { @Test public void cacheIsUpdatedWhenTracesStartAndStop() { final AtomicInteger cacheUpdateCallCount = new AtomicInteger(0); - sCacheUpdater = cacheUpdateCallCount::incrementAndGet; + sCacheUpdater = (instance) -> cacheUpdateCallCount.incrementAndGet(); PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder() .enableProtoLog(true, diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtologDataSourceTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtologDataSourceTest.java index ce519b7a1576..49249333b72b 100644 --- a/tests/Tracing/src/com/android/internal/protolog/ProtologDataSourceTest.java +++ b/tests/Tracing/src/com/android/internal/protolog/ProtologDataSourceTest.java @@ -67,9 +67,6 @@ public class ProtologDataSourceTest { @Test public void allEnabledTraceMode() { - final ProtoLogDataSource ds = - new ProtoLogDataSource((idx, c) -> {}, () -> {}, (idx, c) -> {}); - final ProtoLogDataSource.TlsState tlsState = createTlsState( DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig( ProtologConfig.ProtoLogConfig.newBuilder() @@ -154,8 +151,7 @@ public class ProtologDataSourceTest { private ProtoLogDataSource.TlsState createTlsState( DataSourceConfigOuterClass.DataSourceConfig config) { - final ProtoLogDataSource ds = - Mockito.spy(new ProtoLogDataSource((idx, c) -> {}, () -> {}, (idx, c) -> {})); + final ProtoLogDataSource ds = Mockito.spy(new ProtoLogDataSource()); ProtoInputStream configStream = new ProtoInputStream(config.toByteArray()); final ProtoLogDataSource.Instance dsInstance = Mockito.spy( diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h index 40ff5b633d97..03da460affb5 100644 --- a/tools/aapt2/util/Util.h +++ b/tools/aapt2/util/Util.h @@ -111,8 +111,8 @@ const char* GetToolName(); // Retrieves the build fingerprint of aapt2. std::string GetToolFingerprint(); -template <typename T> -typename std::enable_if<std::is_arithmetic<T>::value, int>::type compare(const T& a, const T& b) { +template <std::integral T> +int compare(T a, T b) { if (a < b) { return -1; } else if (a > b) { @@ -123,10 +123,7 @@ typename std::enable_if<std::is_arithmetic<T>::value, int>::type compare(const T // Makes a std::unique_ptr<> with the template parameter inferred by the compiler. // This will be present in C++14 and can be removed then. -template <typename T, class... Args> -std::unique_ptr<T> make_unique(Args&&... args) { - return std::unique_ptr<T>(new T{std::forward<Args>(args)...}); -} +using std::make_unique; // Writes a set of items to the std::ostream, joining the times with the provided separator. template <typename Container> diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt index 108942ee754a..9222ff4bf52c 100644 --- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt +++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt @@ -16,6 +16,7 @@ package com.android.protolog.tool +import com.android.internal.protolog.common.IProtoLog import com.android.internal.protolog.common.LogLevel import com.android.internal.protolog.common.ProtoLogToolInjected import com.android.protolog.tool.CommandOptions.Companion.USAGE @@ -319,6 +320,7 @@ object ProtoLogTool { MethodCallExpr() .setName("isEnabled") .setArguments(NodeList( + NameExpr("protoLogInstance"), FieldAccessExpr() .setScope(NameExpr(protoLogGroupsClassName)) .setName(group.value.name), @@ -332,6 +334,7 @@ object ProtoLogTool { } cacheClass.addMethod("update").setPrivate(true).setStatic(true) + .addParameter(IProtoLog::class.java, "protoLogInstance") .setBody(updateBlockStmt) classDeclaration.addMember(cacheClass) diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp index e6d0a3d4149f..2ebede39504e 100644 --- a/tools/systemfeatures/Android.bp +++ b/tools/systemfeatures/Android.bp @@ -13,6 +13,7 @@ java_library_host { srcs: [ "src/**/*.java", "src/**/*.kt", + ":framework-metalava-annotations", ], static_libs: [ "guava", @@ -26,6 +27,12 @@ java_binary_host { static_libs: ["systemfeatures-gen-lib"], } +java_plugin { + name: "systemfeatures-metadata-processor", + processor_class: "com.android.systemfeatures.SystemFeaturesMetadataProcessor", + static_libs: ["systemfeatures-gen-lib"], +} + genrule { name: "systemfeatures-gen-tests-srcs", cmd: "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwNoFeatures --readonly=false > $(location RwNoFeatures.java) && " + @@ -61,6 +68,7 @@ java_test_host { "systemfeatures-gen-lib", "truth", ], + plugins: ["systemfeatures-metadata-processor"], } // Rename the goldens as they may be copied into the source tree, and we don't diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt new file mode 100644 index 000000000000..100d869a663f --- /dev/null +++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt @@ -0,0 +1,110 @@ +/* + * 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.systemfeatures + +import android.annotation.SdkConstant +import com.squareup.javapoet.FieldSpec +import com.squareup.javapoet.JavaFile +import com.squareup.javapoet.TypeSpec +import java.io.IOException +import javax.annotation.processing.AbstractProcessor +import javax.annotation.processing.ProcessingEnvironment +import javax.annotation.processing.RoundEnvironment +import javax.lang.model.SourceVersion +import javax.lang.model.element.Modifier +import javax.lang.model.element.TypeElement +import javax.tools.Diagnostic + +/* + * Simple Java code generator for computing metadata for system features. + * + * <p>The output is a single class file, `com.android.internal.pm.SystemFeaturesMetadata`, with + * properties computed from feature constant definitions in the PackageManager class. This + * class is only produced if the processed environment includes PackageManager; all other + * invocations are ignored. + */ +class SystemFeaturesMetadataProcessor : AbstractProcessor() { + + private lateinit var packageManagerType: TypeElement + + override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latestSupported() + + override fun getSupportedAnnotationTypes() = setOf(SDK_CONSTANT_ANNOTATION_NAME) + + override fun init(processingEnv: ProcessingEnvironment) { + super.init(processingEnv) + packageManagerType = + processingEnv.elementUtils.getTypeElement("android.content.pm.PackageManager")!! + } + + override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean { + if (roundEnv.processingOver()) { + return false + } + + // We're only interested in feature constants defined in PackageManager. + var featureCount = 0 + roundEnv.getElementsAnnotatedWith(SdkConstant::class.java).forEach { + if ( + it.enclosingElement == packageManagerType && + it.getAnnotation(SdkConstant::class.java).value == + SdkConstant.SdkConstantType.FEATURE + ) { + featureCount++ + } + } + + if (featureCount == 0) { + // This is fine, and happens for any environment that doesn't include PackageManager. + return false + } + + val systemFeatureMetadata = + TypeSpec.classBuilder("SystemFeaturesMetadata") + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addJavadoc("@hide") + .addField( + FieldSpec.builder(Int::class.java, "SDK_FEATURE_COUNT") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) + .addJavadoc( + "The number of `@SdkConstant` features defined in PackageManager." + ) + .addJavadoc("@hide") + .initializer("\$L", featureCount) + .build() + ) + .build() + + try { + JavaFile.builder("com.android.internal.pm", systemFeatureMetadata) + .skipJavaLangImports(true) + .build() + .writeTo(processingEnv.filer) + } catch (e: IOException) { + processingEnv.messager.printMessage( + Diagnostic.Kind.ERROR, + "Failed to write file: ${e.message}", + ) + } + + return true + } + + companion object { + private val SDK_CONSTANT_ANNOTATION_NAME = SdkConstant::class.qualifiedName + } +} diff --git a/tools/systemfeatures/tests/src/PackageManager.java b/tools/systemfeatures/tests/src/PackageManager.java index db670482065a..839a9377476d 100644 --- a/tools/systemfeatures/tests/src/PackageManager.java +++ b/tools/systemfeatures/tests/src/PackageManager.java @@ -16,14 +16,33 @@ package android.content.pm; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; + /** Stub for testing */ public class PackageManager { + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_AUTO = "automotive"; + + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_PC = "pc"; + + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_VULKAN = "vulkan"; + + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_WATCH = "watch"; + + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_WIFI = "wifi"; + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String FEATURE_INTENT_CATEGORY = "intent_category_with_feature_name_prefix"; + + public static final String FEATURE_NOT_ANNOTATED = "not_annotated"; + + public static final String NOT_FEATURE = "not_feature"; + /** @hide */ public boolean hasSystemFeature(String featureName, int version) { return false; diff --git a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java new file mode 100644 index 000000000000..4ffb5b979d75 --- /dev/null +++ b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.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 com.android.systemfeatures; + +import static com.google.common.truth.Truth.assertThat; + +import com.android.internal.pm.SystemFeaturesMetadata; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class SystemFeaturesMetadataProcessorTest { + + @Test + public void testSdkFeatureCount() { + // See the fake PackageManager definition in this directory. + // It defines 5 annotated features, and any/all other constants should be ignored. + assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(5); + } +} |