diff options
224 files changed, 5779 insertions, 4268 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java index 6af24be3a372..299ad66a882c 100644 --- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java +++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java @@ -91,6 +91,13 @@ public class EconomyManager { } } + public static final String KEY_ENABLE_TARE = "enable_tare"; + public static final String KEY_ENABLE_POLICY_ALARM = "enable_policy_alarm"; + public static final String KEY_ENABLE_POLICY_JOB_SCHEDULER = "enable_policy_job"; + public static final boolean DEFAULT_ENABLE_TARE = true; + public static final boolean DEFAULT_ENABLE_POLICY_ALARM = true; + public static final boolean DEFAULT_ENABLE_POLICY_JOB_SCHEDULER = true; + // Keys for AlarmManager TARE factors /** @hide */ public static final String KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED = diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index c622259ef7dc..232d3c9aaa53 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -39,6 +39,7 @@ import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROU import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED; import static android.os.UserHandle.USER_SYSTEM; +import static com.android.server.SystemClockTime.TIME_CONFIDENCE_HIGH; import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH; import static com.android.server.SystemTimeZone.getTimeZoneId; import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX; @@ -58,9 +59,9 @@ import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_R import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_UNDEFINED; import android.Manifest; +import android.annotation.CurrentTimeMillisLong; +import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManagerInternal; @@ -89,7 +90,6 @@ import android.os.BatteryManager; import android.os.Binder; import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -103,7 +103,6 @@ import android.os.ServiceManager; import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.ThreadLocalWorkSource; import android.os.Trace; import android.os.UserHandle; @@ -147,6 +146,8 @@ import com.android.server.DeviceIdleInternal; import com.android.server.EventLogTags; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; +import com.android.server.SystemClockTime; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.SystemTimeZone; @@ -883,9 +884,11 @@ public class AlarmManagerService extends SystemService { mInjector.registerDeviceConfigListener(this); final EconomyManagerInternal economyManagerInternal = LocalServices.getService(EconomyManagerInternal.class); - economyManagerInternal.registerTareStateChangeListener(this); + economyManagerInternal.registerTareStateChangeListener(this, + AlarmManagerEconomicPolicy.POLICY_ALARM); onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER)); - updateTareSettings(economyManagerInternal.isEnabled()); + updateTareSettings( + economyManagerInternal.isEnabled(AlarmManagerEconomicPolicy.POLICY_ALARM)); } public void updateAllowWhileIdleWhitelistDurationLocked() { @@ -1417,7 +1420,7 @@ public class AlarmManagerService extends SystemService { private long convertToElapsed(long when, int type) { if (isRtc(type)) { - when -= mInjector.getCurrentTimeMillis() - mInjector.getElapsedRealtime(); + when -= mInjector.getCurrentTimeMillis() - mInjector.getElapsedRealtimeMillis(); } return when; } @@ -1577,7 +1580,7 @@ public class AlarmManagerService extends SystemService { alarmsToDeliver = alarmsForUid; mPendingBackgroundAlarms.remove(uid); } - deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime()); + deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtimeMillis()); } /** @@ -1594,7 +1597,8 @@ public class AlarmManagerService extends SystemService { mPendingBackgroundAlarms, alarmsToDeliver, this::isBackgroundRestricted); if (alarmsToDeliver.size() > 0) { - deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime()); + deliverPendingBackgroundAlarmsLocked( + alarmsToDeliver, mInjector.getElapsedRealtimeMillis()); } } @@ -1908,17 +1912,8 @@ public class AlarmManagerService extends SystemService { mInjector.syncKernelTimeZoneOffset(); } - // Ensure that we're booting with a halfway sensible current time. Use the - // most recent of Build.TIME, the root file system's timestamp, and the - // value of the ro.build.date.utc system property (which is in seconds). - final long systemBuildTime = Long.max( - 1000L * SystemProperties.getLong("ro.build.date.utc", -1L), - Long.max(Environment.getRootDirectory().lastModified(), Build.TIME)); - if (mInjector.getCurrentTimeMillis() < systemBuildTime) { - Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis() - + ", advancing to build time " + systemBuildTime); - mInjector.setKernelTime(systemBuildTime); - } + // Ensure that we're booting with a halfway sensible current time. + mInjector.initializeTimeIfRequired(); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); // Determine SysUI's uid @@ -2133,21 +2128,18 @@ public class AlarmManagerService extends SystemService { } } - boolean setTimeImpl(long millis) { - if (!mInjector.isAlarmDriverPresent()) { - Slog.w(TAG, "Not setting time since no alarm driver is available."); - return false; - } - + boolean setTimeImpl( + @CurrentTimeMillisLong long newSystemClockTimeMillis, @TimeConfidence int confidence, + @NonNull String logMsg) { synchronized (mLock) { - final long currentTimeMillis = mInjector.getCurrentTimeMillis(); - mInjector.setKernelTime(millis); + final long oldSystemClockTimeMillis = mInjector.getCurrentTimeMillis(); + mInjector.setCurrentTimeMillis(newSystemClockTimeMillis, confidence, logMsg); if (KERNEL_TIME_ZONE_SYNC_ENABLED) { // Changing the time may cross a DST transition; sync the kernel offset if needed. final TimeZone timeZone = TimeZone.getTimeZone(SystemTimeZone.getTimeZoneId()); - final int currentTzOffset = timeZone.getOffset(currentTimeMillis); - final int newTzOffset = timeZone.getOffset(millis); + final int currentTzOffset = timeZone.getOffset(oldSystemClockTimeMillis); + final int newTzOffset = timeZone.getOffset(newSystemClockTimeMillis); if (currentTzOffset != newTzOffset) { Slog.i(TAG, "Timezone offset has changed, updating kernel timezone"); mInjector.setKernelTimeZoneOffset(newTzOffset); @@ -2260,7 +2252,7 @@ public class AlarmManagerService extends SystemService { triggerAtTime = 0; } - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long nominalTrigger = convertToElapsed(triggerAtTime, type); // Try to prevent spamming by making sure apps aren't firing alarms in the immediate future final long minTrigger = nowElapsed @@ -2383,7 +2375,7 @@ public class AlarmManagerService extends SystemService { // No need to fuzz as this is already earlier than the coming wake-from-idle. return changedBeforeFuzz; } - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long futurity = upcomingWakeFromIdle - nowElapsed; if (futurity <= mConstants.MIN_DEVICE_IDLE_FUZZ) { @@ -2405,7 +2397,7 @@ public class AlarmManagerService extends SystemService { * @return {@code true} if the alarm delivery time was updated. */ private boolean adjustDeliveryTimeBasedOnBatterySaver(Alarm alarm) { - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); if (isExemptFromBatterySaver(alarm)) { return false; } @@ -2472,7 +2464,7 @@ public class AlarmManagerService extends SystemService { * @return {@code true} if the alarm delivery time was updated. */ private boolean adjustDeliveryTimeBasedOnDeviceIdle(Alarm alarm) { - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); if (mPendingIdleUntil == null || mPendingIdleUntil == alarm) { return alarm.setPolicyElapsed(DEVICE_IDLE_POLICY_INDEX, nowElapsed); } @@ -2527,7 +2519,7 @@ public class AlarmManagerService extends SystemService { * adjustments made in this call. */ private boolean adjustDeliveryTimeBasedOnBucketLocked(Alarm alarm) { - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); if (mConstants.USE_TARE_POLICY || isExemptFromAppStandby(alarm) || mAppStandbyParole) { return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed); } @@ -2587,7 +2579,7 @@ public class AlarmManagerService extends SystemService { * adjustments made in this call. */ private boolean adjustDeliveryTimeBasedOnTareLocked(Alarm alarm) { - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); if (!mConstants.USE_TARE_POLICY || isExemptFromTare(alarm) || hasEnoughWealthLocked(alarm)) { return alarm.setPolicyElapsed(TARE_POLICY_INDEX, nowElapsed); @@ -2643,7 +2635,7 @@ public class AlarmManagerService extends SystemService { ent.pkg = a.sourcePackage; ent.tag = a.statsTag; ent.op = "START IDLE"; - ent.elapsedRealtime = mInjector.getElapsedRealtime(); + ent.elapsedRealtime = mInjector.getElapsedRealtimeMillis(); ent.argRealtime = a.getWhenElapsed(); mAllowWhileIdleDispatches.add(ent); } @@ -2719,6 +2711,13 @@ public class AlarmManagerService extends SystemService { } @Override + public void setTime( + @CurrentTimeMillisLong long unixEpochTimeMillis, int confidence, + String logMsg) { + setTimeImpl(unixEpochTimeMillis, confidence, logMsg); + } + + @Override public void registerInFlightListener(InFlightListener callback) { synchronized (mLock) { mInFlightListeners.add(callback); @@ -2987,12 +2986,16 @@ public class AlarmManagerService extends SystemService { } @Override - public boolean setTime(long millis) { + public boolean setTime(@CurrentTimeMillisLong long millis) { getContext().enforceCallingOrSelfPermission( "android.permission.SET_TIME", "setTime"); - return setTimeImpl(millis); + // The public API (and the shell command that also uses this method) have no concept + // of confidence, but since the time should come either from apps working on behalf of + // the user or a developer, confidence is assumed "high". + final int timeConfidence = TIME_CONFIDENCE_HIGH; + return setTimeImpl(millis, timeConfidence, "AlarmManager.setTime() called"); } @Override @@ -3142,7 +3145,7 @@ public class AlarmManagerService extends SystemService { pw.println(); } - final long nowELAPSED = mInjector.getElapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtimeMillis(); final long nowUPTIME = SystemClock.uptimeMillis(); final long nowRTC = mInjector.getCurrentTimeMillis(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); @@ -3618,7 +3621,7 @@ public class AlarmManagerService extends SystemService { synchronized (mLock) { final long nowRTC = mInjector.getCurrentTimeMillis(); - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); proto.write(AlarmManagerServiceDumpProto.CURRENT_TIME, nowRTC); proto.write(AlarmManagerServiceDumpProto.ELAPSED_REALTIME, nowElapsed); proto.write(AlarmManagerServiceDumpProto.LAST_TIME_CHANGE_CLOCK_TIME, @@ -3956,7 +3959,7 @@ public class AlarmManagerService extends SystemService { void rescheduleKernelAlarmsLocked() { // Schedule the next upcoming wakeup alarm. If there is a deliverable batch // prior to that which contains no wakeups, we schedule that as well. - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); long nextNonWakeup = 0; if (mAlarmStore.size() > 0) { final long firstWakeup = mAlarmStore.getNextWakeupDeliveryTime(); @@ -4069,7 +4072,7 @@ public class AlarmManagerService extends SystemService { @GuardedBy("mLock") private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms, int reason) { final long nowRtc = mInjector.getCurrentTimeMillis(); - final long nowElapsed = mInjector.getElapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(whichAlarms); final boolean removedFromStore = !removedAlarms.isEmpty(); @@ -4208,7 +4211,7 @@ public class AlarmManagerService extends SystemService { void interactiveStateChangedLocked(boolean interactive) { if (mInteractive != interactive) { mInteractive = interactive; - final long nowELAPSED = mInjector.getElapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtimeMillis(); if (interactive) { if (mPendingNonWakeupAlarms.size() > 0) { final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime; @@ -4310,7 +4313,6 @@ public class AlarmManagerService extends SystemService { private static native void close(long nativeData); private static native int set(long nativeData, int type, long seconds, long nanoseconds); private static native int waitForAlarm(long nativeData); - private static native int setKernelTime(long nativeData, long millis); /* * b/246256335: The @Keep ensures that the native definition is kept even when the optimizer can @@ -4357,7 +4359,7 @@ public class AlarmManagerService extends SystemService { ent.pkg = alarm.sourcePackage; ent.tag = alarm.statsTag; ent.op = "END IDLE"; - ent.elapsedRealtime = mInjector.getElapsedRealtime(); + ent.elapsedRealtime = mInjector.getElapsedRealtimeMillis(); ent.argRealtime = alarm.getWhenElapsed(); mAllowWhileIdleDispatches.add(ent); } @@ -4602,20 +4604,27 @@ public class AlarmManagerService extends SystemService { setKernelTimeZoneOffset(utcOffsetMillis); } - void setKernelTime(long millis) { - if (mNativeData != 0) { - AlarmManagerService.setKernelTime(mNativeData, millis); - } + void initializeTimeIfRequired() { + SystemClockTime.initializeIfRequired(); + } + + void setCurrentTimeMillis( + @CurrentTimeMillisLong long unixEpochMillis, + @TimeConfidence int confidence, + @NonNull String logMsg) { + SystemClockTime.setTimeAndConfidence(unixEpochMillis, confidence, logMsg); } void close() { AlarmManagerService.close(mNativeData); } - long getElapsedRealtime() { + @ElapsedRealtimeLong + long getElapsedRealtimeMillis() { return SystemClock.elapsedRealtime(); } + @CurrentTimeMillisLong long getCurrentTimeMillis() { return System.currentTimeMillis(); } @@ -4665,7 +4674,7 @@ public class AlarmManagerService extends SystemService { while (true) { int result = mInjector.waitForAlarm(); final long nowRTC = mInjector.getCurrentTimeMillis(); - final long nowELAPSED = mInjector.getElapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtimeMillis(); synchronized (mLock) { mLastWakeup = nowELAPSED; } @@ -4913,7 +4922,7 @@ public class AlarmManagerService extends SystemService { // this way, so WAKE_UP alarms will be delivered only when the device is awake. ArrayList<Alarm> triggerList = new ArrayList<Alarm>(); synchronized (mLock) { - final long nowELAPSED = mInjector.getElapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtimeMillis(); triggerAlarmsLocked(triggerList, nowELAPSED); updateNextAlarmClockLocked(); } @@ -5090,7 +5099,7 @@ public class AlarmManagerService extends SystemService { flags |= mConstants.TIME_TICK_ALLOWED_WHILE_IDLE ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED : 0; - setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0, + setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtimeMillis() + tickEventDelay, 0, 0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null, Process.myUid(), "android", null, EXACT_ALLOW_REASON_ALLOW_LIST); @@ -5277,7 +5286,7 @@ public class AlarmManagerService extends SystemService { } synchronized (mLock) { mTemporaryQuotaReserve.replenishQuota(packageName, userId, quotaBump, - mInjector.getElapsedRealtime()); + mInjector.getElapsedRealtimeMillis()); } mHandler.obtainMessage(AlarmHandler.TEMPORARY_QUOTA_CHANGED, userId, -1, packageName).sendToTarget(); @@ -5439,7 +5448,7 @@ public class AlarmManagerService extends SystemService { } private void updateStatsLocked(InFlight inflight) { - final long nowELAPSED = mInjector.getElapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtimeMillis(); BroadcastStats bs = inflight.mBroadcastStats; bs.nesting--; if (bs.nesting <= 0) { 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 1775d908e21b..f5c0ed9f03f7 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -116,6 +116,7 @@ import com.android.server.job.restrictions.JobRestriction; import com.android.server.job.restrictions.ThermalStatusRestriction; import com.android.server.pm.UserManagerInternal; import com.android.server.tare.EconomyManagerInternal; +import com.android.server.tare.JobSchedulerEconomicPolicy; import com.android.server.usage.AppStandbyInternal; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import com.android.server.utils.quota.Categorizer; @@ -373,10 +374,12 @@ public class JobSchedulerService extends com.android.server.SystemService JobSchedulerBackgroundThread.getExecutor(), this); final EconomyManagerInternal economyManagerInternal = LocalServices.getService(EconomyManagerInternal.class); - economyManagerInternal.registerTareStateChangeListener(this); + economyManagerInternal + .registerTareStateChangeListener(this, JobSchedulerEconomicPolicy.POLICY_JOB); // Load all the constants. synchronized (mLock) { - mConstants.updateTareSettingsLocked(economyManagerInternal.isEnabled()); + mConstants.updateTareSettingsLocked( + economyManagerInternal.isEnabled(JobSchedulerEconomicPolicy.POLICY_JOB)); } onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER)); } 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 78ab06c9e332..ff4d26dc807d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java @@ -1095,6 +1095,8 @@ public final class JobStore { } if ((netCapabilitiesIntArray != null) && (netTransportTypesIntArray != null)) { + // S+ format. No capability or transport validation since the values should be in + // line with what's defined in the Connectivity mainline module. final NetworkRequest.Builder builder = new NetworkRequest.Builder() .clearCapabilities(); @@ -1111,6 +1113,7 @@ public final class JobStore { } jobBuilder.setRequiredNetwork(builder.build()); } else if (netCapabilitiesLong != null && netTransportTypesLong != null) { + // Format used on R- builds. Drop any unexpected capabilities and transports. final NetworkRequest.Builder builder = new NetworkRequest.Builder() .clearCapabilities(); final int maxNetCapabilityInR = NET_CAPABILITY_TEMPORARILY_NOT_METERED; diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java index 12ec9a4624e7..7a13e3f1fad7 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java @@ -1196,7 +1196,11 @@ class Agent { final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i); final EconomicPolicy.Action action = economicPolicy.getAction(aa.actionId); if (action == null) { - throw new IllegalArgumentException("Invalid action id: " + aa.actionId); + if ((aa.actionId & EconomicPolicy.ALL_POLICIES) == 0) { + throw new IllegalArgumentException("Invalid action id: " + aa.actionId); + } else { + Slog.w(TAG, "Tracking disabled policy's action? " + aa.actionId); + } } } mListener = listener; diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java index e791e98a6698..b426f16744e3 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java @@ -117,23 +117,23 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { private static final String TAG = "TARE- " + AlarmManagerEconomicPolicy.class.getSimpleName(); public static final int ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE = - TYPE_ACTION | POLICY_AM | 0; + TYPE_ACTION | POLICY_ALARM | 0; public static final int ACTION_ALARM_WAKEUP_EXACT = - TYPE_ACTION | POLICY_AM | 1; + TYPE_ACTION | POLICY_ALARM | 1; public static final int ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE = - TYPE_ACTION | POLICY_AM | 2; + TYPE_ACTION | POLICY_ALARM | 2; public static final int ACTION_ALARM_WAKEUP_INEXACT = - TYPE_ACTION | POLICY_AM | 3; + TYPE_ACTION | POLICY_ALARM | 3; public static final int ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE = - TYPE_ACTION | POLICY_AM | 4; + TYPE_ACTION | POLICY_ALARM | 4; public static final int ACTION_ALARM_NONWAKEUP_EXACT = - TYPE_ACTION | POLICY_AM | 5; + TYPE_ACTION | POLICY_ALARM | 5; public static final int ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE = - TYPE_ACTION | POLICY_AM | 6; + TYPE_ACTION | POLICY_ALARM | 6; public static final int ACTION_ALARM_NONWAKEUP_INEXACT = - TYPE_ACTION | POLICY_AM | 7; + TYPE_ACTION | POLICY_ALARM | 7; public static final int ACTION_ALARM_CLOCK = - TYPE_ACTION | POLICY_AM | 8; + TYPE_ACTION | POLICY_ALARM | 8; private static final int[] COST_MODIFIERS = new int[]{ COST_MODIFIER_CHARGING, diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Analyst.java b/apex/jobscheduler/service/java/com/android/server/tare/Analyst.java index bc6fe7e5a535..f27da4a1a4b2 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Analyst.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Analyst.java @@ -16,6 +16,8 @@ package com.android.server.tare; +import static android.text.format.DateUtils.HOUR_IN_MILLIS; + import static com.android.server.tare.EconomicPolicy.TYPE_ACTION; import static com.android.server.tare.EconomicPolicy.TYPE_REGULATION; import static com.android.server.tare.EconomicPolicy.TYPE_REWARD; @@ -23,9 +25,16 @@ import static com.android.server.tare.EconomicPolicy.getEventType; import static com.android.server.tare.TareUtils.cakeToString; import android.annotation.NonNull; +import android.os.BatteryManagerInternal; +import android.os.RemoteException; import android.util.IndentingPrintWriter; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.IBatteryStats; +import com.android.server.LocalServices; +import com.android.server.am.BatteryStatsService; + import java.util.ArrayList; import java.util.List; @@ -38,6 +47,8 @@ public class Analyst { || Log.isLoggable(TAG, Log.DEBUG); private static final int NUM_PERIODS_TO_RETAIN = 8; + @VisibleForTesting + static final long MIN_REPORT_DURATION_FOR_RESET = 24 * HOUR_IN_MILLIS; static final class Report { /** How much the battery was discharged over the tracked period. */ @@ -73,6 +84,22 @@ public class Analyst { public long cumulativeNegativeRegulations = 0; public int numNegativeRegulations = 0; + /** + * The approximate amount of time the screen has been off while on battery while this + * report has been active. + */ + public long screenOffDurationMs = 0; + /** + * The approximate amount of battery discharge while this report has been active. + */ + public long screenOffDischargeMah = 0; + /** The offset used to get the delta when polling the screen off time from BatteryStats. */ + private long bsScreenOffRealtimeBase = 0; + /** + * The offset used to get the delta when polling the screen off discharge from BatteryStats. + */ + private long bsScreenOffDischargeMahBase = 0; + private void clear() { cumulativeBatteryDischarge = 0; currentBatteryLevel = 0; @@ -86,13 +113,27 @@ public class Analyst { numPositiveRegulations = 0; cumulativeNegativeRegulations = 0; numNegativeRegulations = 0; + screenOffDurationMs = 0; + screenOffDischargeMah = 0; + bsScreenOffRealtimeBase = 0; + bsScreenOffDischargeMahBase = 0; } } + private final IBatteryStats mIBatteryStats; + private int mPeriodIndex = 0; /** How much the battery was discharged over the tracked period. */ private final Report[] mReports = new Report[NUM_PERIODS_TO_RETAIN]; + Analyst() { + this(BatteryStatsService.getService()); + } + + @VisibleForTesting Analyst(IBatteryStats iBatteryStats) { + mIBatteryStats = iBatteryStats; + } + /** Returns the list of most recent reports, with the oldest report first. */ @NonNull List<Report> getReports() { @@ -107,13 +148,35 @@ public class Analyst { return list; } + long getBatteryScreenOffDischargeMah() { + long discharge = 0; + for (Report report : mReports) { + if (report == null) { + continue; + } + discharge += report.screenOffDischargeMah; + } + return discharge; + } + + long getBatteryScreenOffDurationMs() { + long duration = 0; + for (Report report : mReports) { + if (report == null) { + continue; + } + duration += report.screenOffDurationMs; + } + return duration; + } + /** * Tracks the given reports instead of whatever is currently saved. Reports should be ordered * oldest to most recent. */ void loadReports(@NonNull List<Report> reports) { final int numReports = reports.size(); - mPeriodIndex = Math.max(0, numReports - 1); + mPeriodIndex = Math.max(0, Math.min(NUM_PERIODS_TO_RETAIN, numReports) - 1); for (int i = 0; i < NUM_PERIODS_TO_RETAIN; ++i) { if (i < numReports) { mReports[i] = reports.get(i); @@ -121,22 +184,38 @@ public class Analyst { mReports[i] = null; } } + final Report latest = mReports[mPeriodIndex]; + if (latest != null) { + latest.bsScreenOffRealtimeBase = getLatestBatteryScreenOffRealtimeMs(); + latest.bsScreenOffDischargeMahBase = getLatestScreenOffDischargeMah(); + } } void noteBatteryLevelChange(int newBatteryLevel) { - if (newBatteryLevel == 100 && mReports[mPeriodIndex] != null - && mReports[mPeriodIndex].currentBatteryLevel < newBatteryLevel) { + final boolean deviceDischargedEnough = mReports[mPeriodIndex] != null + && newBatteryLevel >= 90 + // Battery level is increasing, so device is charging. + && mReports[mPeriodIndex].currentBatteryLevel < newBatteryLevel + && mReports[mPeriodIndex].cumulativeBatteryDischarge >= 25; + final boolean reportLongEnough = mReports[mPeriodIndex] != null + // Battery level is increasing, so device is charging. + && mReports[mPeriodIndex].currentBatteryLevel < newBatteryLevel + && mReports[mPeriodIndex].screenOffDurationMs >= MIN_REPORT_DURATION_FOR_RESET; + final boolean shouldStartNewReport = deviceDischargedEnough || reportLongEnough; + if (shouldStartNewReport) { mPeriodIndex = (mPeriodIndex + 1) % NUM_PERIODS_TO_RETAIN; if (mReports[mPeriodIndex] != null) { final Report report = mReports[mPeriodIndex]; report.clear(); report.currentBatteryLevel = newBatteryLevel; + report.bsScreenOffRealtimeBase = getLatestBatteryScreenOffRealtimeMs(); + report.bsScreenOffDischargeMahBase = getLatestScreenOffDischargeMah(); return; } } if (mReports[mPeriodIndex] == null) { - Report report = new Report(); + Report report = initializeReport(); mReports[mPeriodIndex] = report; report.currentBatteryLevel = newBatteryLevel; return; @@ -145,13 +224,27 @@ public class Analyst { final Report report = mReports[mPeriodIndex]; if (newBatteryLevel < report.currentBatteryLevel) { report.cumulativeBatteryDischarge += (report.currentBatteryLevel - newBatteryLevel); + + final long latestScreenOffRealtime = getLatestBatteryScreenOffRealtimeMs(); + final long latestScreenOffDischargeMah = getLatestScreenOffDischargeMah(); + if (report.bsScreenOffRealtimeBase > latestScreenOffRealtime) { + // BatteryStats reset + report.bsScreenOffRealtimeBase = 0; + report.bsScreenOffDischargeMahBase = 0; + } + report.screenOffDurationMs += + (latestScreenOffRealtime - report.bsScreenOffRealtimeBase); + report.screenOffDischargeMah += + (latestScreenOffDischargeMah - report.bsScreenOffDischargeMahBase); + report.bsScreenOffRealtimeBase = latestScreenOffRealtime; + report.bsScreenOffDischargeMahBase = latestScreenOffDischargeMah; } report.currentBatteryLevel = newBatteryLevel; } void noteTransaction(@NonNull Ledger.Transaction transaction) { if (mReports[mPeriodIndex] == null) { - mReports[mPeriodIndex] = new Report(); + mReports[mPeriodIndex] = initializeReport(); } final Report report = mReports[mPeriodIndex]; switch (getEventType(transaction.eventId)) { @@ -191,6 +284,32 @@ public class Analyst { mPeriodIndex = 0; } + private long getLatestBatteryScreenOffRealtimeMs() { + try { + return mIBatteryStats.computeBatteryScreenOffRealtimeMs(); + } catch (RemoteException e) { + // Shouldn't happen + return 0; + } + } + + private long getLatestScreenOffDischargeMah() { + try { + return mIBatteryStats.getScreenOffDischargeMah(); + } catch (RemoteException e) { + // Shouldn't happen + return 0; + } + } + + @NonNull + private Report initializeReport() { + final Report report = new Report(); + report.bsScreenOffRealtimeBase = getLatestBatteryScreenOffRealtimeMs(); + report.bsScreenOffDischargeMahBase = getLatestScreenOffDischargeMah(); + return report; + } + @NonNull private String padStringWithSpaces(@NonNull String text, int targetLength) { // Make sure to have at least one space on either side. @@ -199,6 +318,8 @@ public class Analyst { } void dump(IndentingPrintWriter pw) { + final BatteryManagerInternal bmi = LocalServices.getService(BatteryManagerInternal.class); + final long batteryCapacityMah = bmi.getBatteryFullCharge() / 1000; pw.println("Reports:"); pw.increaseIndent(); pw.print(" Total Discharge"); @@ -208,6 +329,7 @@ public class Analyst { pw.print(padStringWithSpaces("Rewards (avg/reward : avg/discharge)", statColsLength)); pw.print(padStringWithSpaces("+Regs (avg/reg : avg/discharge)", statColsLength)); pw.print(padStringWithSpaces("-Regs (avg/reg : avg/discharge)", statColsLength)); + pw.print(padStringWithSpaces("Bg drain estimate", statColsLength)); pw.println(); for (int r = 0; r < NUM_PERIODS_TO_RETAIN; ++r) { final int idx = (mPeriodIndex - r + NUM_PERIODS_TO_RETAIN) % NUM_PERIODS_TO_RETAIN; @@ -283,6 +405,15 @@ public class Analyst { } else { pw.print(padStringWithSpaces("N/A", statColsLength)); } + if (report.screenOffDurationMs > 0) { + pw.print(padStringWithSpaces(String.format("%d mAh (%.2f%%/hr)", + report.screenOffDischargeMah, + 1.0 * report.screenOffDischargeMah * HOUR_IN_MILLIS + / (batteryCapacityMah * report.screenOffDurationMs)), + statColsLength)); + } else { + pw.print(padStringWithSpaces("N/A", statColsLength)); + } pw.println(); } pw.decreaseIndent(); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java index 625f99d64ef4..66f7c357d223 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java @@ -18,24 +18,30 @@ package com.android.server.tare; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.tare.EconomyManager; import android.provider.DeviceConfig; import android.util.ArraySet; import android.util.IndentingPrintWriter; +import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import libcore.util.EmptyArray; /** Combines all enabled policies into one. */ public class CompleteEconomicPolicy extends EconomicPolicy { + private static final String TAG = "TARE-" + CompleteEconomicPolicy.class.getSimpleName(); + private final CompleteInjector mInjector; private final ArraySet<EconomicPolicy> mEnabledEconomicPolicies = new ArraySet<>(); /** Lazily populated set of actions covered by this policy. */ private final SparseArray<Action> mActions = new SparseArray<>(); /** Lazily populated set of rewards covered by this policy. */ private final SparseArray<Reward> mRewards = new SparseArray<>(); - private final int[] mCostModifiers; + private int mEnabledEconomicPolicyIds = 0; + private int[] mCostModifiers = EmptyArray.INT; private long mInitialConsumptionLimit; private long mHardConsumptionLimit; @@ -47,11 +53,34 @@ public class CompleteEconomicPolicy extends EconomicPolicy { CompleteEconomicPolicy(@NonNull InternalResourceService irs, @NonNull CompleteInjector injector) { super(irs); - if (injector.isPolicyEnabled(POLICY_AM)) { - mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(irs, injector)); + mInjector = injector; + + if (mInjector.isPolicyEnabled(POLICY_ALARM, null)) { + mEnabledEconomicPolicyIds |= POLICY_ALARM; + mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(mIrs, mInjector)); } - if (injector.isPolicyEnabled(POLICY_JS)) { - mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(irs, injector)); + if (mInjector.isPolicyEnabled(POLICY_JOB, null)) { + mEnabledEconomicPolicyIds |= POLICY_JOB; + mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(mIrs, mInjector)); + } + } + + @Override + void setup(@NonNull DeviceConfig.Properties properties) { + super.setup(properties); + + mActions.clear(); + mRewards.clear(); + + mEnabledEconomicPolicies.clear(); + mEnabledEconomicPolicyIds = 0; + if (mInjector.isPolicyEnabled(POLICY_ALARM, properties)) { + mEnabledEconomicPolicyIds |= POLICY_ALARM; + mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(mIrs, mInjector)); + } + if (mInjector.isPolicyEnabled(POLICY_JOB, properties)) { + mEnabledEconomicPolicyIds |= POLICY_JOB; + mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(mIrs, mInjector)); } ArraySet<Integer> costModifiers = new ArraySet<>(); @@ -61,17 +90,8 @@ public class CompleteEconomicPolicy extends EconomicPolicy { costModifiers.add(s); } } - mCostModifiers = new int[costModifiers.size()]; - for (int i = 0; i < costModifiers.size(); ++i) { - mCostModifiers[i] = costModifiers.valueAt(i); - } + mCostModifiers = ArrayUtils.convertToIntArray(costModifiers); - updateLimits(); - } - - @Override - void setup(@NonNull DeviceConfig.Properties properties) { - super.setup(properties); for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) { mEnabledEconomicPolicies.valueAt(i).setup(properties); } @@ -170,11 +190,37 @@ public class CompleteEconomicPolicy extends EconomicPolicy { return reward; } + boolean isPolicyEnabled(@Policy int policyId) { + return (mEnabledEconomicPolicyIds & policyId) == policyId; + } + + int getEnabledPolicyIds() { + return mEnabledEconomicPolicyIds; + } + @VisibleForTesting static class CompleteInjector extends Injector { - boolean isPolicyEnabled(int policy) { - return true; + boolean isPolicyEnabled(int policy, @Nullable DeviceConfig.Properties properties) { + final String key; + final boolean defaultEnable; + switch (policy) { + case POLICY_ALARM: + key = EconomyManager.KEY_ENABLE_POLICY_ALARM; + defaultEnable = EconomyManager.DEFAULT_ENABLE_POLICY_ALARM; + break; + case POLICY_JOB: + key = EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER; + defaultEnable = EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER; + break; + default: + Slog.wtf(TAG, "Unknown policy: " + policy); + return false; + } + if (properties == null) { + return defaultEnable; + } + return properties.getBoolean(key, defaultEnable); } } @@ -189,7 +235,10 @@ public class CompleteEconomicPolicy extends EconomicPolicy { pw.println("Cached actions:"); pw.increaseIndent(); for (int i = 0; i < mActions.size(); ++i) { - dumpAction(pw, mActions.valueAt(i)); + final Action action = mActions.valueAt(i); + if (action != null) { + dumpAction(pw, action); + } } pw.decreaseIndent(); @@ -197,7 +246,10 @@ public class CompleteEconomicPolicy extends EconomicPolicy { pw.println("Cached rewards:"); pw.increaseIndent(); for (int i = 0; i < mRewards.size(); ++i) { - dumpReward(pw, mRewards.valueAt(i)); + final Reward reward = mRewards.valueAt(i); + if (reward != null) { + dumpReward(pw, reward); + } } pw.decreaseIndent(); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java index 7391bcfa8d88..008dcb8edf63 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java @@ -57,9 +57,10 @@ public abstract class EconomicPolicy { private static final int SHIFT_POLICY = 28; static final int MASK_POLICY = 0b11 << SHIFT_POLICY; + static final int ALL_POLICIES = MASK_POLICY; // Reserve 0 for the base/common policy. - static final int POLICY_AM = 1 << SHIFT_POLICY; - static final int POLICY_JS = 2 << SHIFT_POLICY; + public static final int POLICY_ALARM = 1 << SHIFT_POLICY; + public static final int POLICY_JOB = 2 << SHIFT_POLICY; static final int MASK_EVENT = -1 ^ (MASK_TYPE | MASK_POLICY); @@ -115,6 +116,15 @@ public abstract class EconomicPolicy { } @IntDef({ + ALL_POLICIES, + POLICY_ALARM, + POLICY_JOB, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Policy { + } + + @IntDef({ REWARD_TOP_ACTIVITY, REWARD_NOTIFICATION_SEEN, REWARD_NOTIFICATION_INTERACTION, @@ -342,7 +352,7 @@ public abstract class EconomicPolicy { @NonNull static String actionToString(int eventId) { switch (eventId & MASK_POLICY) { - case POLICY_AM: + case POLICY_ALARM: switch (eventId) { case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE: return "ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE"; @@ -365,7 +375,7 @@ public abstract class EconomicPolicy { } break; - case POLICY_JS: + case POLICY_JOB: switch (eventId) { case JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START: return "JOB_MAX_START"; diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java index 0fa0c47e1b49..716769c21afc 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java @@ -138,6 +138,9 @@ public interface EconomyManagerInternal { /** Returns true if TARE is enabled. */ boolean isEnabled(); + /** Returns true if TARE and the specified policy are enabled. */ + boolean isEnabled(@EconomicPolicy.Policy int policyId); + /** * Register an {@link AffordabilityChangeListener} to track when an app's ability to afford the * indicated bill changes. @@ -155,7 +158,8 @@ public interface EconomyManagerInternal { /** * Register a {@link TareStateChangeListener} to track when TARE's state changes. */ - void registerTareStateChangeListener(@NonNull TareStateChangeListener listener); + void registerTareStateChangeListener(@NonNull TareStateChangeListener listener, + @EconomicPolicy.Policy int policyId); /** * Unregister a {@link TareStateChangeListener} from being notified when TARE's state changes. diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java index 4a26d213a48a..dd0a19433683 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java @@ -30,6 +30,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.AppOpsManager; +import android.app.tare.EconomyManager; import android.app.tare.IEconomyManager; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; @@ -82,7 +83,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.CopyOnWriteArraySet; /** * Responsible for handling app's ARC count based on events, ensuring ARCs are credited when @@ -112,6 +112,19 @@ public class InternalResourceService extends SystemService { * limit). */ private static final int QUANTITATIVE_EASING_BATTERY_THRESHOLD = 50; + /** + * The battery level above which we may consider adjusting the desired stock level. + */ + private static final int STOCK_RECALCULATION_BATTERY_THRESHOLD = 80; + /** + * The amount of time to wait before considering recalculating the desired stock level. + */ + private static final long STOCK_RECALCULATION_DELAY_MS = 16 * HOUR_IN_MILLIS; + /** + * The minimum amount of time we must have background drain for before considering + * recalculating the desired stock level. + */ + private static final long STOCK_RECALCULATION_MIN_DATA_DURATION_MS = 8 * HOUR_IN_MILLIS; private static final int PACKAGE_QUERY_FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_APEX; @@ -148,8 +161,9 @@ public class InternalResourceService extends SystemService { @GuardedBy("mPackageToUidCache") private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>(); - private final CopyOnWriteArraySet<TareStateChangeListener> mStateChangeListeners = - new CopyOnWriteArraySet<>(); + @GuardedBy("mStateChangeListeners") + private final SparseSetArray<TareStateChangeListener> mStateChangeListeners = + new SparseSetArray<>(); /** * List of packages that are fully restricted and shouldn't be allowed to run in the background. @@ -177,6 +191,9 @@ public class InternalResourceService extends SystemService { @GuardedBy("mLock") private int mCurrentBatteryLevel; + // TODO(250007395): make configurable per device + private final int mTargetBackgroundBatteryLifeHours; + private final IAppOpsCallback mApbListener = new IAppOpsCallback.Stub() { @Override public void opChanged(int op, int uid, String packageName) { @@ -290,6 +307,7 @@ public class InternalResourceService extends SystemService { private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1; private static final int MSG_PROCESS_USAGE_EVENT = 2; private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 3; + private static final int MSG_NOTIFY_STATE_CHANGE_LISTENER = 4; private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*"; /** @@ -316,6 +334,11 @@ public class InternalResourceService extends SystemService { mConfigObserver = new ConfigObserver(mHandler, context); + mTargetBackgroundBatteryLifeHours = + mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH) + ? 200 // ~ 0.5%/hr + : 100; // ~ 1%/hr + publishLocalService(EconomyManagerInternal.class, new LocalService()); } @@ -421,6 +444,12 @@ public class InternalResourceService extends SystemService { return mIsEnabled; } + boolean isEnabled(int policyId) { + synchronized (mLock) { + return isEnabled() && mCompleteEconomicPolicy.isPolicyEnabled(policyId); + } + } + boolean isPackageExempted(final int userId, @NonNull String pkgName) { synchronized (mLock) { return mExemptedApps.contains(pkgName); @@ -461,6 +490,9 @@ public class InternalResourceService extends SystemService { mAnalyst.noteBatteryLevelChange(newBatteryLevel); final boolean increased = newBatteryLevel > mCurrentBatteryLevel; if (increased) { + if (newBatteryLevel >= STOCK_RECALCULATION_BATTERY_THRESHOLD) { + maybeAdjustDesiredStockLevelLocked(); + } mAgent.distributeBasicIncomeLocked(newBatteryLevel); } else if (newBatteryLevel == mCurrentBatteryLevel) { // The broadcast is also sent when the plug type changes... @@ -623,6 +655,10 @@ public class InternalResourceService extends SystemService { */ @GuardedBy("mLock") void maybePerformQuantitativeEasingLocked() { + if (mConfigObserver.ENABLE_TIP3) { + maybeAdjustDesiredStockLevelLocked(); + return; + } // We don't need to increase the limit if the device runs out of consumable credits // when the battery is low. final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked(); @@ -643,6 +679,68 @@ public class InternalResourceService extends SystemService { } } + /** + * Adjust the consumption limit based on historical data and the target battery drain. + */ + @GuardedBy("mLock") + void maybeAdjustDesiredStockLevelLocked() { + if (!mConfigObserver.ENABLE_TIP3) { + return; + } + // Don't adjust the limit too often or while the battery is low. + final long now = getCurrentTimeMillis(); + if ((now - mScribe.getLastStockRecalculationTimeLocked()) < STOCK_RECALCULATION_DELAY_MS + || mCurrentBatteryLevel <= STOCK_RECALCULATION_BATTERY_THRESHOLD) { + return; + } + + // For now, use screen off battery drain as a proxy for background battery drain. + // TODO: get more accurate background battery drain numbers + final long totalScreenOffDurationMs = mAnalyst.getBatteryScreenOffDurationMs(); + if (totalScreenOffDurationMs < STOCK_RECALCULATION_MIN_DATA_DURATION_MS) { + return; + } + final long totalDischargeMah = mAnalyst.getBatteryScreenOffDischargeMah(); + final long batteryCapacityMah = mBatteryManagerInternal.getBatteryFullCharge() / 1000; + final long estimatedLifeHours = batteryCapacityMah * totalScreenOffDurationMs + / totalDischargeMah / HOUR_IN_MILLIS; + final long percentageOfTarget = + 100 * estimatedLifeHours / mTargetBackgroundBatteryLifeHours; + if (DEBUG) { + Slog.d(TAG, "maybeAdjustDesiredStockLevelLocked:" + + " screenOffMs=" + totalScreenOffDurationMs + + " dischargeMah=" + totalDischargeMah + + " capacityMah=" + batteryCapacityMah + + " estimatedLifeHours=" + estimatedLifeHours + + " %ofTarget=" + percentageOfTarget); + } + final long currentConsumptionLimit = mScribe.getSatiatedConsumptionLimitLocked(); + final long newConsumptionLimit; + if (percentageOfTarget > 105) { + // The stock is too low. We're doing pretty well. We can increase the stock slightly + // to let apps do more work in the background. + newConsumptionLimit = Math.min((long) (currentConsumptionLimit * 1.01), + mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()); + } else if (percentageOfTarget < 100) { + // The stock is too high IMO. We're below the target. Decrease the stock to reduce + // background work. + newConsumptionLimit = Math.max((long) (currentConsumptionLimit * .98), + mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()); + } else { + // The stock is just right. + return; + } + // TODO(250007191): calculate and log implied service level + if (newConsumptionLimit != currentConsumptionLimit) { + Slog.i(TAG, "Adjusting consumption limit from " + cakeToString(currentConsumptionLimit) + + " to " + cakeToString(newConsumptionLimit) + + " because drain was " + percentageOfTarget + "% of target"); + mScribe.setConsumptionLimitLocked(newConsumptionLimit); + adjustCreditSupplyLocked(/* allowIncrease */ true); + mScribe.setLastStockRecalculationTimeLocked(now); + } + } + void postAffordabilityChanged(final int userId, @NonNull final String pkgName, @NonNull Agent.ActionAffordabilityNote affordabilityNote) { if (DEBUG) { @@ -994,9 +1092,30 @@ public class InternalResourceService extends SystemService { } break; + case MSG_NOTIFY_STATE_CHANGE_LISTENER: { + final int policy = msg.arg1; + final TareStateChangeListener listener = (TareStateChangeListener) msg.obj; + listener.onTareEnabledStateChanged(isEnabled(policy)); + } + break; + case MSG_NOTIFY_STATE_CHANGE_LISTENERS: { - for (TareStateChangeListener listener : mStateChangeListeners) { - listener.onTareEnabledStateChanged(mIsEnabled); + final int changedPolicies = msg.arg1; + synchronized (mStateChangeListeners) { + final int size = mStateChangeListeners.size(); + for (int l = 0; l < size; ++l) { + final int policy = mStateChangeListeners.keyAt(l); + if ((policy & changedPolicies) == 0) { + continue; + } + final ArraySet<TareStateChangeListener> listeners = + mStateChangeListeners.get(policy); + final boolean isEnabled = isEnabled(policy); + for (int p = listeners.size() - 1; p >= 0; --p) { + final TareStateChangeListener listener = listeners.valueAt(p); + listener.onTareEnabledStateChanged(isEnabled); + } + } } } break; @@ -1101,16 +1220,28 @@ public class InternalResourceService extends SystemService { } @Override - public void registerTareStateChangeListener(@NonNull TareStateChangeListener listener) { + public void registerTareStateChangeListener(@NonNull TareStateChangeListener listener, + int policyId) { if (!isTareSupported()) { return; } - mStateChangeListeners.add(listener); + synchronized (mStateChangeListeners) { + if (mStateChangeListeners.add(policyId, listener)) { + mHandler.obtainMessage(MSG_NOTIFY_STATE_CHANGE_LISTENER, policyId, 0, listener) + .sendToTarget(); + } + } } @Override public void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener) { - mStateChangeListeners.remove(listener); + synchronized (mStateChangeListeners) { + for (int i = mStateChangeListeners.size() - 1; i >= 0; --i) { + final ArraySet<TareStateChangeListener> listeners = + mStateChangeListeners.get(mStateChangeListeners.keyAt(i)); + listeners.remove(listener); + } + } } @Override @@ -1175,6 +1306,11 @@ public class InternalResourceService extends SystemService { } @Override + public boolean isEnabled(int policyId) { + return InternalResourceService.this.isEnabled(policyId); + } + + @Override public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId, @Nullable String tag) { if (!mIsEnabled) { @@ -1213,7 +1349,12 @@ public class InternalResourceService extends SystemService { private class ConfigObserver extends ContentObserver implements DeviceConfig.OnPropertiesChangedListener { - private static final String KEY_DC_ENABLE_TARE = "enable_tare"; + private static final String KEY_ENABLE_TIP3 = "enable_tip3"; + + private static final boolean DEFAULT_ENABLE_TIP3 = true; + + /** Use a target background battery drain rate to determine consumption limits. */ + public boolean ENABLE_TIP3 = DEFAULT_ENABLE_TIP3; private final ContentResolver mContentResolver; @@ -1261,12 +1402,16 @@ public class InternalResourceService extends SystemService { continue; } switch (name) { - case KEY_DC_ENABLE_TARE: + case EconomyManager.KEY_ENABLE_TARE: updateEnabledStatus(); break; + case KEY_ENABLE_TIP3: + ENABLE_TIP3 = properties.getBoolean(name, DEFAULT_ENABLE_TIP3); + break; default: if (!economicPolicyUpdated - && (name.startsWith("am") || name.startsWith("js"))) { + && (name.startsWith("am") || name.startsWith("js") + || name.startsWith("enable_policy"))) { updateEconomicPolicy(); economicPolicyUpdated = true; } @@ -1278,7 +1423,7 @@ public class InternalResourceService extends SystemService { private void updateEnabledStatus() { // User setting should override DeviceConfig setting. final boolean isTareEnabledDC = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TARE, - KEY_DC_ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE == 1); + EconomyManager.KEY_ENABLE_TARE, EconomyManager.DEFAULT_ENABLE_TARE); final boolean isTareEnabled = isTareSupported() && Settings.Global.getInt(mContentResolver, Settings.Global.ENABLE_TARE, isTareEnabledDC ? 1 : 0) == 1; @@ -1289,7 +1434,9 @@ public class InternalResourceService extends SystemService { } else { tearDownEverything(); } - mHandler.sendEmptyMessage(MSG_NOTIFY_STATE_CHANGE_LISTENERS); + mHandler.obtainMessage( + MSG_NOTIFY_STATE_CHANGE_LISTENERS, EconomicPolicy.ALL_POLICIES, 0) + .sendToTarget(); } } @@ -1298,9 +1445,10 @@ public class InternalResourceService extends SystemService { final long initialLimit = mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit(); final long hardLimit = mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit(); + final int oldEnabledPolicies = mCompleteEconomicPolicy.getEnabledPolicyIds(); mCompleteEconomicPolicy.tearDown(); mCompleteEconomicPolicy = new CompleteEconomicPolicy(InternalResourceService.this); - if (mIsEnabled && mBootPhase >= PHASE_SYSTEM_SERVICES_READY) { + if (mIsEnabled && mBootPhase >= PHASE_THIRD_PARTY_APPS_CAN_START) { mCompleteEconomicPolicy.setup(getAllDeviceConfigProperties()); if (initialLimit != mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit() || hardLimit @@ -1310,6 +1458,13 @@ public class InternalResourceService extends SystemService { mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()); } mAgent.onPricingChangedLocked(); + final int newEnabledPolicies = mCompleteEconomicPolicy.getEnabledPolicyIds(); + if (oldEnabledPolicies != newEnabledPolicies) { + final int changedPolicies = oldEnabledPolicies ^ newEnabledPolicies; + mHandler.obtainMessage( + MSG_NOTIFY_STATE_CHANGE_LISTENERS, changedPolicies, 0) + .sendToTarget(); + } } } } diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java index 322e824286d5..71c6d099ac77 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java @@ -133,19 +133,19 @@ import android.util.SparseArray; public class JobSchedulerEconomicPolicy extends EconomicPolicy { private static final String TAG = "TARE- " + JobSchedulerEconomicPolicy.class.getSimpleName(); - public static final int ACTION_JOB_MAX_START = TYPE_ACTION | POLICY_JS | 0; - public static final int ACTION_JOB_MAX_RUNNING = TYPE_ACTION | POLICY_JS | 1; - public static final int ACTION_JOB_HIGH_START = TYPE_ACTION | POLICY_JS | 2; - public static final int ACTION_JOB_HIGH_RUNNING = TYPE_ACTION | POLICY_JS | 3; - public static final int ACTION_JOB_DEFAULT_START = TYPE_ACTION | POLICY_JS | 4; - public static final int ACTION_JOB_DEFAULT_RUNNING = TYPE_ACTION | POLICY_JS | 5; - public static final int ACTION_JOB_LOW_START = TYPE_ACTION | POLICY_JS | 6; - public static final int ACTION_JOB_LOW_RUNNING = TYPE_ACTION | POLICY_JS | 7; - public static final int ACTION_JOB_MIN_START = TYPE_ACTION | POLICY_JS | 8; - public static final int ACTION_JOB_MIN_RUNNING = TYPE_ACTION | POLICY_JS | 9; - public static final int ACTION_JOB_TIMEOUT = TYPE_ACTION | POLICY_JS | 10; - - public static final int REWARD_APP_INSTALL = TYPE_REWARD | POLICY_JS | 0; + public static final int ACTION_JOB_MAX_START = TYPE_ACTION | POLICY_JOB | 0; + public static final int ACTION_JOB_MAX_RUNNING = TYPE_ACTION | POLICY_JOB | 1; + public static final int ACTION_JOB_HIGH_START = TYPE_ACTION | POLICY_JOB | 2; + public static final int ACTION_JOB_HIGH_RUNNING = TYPE_ACTION | POLICY_JOB | 3; + public static final int ACTION_JOB_DEFAULT_START = TYPE_ACTION | POLICY_JOB | 4; + public static final int ACTION_JOB_DEFAULT_RUNNING = TYPE_ACTION | POLICY_JOB | 5; + public static final int ACTION_JOB_LOW_START = TYPE_ACTION | POLICY_JOB | 6; + public static final int ACTION_JOB_LOW_RUNNING = TYPE_ACTION | POLICY_JOB | 7; + public static final int ACTION_JOB_MIN_START = TYPE_ACTION | POLICY_JOB | 8; + public static final int ACTION_JOB_MIN_RUNNING = TYPE_ACTION | POLICY_JOB | 9; + public static final int ACTION_JOB_TIMEOUT = TYPE_ACTION | POLICY_JOB | 10; + + public static final int REWARD_APP_INSTALL = TYPE_REWARD | POLICY_JOB | 0; private static final int[] COST_MODIFIERS = new int[]{ COST_MODIFIER_CHARGING, diff --git a/apex/jobscheduler/service/java/com/android/server/tare/README.md b/apex/jobscheduler/service/java/com/android/server/tare/README.md index e338ed1c6987..8d25ecce8431 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/README.md +++ b/apex/jobscheduler/service/java/com/android/server/tare/README.md @@ -80,9 +80,9 @@ consumption limit, then the available resources are decreased to match the scale Regulations are unique events invoked by the ~~government~~ system in order to get the whole economy moving smoothly. -# Previous Implementations +# Significant Changes -## V0 +## Tare Improvement Proposal #1 (TIP1) The initial implementation/proposal combined the supply of resources with the allocation in a single mechanism. It defined the maximum number of resources (ARCs) available at a time, and then divided @@ -98,10 +98,25 @@ allocated as part of the rewards. There were several problems with that mechanis These problems effectively meant that misallocation was a big problem, demand wasn't well reflected, and some apps may not have been able to perform work even though they otherwise should have been. -Tare Improvement Proposal #1 (TIP1) separated allocation (to apps) from supply (by the system) and +TIP1 separated allocation (to apps) from supply (by the system) and allowed apps to accrue credits as appropriate while still limiting the total number of credits consumed. +## Tare Improvement Proposal #3 (TIP3) + +TIP1 introduced Consumption Limits, which control the total number of ARCs that can be used to +perform actions, based on the production costs of each action. The Consumption Limits were initially +determined manually, but could increase in the system if apps used the full consumption limit before +the device had drained to 50% battery. As with any system that relies on manually deciding +parameters, the only mechanism to identify an optimal value is through experimentation, which can +take many iterations and requires extended periods of time to observe results. The limits are also +chosen and adjusted without consideration of the resulting battery drain of each possible value. In +addition, having the system potentially increase the limit without considering a decrease introduced +potential for battery life to get worse as time goes on and the user installed more background-work +demanding apps. + +TIP3 uses a target background battery drain rate to dynamically adjust the Consumption Limit. + # Potential Future Changes These are some ideas for further changes. There's no guarantee that they'll be implemented. diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java index bd4fd72b78ce..27d00b76f452 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java @@ -84,6 +84,8 @@ public class Scribe { private static final String XML_ATTR_USER_ID = "userId"; private static final String XML_ATTR_VERSION = "version"; private static final String XML_ATTR_LAST_RECLAMATION_TIME = "lastReclamationTime"; + private static final String XML_ATTR_LAST_STOCK_RECALCULATION_TIME = + "lastStockRecalculationTime"; private static final String XML_ATTR_REMAINING_CONSUMABLE_CAKES = "remainingConsumableCakes"; private static final String XML_ATTR_CONSUMPTION_LIMIT = "consumptionLimit"; private static final String XML_ATTR_PR_DISCHARGE = "discharge"; @@ -98,6 +100,8 @@ public class Scribe { private static final String XML_ATTR_PR_NUM_POS_REGULATIONS = "numPosRegulations"; private static final String XML_ATTR_PR_NEG_REGULATIONS = "negRegulations"; private static final String XML_ATTR_PR_NUM_NEG_REGULATIONS = "numNegRegulations"; + private static final String XML_ATTR_PR_SCREEN_OFF_DURATION_MS = "screenOffDurationMs"; + private static final String XML_ATTR_PR_SCREEN_OFF_DISCHARGE_MAH = "screenOffDischargeMah"; /** Version of the file schema. */ private static final int STATE_FILE_VERSION = 0; @@ -111,6 +115,8 @@ public class Scribe { @GuardedBy("mIrs.getLock()") private long mLastReclamationTime; @GuardedBy("mIrs.getLock()") + private long mLastStockRecalculationTime; + @GuardedBy("mIrs.getLock()") private long mSatiatedConsumptionLimit; @GuardedBy("mIrs.getLock()") private long mRemainingConsumableCakes; @@ -173,6 +179,11 @@ public class Scribe { } @GuardedBy("mIrs.getLock()") + long getLastStockRecalculationTimeLocked() { + return mLastStockRecalculationTime; + } + + @GuardedBy("mIrs.getLock()") @NonNull Ledger getLedgerLocked(final int userId, @NonNull final String pkgName) { Ledger ledger = mLedgers.get(userId, pkgName); @@ -281,6 +292,8 @@ public class Scribe { case XML_TAG_HIGH_LEVEL_STATE: mLastReclamationTime = parser.getAttributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME); + mLastStockRecalculationTime = parser.getAttributeLong(null, + XML_ATTR_LAST_STOCK_RECALCULATION_TIME, 0); mSatiatedConsumptionLimit = parser.getAttributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mIrs.getInitialSatiatedConsumptionLimitLocked()); @@ -337,6 +350,12 @@ public class Scribe { } @GuardedBy("mIrs.getLock()") + void setLastStockRecalculationTimeLocked(long time) { + mLastStockRecalculationTime = time; + postWrite(); + } + + @GuardedBy("mIrs.getLock()") void tearDownLocked() { TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable); TareHandlerThread.getHandler().removeCallbacks(mWriteRunnable); @@ -504,7 +523,6 @@ public class Scribe { return earliestEndTime; } - /** * @param parser Xml parser at the beginning of a {@link #XML_TAG_PERIOD_REPORT} tag. The next * "parser.next()" call will take the parser into the body of the report tag. @@ -531,6 +549,10 @@ public class Scribe { parser.getAttributeLong(null, XML_ATTR_PR_NEG_REGULATIONS); report.numNegativeRegulations = parser.getAttributeInt(null, XML_ATTR_PR_NUM_NEG_REGULATIONS); + report.screenOffDurationMs = + parser.getAttributeLong(null, XML_ATTR_PR_SCREEN_OFF_DURATION_MS, 0); + report.screenOffDischargeMah = + parser.getAttributeLong(null, XML_ATTR_PR_SCREEN_OFF_DISCHARGE_MAH, 0); return report; } @@ -606,6 +628,8 @@ public class Scribe { out.startTag(null, XML_TAG_HIGH_LEVEL_STATE); out.attributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME, mLastReclamationTime); + out.attributeLong(null, + XML_ATTR_LAST_STOCK_RECALCULATION_TIME, mLastStockRecalculationTime); out.attributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mSatiatedConsumptionLimit); out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_CAKES, mRemainingConsumableCakes); @@ -718,6 +742,8 @@ public class Scribe { out.attributeInt(null, XML_ATTR_PR_NUM_POS_REGULATIONS, report.numPositiveRegulations); out.attributeLong(null, XML_ATTR_PR_NEG_REGULATIONS, report.cumulativeNegativeRegulations); out.attributeInt(null, XML_ATTR_PR_NUM_NEG_REGULATIONS, report.numNegativeRegulations); + out.attributeLong(null, XML_ATTR_PR_SCREEN_OFF_DURATION_MS, report.screenOffDurationMs); + out.attributeLong(null, XML_ATTR_PR_SCREEN_OFF_DISCHARGE_MAH, report.screenOffDischargeMah); out.endTag(null, XML_TAG_PERIOD_REPORT); } diff --git a/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp b/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp index 8f9e187a7a93..b2ed4d47adf4 100644 --- a/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp +++ b/apex/jobscheduler/service/jni/com_android_server_alarm_AlarmManagerService.cpp @@ -76,19 +76,17 @@ typedef std::array<int, N_ANDROID_TIMERFDS> TimerFds; class AlarmImpl { public: - AlarmImpl(const TimerFds &fds, int epollfd, const std::string &rtc_dev) - : fds{fds}, epollfd{epollfd}, rtc_dev{rtc_dev} {} + AlarmImpl(const TimerFds &fds, int epollfd) + : fds{fds}, epollfd{epollfd} {} ~AlarmImpl(); int set(int type, struct timespec *ts); - int setTime(struct timeval *tv); int waitForAlarm(); int getTime(int type, struct itimerspec *spec); private: const TimerFds fds; const int epollfd; - std::string rtc_dev; }; AlarmImpl::~AlarmImpl() @@ -131,43 +129,6 @@ int AlarmImpl::getTime(int type, struct itimerspec *spec) return timerfd_gettime(fds[type], spec); } -int AlarmImpl::setTime(struct timeval *tv) -{ - if (settimeofday(tv, NULL) == -1) { - ALOGV("settimeofday() failed: %s", strerror(errno)); - return -1; - } - - android::base::unique_fd fd{open(rtc_dev.c_str(), O_RDWR)}; - if (!fd.ok()) { - ALOGE("Unable to open %s: %s", rtc_dev.c_str(), strerror(errno)); - return -1; - } - - struct tm tm; - if (!gmtime_r(&tv->tv_sec, &tm)) { - ALOGV("gmtime_r() failed: %s", strerror(errno)); - return -1; - } - - struct rtc_time rtc = {}; - rtc.tm_sec = tm.tm_sec; - rtc.tm_min = tm.tm_min; - rtc.tm_hour = tm.tm_hour; - rtc.tm_mday = tm.tm_mday; - rtc.tm_mon = tm.tm_mon; - rtc.tm_year = tm.tm_year; - rtc.tm_wday = tm.tm_wday; - rtc.tm_yday = tm.tm_yday; - rtc.tm_isdst = tm.tm_isdst; - if (ioctl(fd, RTC_SET_TIME, &rtc) == -1) { - ALOGV("RTC_SET_TIME ioctl failed: %s", strerror(errno)); - return -1; - } - - return 0; -} - int AlarmImpl::waitForAlarm() { epoll_event events[N_ANDROID_TIMERFDS]; @@ -198,28 +159,6 @@ int AlarmImpl::waitForAlarm() return result; } -static jint android_server_alarm_AlarmManagerService_setKernelTime(JNIEnv*, jobject, jlong nativeData, jlong millis) -{ - AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData); - - if (millis <= 0 || millis / 1000LL >= std::numeric_limits<time_t>::max()) { - return -1; - } - - struct timeval tv; - tv.tv_sec = (millis / 1000LL); - tv.tv_usec = ((millis % 1000LL) * 1000LL); - - ALOGD("Setting time of day to sec=%ld", tv.tv_sec); - - int ret = impl->setTime(&tv); - if (ret < 0) { - ALOGW("Unable to set rtc to %ld: %s", tv.tv_sec, strerror(errno)); - ret = -1; - } - return ret; -} - static jint android_server_alarm_AlarmManagerService_setKernelTimezone(JNIEnv*, jobject, jlong, jint minswest) { struct timezone tz; @@ -287,19 +226,7 @@ static jlong android_server_alarm_AlarmManagerService_init(JNIEnv*, jobject) } } - // Find the wall clock RTC. We expect this always to be /dev/rtc0, but - // check the /dev/rtc symlink first so that legacy devices that don't use - // rtc0 can add a symlink rather than need to carry a local patch to this - // code. - // - // TODO: if you're reading this in a world where all devices are using the - // GKI, you can remove the readlink and just assume /dev/rtc0. - std::string dev_rtc; - if (!android::base::Readlink("/dev/rtc", &dev_rtc)) { - dev_rtc = "/dev/rtc0"; - } - - std::unique_ptr<AlarmImpl> alarm{new AlarmImpl(fds, epollfd, dev_rtc)}; + std::unique_ptr<AlarmImpl> alarm{new AlarmImpl(fds, epollfd)}; for (size_t i = 0; i < fds.size(); i++) { epoll_event event; @@ -392,7 +319,6 @@ static const JNINativeMethod sMethods[] = { {"close", "(J)V", (void*)android_server_alarm_AlarmManagerService_close}, {"set", "(JIJJ)I", (void*)android_server_alarm_AlarmManagerService_set}, {"waitForAlarm", "(J)I", (void*)android_server_alarm_AlarmManagerService_waitForAlarm}, - {"setKernelTime", "(JJ)I", (void*)android_server_alarm_AlarmManagerService_setKernelTime}, {"setKernelTimezone", "(JI)I", (void*)android_server_alarm_AlarmManagerService_setKernelTimezone}, {"getNextAlarm", "(JI)J", (void*)android_server_alarm_AlarmManagerService_getNextAlarm}, }; diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index 320e147c93a5..44311648da80 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -23,6 +23,7 @@ #include <cstring> #include <filesystem> #include <fstream> +#include <limits> #include <memory> #include <ostream> #include <string> @@ -301,28 +302,42 @@ Status Idmap2Service::createFabricatedOverlay( return ok(); } -Status Idmap2Service::acquireFabricatedOverlayIterator() { +Status Idmap2Service::acquireFabricatedOverlayIterator(int32_t* _aidl_return) { + std::lock_guard l(frro_iter_mutex_); if (frro_iter_.has_value()) { LOG(WARNING) << "active ffro iterator was not previously released"; } frro_iter_ = std::filesystem::directory_iterator(kIdmapCacheDir); + if (frro_iter_id_ == std::numeric_limits<int32_t>::max()) { + frro_iter_id_ = 0; + } else { + ++frro_iter_id_; + } + *_aidl_return = frro_iter_id_; return ok(); } -Status Idmap2Service::releaseFabricatedOverlayIterator() { +Status Idmap2Service::releaseFabricatedOverlayIterator(int32_t iteratorId) { + std::lock_guard l(frro_iter_mutex_); if (!frro_iter_.has_value()) { LOG(WARNING) << "no active ffro iterator to release"; + } else if (frro_iter_id_ != iteratorId) { + LOG(WARNING) << "incorrect iterator id in a call to release"; } else { - frro_iter_ = std::nullopt; + frro_iter_.reset(); } return ok(); } -Status Idmap2Service::nextFabricatedOverlayInfos( +Status Idmap2Service::nextFabricatedOverlayInfos(int32_t iteratorId, std::vector<os::FabricatedOverlayInfo>* _aidl_return) { + std::lock_guard l(frro_iter_mutex_); + constexpr size_t kMaxEntryCount = 100; if (!frro_iter_.has_value()) { return error("no active frro iterator"); + } else if (frro_iter_id_ != iteratorId) { + return error("incorrect iterator id in a call to next"); } size_t count = 0; @@ -330,22 +345,22 @@ Status Idmap2Service::nextFabricatedOverlayInfos( auto entry_iter_end = end(*frro_iter_); for (; entry_iter != entry_iter_end && count < kMaxEntryCount; ++entry_iter) { auto& entry = *entry_iter; - if (!entry.is_regular_file() || !android::IsFabricatedOverlay(entry.path())) { + if (!entry.is_regular_file() || !android::IsFabricatedOverlay(entry.path().native())) { continue; } - const auto overlay = FabricatedOverlayContainer::FromPath(entry.path()); + const auto overlay = FabricatedOverlayContainer::FromPath(entry.path().native()); if (!overlay) { LOG(WARNING) << "Failed to open '" << entry.path() << "': " << overlay.GetErrorMessage(); continue; } - const auto info = (*overlay)->GetManifestInfo(); + auto info = (*overlay)->GetManifestInfo(); os::FabricatedOverlayInfo out_info; - out_info.packageName = info.package_name; - out_info.overlayName = info.name; - out_info.targetPackageName = info.target_package; - out_info.targetOverlayable = info.target_name; + out_info.packageName = std::move(info.package_name); + out_info.overlayName = std::move(info.name); + out_info.targetPackageName = std::move(info.target_package); + out_info.targetOverlayable = std::move(info.target_name); out_info.path = entry.path(); _aidl_return->emplace_back(std::move(out_info)); count++; diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h index c61e4bc98a54..cc8cc5f218d2 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.h +++ b/cmds/idmap2/idmap2d/Idmap2Service.h @@ -26,7 +26,10 @@ #include <filesystem> #include <memory> +#include <mutex> +#include <optional> #include <string> +#include <variant> #include <vector> namespace android::os { @@ -60,11 +63,11 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 { binder::Status deleteFabricatedOverlay(const std::string& overlay_path, bool* _aidl_return) override; - binder::Status acquireFabricatedOverlayIterator() override; + binder::Status acquireFabricatedOverlayIterator(int32_t* _aidl_return) override; - binder::Status releaseFabricatedOverlayIterator() override; + binder::Status releaseFabricatedOverlayIterator(int32_t iteratorId) override; - binder::Status nextFabricatedOverlayInfos( + binder::Status nextFabricatedOverlayInfos(int32_t iteratorId, std::vector<os::FabricatedOverlayInfo>* _aidl_return) override; binder::Status dumpIdmap(const std::string& overlay_path, std::string* _aidl_return) override; @@ -74,7 +77,9 @@ class Idmap2Service : public BinderService<Idmap2Service>, public BnIdmap2 { // be able to be recalculated if idmap2 dies and restarts. std::unique_ptr<idmap2::TargetResourceContainer> framework_apk_cache_; + int32_t frro_iter_id_ = 0; std::optional<std::filesystem::directory_iterator> frro_iter_; + std::mutex frro_iter_mutex_; template <typename T> using MaybeUniquePtr = std::variant<std::unique_ptr<T>, T*>; diff --git a/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl b/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl index 0059cf293177..2bbfba97a6c6 100644 --- a/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl +++ b/cmds/idmap2/idmap2d/aidl/services/android/os/IIdmap2.aidl @@ -41,9 +41,9 @@ interface IIdmap2 { @nullable FabricatedOverlayInfo createFabricatedOverlay(in FabricatedOverlayInternal overlay); boolean deleteFabricatedOverlay(@utf8InCpp String path); - void acquireFabricatedOverlayIterator(); - void releaseFabricatedOverlayIterator(); - List<FabricatedOverlayInfo> nextFabricatedOverlayInfos(); + int acquireFabricatedOverlayIterator(); + void releaseFabricatedOverlayIterator(int iteratorId); + List<FabricatedOverlayInfo> nextFabricatedOverlayInfos(int iteratorId); @utf8InCpp String dumpIdmap(@utf8InCpp String overlayApkPath); } diff --git a/core/api/current.txt b/core/api/current.txt index 9cfc161fb7e4..7a412d8e2a81 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9015,6 +9015,7 @@ package android.companion { public final class CompanionDeviceManager { method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER, android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING, android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull android.companion.CompanionDeviceManager.Callback, @Nullable android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH, android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER, android.Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING, android.Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION}, conditional=true) public void associate(@NonNull android.companion.AssociationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.companion.CompanionDeviceManager.Callback); + method @Nullable public android.content.IntentSender buildAssociationCancellationIntent(); method @Nullable public android.content.IntentSender buildPermissionTransferUserConsentIntent(int) throws android.companion.DeviceNotAssociatedException; method @Deprecated public void disassociate(@NonNull String); method public void disassociate(int); @@ -45192,6 +45193,14 @@ package android.text { method public void getChars(int, int, char[], int); } + public class GraphemeClusterSegmentFinder extends android.text.SegmentFinder { + ctor public GraphemeClusterSegmentFinder(@NonNull CharSequence, @NonNull android.text.TextPaint); + method public int nextEndBoundary(@IntRange(from=0) int); + method public int nextStartBoundary(@IntRange(from=0) int); + method public int previousEndBoundary(@IntRange(from=0) int); + method public int previousStartBoundary(@IntRange(from=0) int); + } + public class Html { method public static String escapeHtml(CharSequence); method @Deprecated public static android.text.Spanned fromHtml(String); @@ -45321,6 +45330,7 @@ package android.text { method public final int getParagraphLeft(int); method public final int getParagraphRight(int); method public float getPrimaryHorizontal(int); + method @Nullable public int[] getRangeForRect(@NonNull android.graphics.RectF, @NonNull android.text.SegmentFinder, @NonNull android.text.Layout.TextInclusionStrategy); method public float getSecondaryHorizontal(int); method public void getSelectionPath(int, int, android.graphics.Path); method public final float getSpacingAdd(); @@ -45344,6 +45354,9 @@ package android.text { field public static final int HYPHENATION_FREQUENCY_NONE = 0; // 0x0 field public static final int HYPHENATION_FREQUENCY_NORMAL = 1; // 0x1 field public static final int HYPHENATION_FREQUENCY_NORMAL_FAST = 3; // 0x3 + field @NonNull public static final android.text.Layout.TextInclusionStrategy INCLUSION_STRATEGY_ANY_OVERLAP; + field @NonNull public static final android.text.Layout.TextInclusionStrategy INCLUSION_STRATEGY_CONTAINS_ALL; + field @NonNull public static final android.text.Layout.TextInclusionStrategy INCLUSION_STRATEGY_CONTAINS_CENTER; field public static final int JUSTIFICATION_MODE_INTER_WORD = 1; // 0x1 field public static final int JUSTIFICATION_MODE_NONE = 0; // 0x0 } @@ -45357,6 +45370,10 @@ package android.text { public static class Layout.Directions { } + @java.lang.FunctionalInterface public static interface Layout.TextInclusionStrategy { + method public boolean isSegmentInside(@NonNull android.graphics.RectF, @NonNull android.graphics.RectF); + } + @Deprecated public abstract class LoginFilter implements android.text.InputFilter { method @Deprecated public CharSequence filter(CharSequence, int, int, android.text.Spanned, int, int); method @Deprecated public abstract boolean isAllowed(char); @@ -45433,6 +45450,15 @@ package android.text { method public android.text.PrecomputedText.Params.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic); } + public abstract class SegmentFinder { + ctor public SegmentFinder(); + method public abstract int nextEndBoundary(@IntRange(from=0) int); + method public abstract int nextStartBoundary(@IntRange(from=0) int); + method public abstract int previousEndBoundary(@IntRange(from=0) int); + method public abstract int previousStartBoundary(@IntRange(from=0) int); + field public static final int DONE = -1; // 0xffffffff + } + public class Selection { method public static boolean extendDown(android.text.Spannable, android.text.Layout); method public static boolean extendLeft(android.text.Spannable, android.text.Layout); @@ -45718,6 +45744,14 @@ package android.text { method public void onTextChanged(CharSequence, int, int, int); } + public class WordSegmentFinder extends android.text.SegmentFinder { + ctor public WordSegmentFinder(@NonNull CharSequence, @NonNull java.util.Locale); + method public int nextEndBoundary(@IntRange(from=0) int); + method public int nextStartBoundary(@IntRange(from=0) int); + method public int previousEndBoundary(@IntRange(from=0) int); + method public int previousStartBoundary(@IntRange(from=0) int); + } + } package android.text.format { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 212e3581269a..422b6a389b05 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -8051,8 +8051,9 @@ public class Activity extends ContextThemeWrapper resultData.prepareToLeaveProcess(this); } upIntent.prepareToLeaveProcess(this); - return ActivityClient.getInstance().navigateUpTo(mToken, upIntent, resultCode, - resultData); + String resolvedType = upIntent.resolveTypeIfNeeded(getContentResolver()); + return ActivityClient.getInstance().navigateUpTo(mToken, upIntent, resolvedType, + resultCode, resultData); } else { return mParent.navigateUpToFromChild(this, upIntent); } diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index 482f456b5d83..d1e6780e3618 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -141,11 +141,11 @@ public class ActivityClient { } } - boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode, + boolean navigateUpTo(IBinder token, Intent destIntent, String resolvedType, int resultCode, Intent resultData) { try { - return getActivityClientController().navigateUpTo(token, destIntent, resultCode, - resultData); + return getActivityClientController().navigateUpTo(token, destIntent, resolvedType, + resultCode, resultData); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 626b7d3cd5b1..8da9631a23b0 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -31,6 +31,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ActivityPresentationInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PermissionMethod; +import android.content.pm.PermissionName; import android.content.pm.UserInfo; import android.net.Uri; import android.os.Bundle; @@ -294,7 +295,7 @@ public abstract class ActivityManagerInternal { /** Checks if the calling binder pid as the permission. */ @PermissionMethod - public abstract void enforceCallingPermission(String permission, String func); + public abstract void enforceCallingPermission(@PermissionName String permission, String func); /** Returns the current user id. */ public abstract int getCurrentUserId(); diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index f5e5cda9c639..9aa67bc51182 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -60,8 +60,8 @@ interface IActivityClientController { in SizeConfigurationBuckets sizeConfigurations); boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot); boolean shouldUpRecreateTask(in IBinder token, in String destAffinity); - boolean navigateUpTo(in IBinder token, in Intent target, int resultCode, - in Intent resultData); + boolean navigateUpTo(in IBinder token, in Intent target, in String resolvedType, + int resultCode, in Intent resultData); boolean releaseActivityInstance(in IBinder token); boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask); boolean finishActivityAffinity(in IBinder token); diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 2bd36b8675e1..4142bceb2c45 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -30,6 +30,7 @@ import android.annotation.UserHandleAware; import android.app.Activity; import android.app.NotificationManager; import android.app.PendingIntent; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; import android.content.Context; @@ -367,6 +368,10 @@ public final class CompanionDeviceManager { * recommended to do when an association is no longer relevant to avoid unnecessary battery * and/or data drain resulting from special privileges that the association provides</p> * + * <p>Note that if you use this api to associate with a Bluetooth device, please make sure + * to cancel your own Bluetooth discovery before calling this api, otherwise the callback + * may fail to return the desired device.</p> + * * <p>Calling this API requires a uses-feature * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p> ** @@ -377,6 +382,7 @@ public final class CompanionDeviceManager { * @see AssociationRequest.Builder * @see #getMyAssociations() * @see #disassociate(int) + * @see BluetoothAdapter#cancelDiscovery() */ @UserHandleAware @RequiresPermission(anyOf = { @@ -403,6 +409,34 @@ public final class CompanionDeviceManager { } /** + * Cancel the current association activity. + * + * <p>The app should launch the returned {@code intentSender} by calling + * {@link Activity#startIntentSenderForResult(IntentSender, int, Intent, int, int, int)} to + * cancel the current association activity</p> + * + * <p>Calling this API requires a uses-feature + * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p> + * + * @return An {@link IntentSender} that the app should use to launch in order to cancel the + * current association activity + */ + @UserHandleAware + @Nullable + public IntentSender buildAssociationCancellationIntent() { + if (!checkFeaturePresent()) return null; + + try { + PendingIntent pendingIntent = mService.buildAssociationCancellationIntent( + mContext.getOpPackageName(), mContext.getUserId()); + return pendingIntent.getIntentSender(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + + /** * <p>Calling this API requires a uses-feature * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p> * diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl index 17e313252010..24ef52b37dab 100644 --- a/core/java/android/companion/ICompanionDeviceManager.aidl +++ b/core/java/android/companion/ICompanionDeviceManager.aidl @@ -82,4 +82,6 @@ interface ICompanionDeviceManager { void detachSystemDataTransport(String packageName, int userId, int associationId); boolean isCompanionApplicationBound(String packageName, int userId); + + PendingIntent buildAssociationCancellationIntent(in String callingPackage, int userId); } diff --git a/core/java/android/companion/OWNERS b/core/java/android/companion/OWNERS index 004f66caed7b..0348fe2776fe 100644 --- a/core/java/android/companion/OWNERS +++ b/core/java/android/companion/OWNERS @@ -1,5 +1,3 @@ -ewol@google.com evanxinchen@google.com guojing@google.com -svetoslavganov@google.com -sergeynv@google.com
\ No newline at end of file +raphk@google.com
\ No newline at end of file diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 0d73c1375b03..cb5a99fcc811 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -52,6 +52,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PermissionMethod; +import android.content.pm.PermissionName; import android.content.res.AssetManager; import android.content.res.ColorStateList; import android.content.res.Configuration; @@ -6088,7 +6089,8 @@ public abstract class Context { @CheckResult(suggest="#enforcePermission(String,int,int,String)") @PackageManager.PermissionResult @PermissionMethod - public abstract int checkPermission(@NonNull String permission, int pid, int uid); + public abstract int checkPermission( + @NonNull @PermissionName String permission, int pid, int uid); /** @hide */ @SuppressWarnings("HiddenAbstractMethod") @@ -6121,7 +6123,7 @@ public abstract class Context { @CheckResult(suggest="#enforceCallingPermission(String,String)") @PackageManager.PermissionResult @PermissionMethod - public abstract int checkCallingPermission(@NonNull String permission); + public abstract int checkCallingPermission(@NonNull @PermissionName String permission); /** * Determine whether the calling process of an IPC <em>or you</em> have been @@ -6142,7 +6144,7 @@ public abstract class Context { @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") @PackageManager.PermissionResult @PermissionMethod - public abstract int checkCallingOrSelfPermission(@NonNull String permission); + public abstract int checkCallingOrSelfPermission(@NonNull @PermissionName String permission); /** * Determine whether <em>you</em> have been granted a particular permission. @@ -6172,7 +6174,7 @@ public abstract class Context { */ @PermissionMethod public abstract void enforcePermission( - @NonNull String permission, int pid, int uid, @Nullable String message); + @NonNull @PermissionName String permission, int pid, int uid, @Nullable String message); /** * If the calling process of an IPC you are handling has not been @@ -6194,7 +6196,7 @@ public abstract class Context { */ @PermissionMethod public abstract void enforceCallingPermission( - @NonNull String permission, @Nullable String message); + @NonNull @PermissionName String permission, @Nullable String message); /** * If neither you nor the calling process of an IPC you are @@ -6211,7 +6213,7 @@ public abstract class Context { */ @PermissionMethod public abstract void enforceCallingOrSelfPermission( - @NonNull String permission, @Nullable String message); + @NonNull @PermissionName String permission, @Nullable String message); /** * Grant permission to access a specific Uri to another package, regardless diff --git a/core/java/android/content/pm/PermissionMethod.java b/core/java/android/content/pm/PermissionMethod.java index 021b2e1cd8f1..ba97342c5e3e 100644 --- a/core/java/android/content/pm/PermissionMethod.java +++ b/core/java/android/content/pm/PermissionMethod.java @@ -26,7 +26,7 @@ import java.lang.annotation.Target; * Documents that the subject method's job is to look * up whether the provided or calling uid/pid has the requested permission. * - * Methods should either return `void`, but potentially throw {@link SecurityException}, + * <p>Methods should either return `void`, but potentially throw {@link SecurityException}, * or return {@link android.content.pm.PackageManager.PermissionResult} `int`. * * @hide diff --git a/core/java/android/content/pm/PermissionName.java b/core/java/android/content/pm/PermissionName.java new file mode 100644 index 000000000000..719e13be05cc --- /dev/null +++ b/core/java/android/content/pm/PermissionName.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.LOCAL_VARIABLE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.CLASS; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Denotes that the annotated {@link String} represents a permission name. + * + * @hide + */ +@Retention(CLASS) +@Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD}) +public @interface PermissionName {} diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 4965057a7fdb..8bfa0e99e3b4 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -340,9 +340,6 @@ public final class Trace { * @hide */ public static void instant(long traceTag, String methodName) { - if (methodName == null) { - throw new IllegalArgumentException("methodName cannot be null"); - } if (isTagEnabled(traceTag)) { nativeInstant(traceTag, methodName); } @@ -357,12 +354,6 @@ public final class Trace { * @hide */ public static void instantForTrack(long traceTag, String trackName, String methodName) { - if (trackName == null) { - throw new IllegalArgumentException("trackName cannot be null"); - } - if (methodName == null) { - throw new IllegalArgumentException("methodName cannot be null"); - } if (isTagEnabled(traceTag)) { nativeInstantForTrack(traceTag, trackName, methodName); } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 345486e390cd..99b9156dacde 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -808,6 +808,13 @@ public final class DeviceConfig { @SystemApi public static final String NAMESPACE_BACKUP_AND_RESTORE = "backup_and_restore"; + /** + * Namespace for ARC App Compat related features. + * + * @hide + */ + public static final String NAMESPACE_ARC_APP_COMPAT = "arc_app_compat"; + private static final Object sLock = new Object(); @GuardedBy("sLock") private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners = diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 8efc5eb6b6ff..e720f1ab1523 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -65,6 +65,7 @@ public final class KeymasterDefs { public static final int KM_TAG_PADDING = Tag.PADDING; // KM_ENUM_REP | 6; public static final int KM_TAG_CALLER_NONCE = Tag.CALLER_NONCE; // KM_BOOL | 7; public static final int KM_TAG_MIN_MAC_LENGTH = Tag.MIN_MAC_LENGTH; // KM_UINT | 8; + public static final int KM_TAG_EC_CURVE = Tag.EC_CURVE; // KM_ENUM | 10; public static final int KM_TAG_RSA_PUBLIC_EXPONENT = Tag.RSA_PUBLIC_EXPONENT; // KM_ULONG | 200; public static final int KM_TAG_INCLUDE_UNIQUE_ID = Tag.INCLUDE_UNIQUE_ID; // KM_BOOL | 202; diff --git a/core/java/android/text/GraphemeClusterSegmentIterator.java b/core/java/android/text/GraphemeClusterSegmentFinder.java index e3976a7eab35..8b3db0a51691 100644 --- a/core/java/android/text/GraphemeClusterSegmentIterator.java +++ b/core/java/android/text/GraphemeClusterSegmentFinder.java @@ -21,16 +21,20 @@ import android.annotation.NonNull; import android.graphics.Paint; /** - * Implementation of {@code SegmentIterator} using grapheme clusters as the text segment. Whitespace + * Implementation of {@code SegmentFinder} using grapheme clusters as the text segment. Whitespace * characters are included as segments. * - * @hide + * <p>For example, the text "a pot" would be divided into five text segments: "a", " ", "p", "o", + * "t". + * + * @see <a href="https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries">Unicode Text + * Segmentation - Grapheme Cluster Boundaries</a> */ -public class GraphemeClusterSegmentIterator extends SegmentIterator { +public class GraphemeClusterSegmentFinder extends SegmentFinder { private final CharSequence mText; private final TextPaint mTextPaint; - public GraphemeClusterSegmentIterator( + public GraphemeClusterSegmentFinder( @NonNull CharSequence text, @NonNull TextPaint textPaint) { mText = text; mTextPaint = textPaint; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index dbb41f47a495..913e9cc1a246 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -168,6 +168,31 @@ public abstract class Layout { public static final float DEFAULT_LINESPACING_ADDITION = 0.0f; /** + * Strategy which considers a text segment to be inside a rectangle area if the segment bounds + * intersect the rectangle. + */ + @NonNull + public static final TextInclusionStrategy INCLUSION_STRATEGY_ANY_OVERLAP = + RectF::intersects; + + /** + * Strategy which considers a text segment to be inside a rectangle area if the center of the + * segment bounds is inside the rectangle. + */ + @NonNull + public static final TextInclusionStrategy INCLUSION_STRATEGY_CONTAINS_CENTER = + (segmentBounds, area) -> + area.contains(segmentBounds.centerX(), segmentBounds.centerY()); + + /** + * Strategy which considers a text segment to be inside a rectangle area if the segment bounds + * are completely contained within the rectangle. + */ + @NonNull + public static final TextInclusionStrategy INCLUSION_STRATEGY_CONTAINS_ALL = + (segmentBounds, area) -> area.contains(segmentBounds); + + /** * Return how wide a layout must be in order to display the specified text with one line per * paragraph. * @@ -1818,11 +1843,11 @@ public abstract class Layout { * is the start of the first text segment inside the area, and the end of the range is the end * of the last text segment inside the area. * - * <p>A text segment is considered to be inside the area if the center of its bounds is inside - * the area. If a text segment spans multiple lines or multiple directional runs (e.g. a - * hyphenated word), the text segment is divided into pieces at the line and run breaks, then - * the text segment is considered to be inside the area if any of its pieces have their center - * inside the area. + * <p>A text segment is considered to be inside the area according to the provided {@link + * TextInclusionStrategy}. If a text segment spans multiple lines or multiple directional runs + * (e.g. a hyphenated word), the text segment is divided into pieces at the line and run breaks, + * then the text segment is considered to be inside the area if any of its pieces are inside the + * area. * * <p>The returned range may also include text segments which are not inside the specified area, * if those text segments are in between text segments which are inside the area. For example, @@ -1830,62 +1855,59 @@ public abstract class Layout { * inside the area and "segment2" is not. * * @param area area for which the text range will be found - * @param segmentIterator iterator for determining the ranges of text to be considered as a text - * segment + * @param segmentFinder SegmentFinder for determining the ranges of text to be considered as a + * text segment + * @param inclusionStrategy strategy for determining whether a text segment is inside the + * specified area * @return int array of size 2 containing the start (inclusive) and end (exclusive) character * offsets of the range, or null if there are no text segments inside the area - * @hide */ @Nullable - public int[] getRangeForRect(@NonNull RectF area, @NonNull SegmentIterator segmentIterator) { - // Find the first line whose vertical center is below the top of the area. + public int[] getRangeForRect(@NonNull RectF area, @NonNull SegmentFinder segmentFinder, + @NonNull TextInclusionStrategy inclusionStrategy) { + // Find the first line whose bottom (without line spacing) is below the top of the area. int startLine = getLineForVertical((int) area.top); - int startLineTop = getLineTop(startLine); - int startLineBottom = getLineBottom(startLine, /* includeLineSpacing= */ false); - if (area.top > (startLineTop + startLineBottom) / 2f) { + if (area.top > getLineBottom(startLine, /* includeLineSpacing= */ false)) { startLine++; if (startLine >= getLineCount()) { - // The top of the area is below the vertical center of the last line, so the area - // does not contain any text. + // The entire area is below the last line, so it does not contain any text. return null; } } - // Find the last line whose vertical center is above the bottom of the area. + // Find the last line whose top is above the bottom of the area. int endLine = getLineForVertical((int) area.bottom); - int endLineTop = getLineTop(endLine); - int endLineBottom = getLineBottom(endLine, /* includeLineSpacing= */ false); - if (area.bottom < (endLineTop + endLineBottom) / 2f) { - endLine--; + if (endLine == 0 && area.bottom < getLineTop(0)) { + // The entire area is above the first line, so it does not contain any text. + return null; } if (endLine < startLine) { - // There are no lines with vertical centers between the top and bottom of the area, so - // the area does not contain any text. + // The entire area is between two lines, so it does not contain any text. return null; } - int start = getStartOrEndOffsetForHorizontalInterval( - startLine, area.left, area.right, segmentIterator, /* getStart= */ true); + int start = getStartOrEndOffsetForAreaWithinLine( + startLine, area, segmentFinder, inclusionStrategy, /* getStart= */ true); // If the area does not contain any text on this line, keep trying subsequent lines until // the end line is reached. while (start == -1 && startLine < endLine) { startLine++; - start = getStartOrEndOffsetForHorizontalInterval( - startLine, area.left, area.right, segmentIterator, /* getStart= */ true); + start = getStartOrEndOffsetForAreaWithinLine( + startLine, area, segmentFinder, inclusionStrategy, /* getStart= */ true); } if (start == -1) { // All lines were checked, the area does not contain any text. return null; } - int end = getStartOrEndOffsetForHorizontalInterval( - endLine, area.left, area.right, segmentIterator, /* getStart= */ false); + int end = getStartOrEndOffsetForAreaWithinLine( + endLine, area, segmentFinder, inclusionStrategy, /* getStart= */ false); // If the area does not contain any text on this line, keep trying previous lines until // the start line is reached. while (end == -1 && startLine < endLine) { endLine--; - end = getStartOrEndOffsetForHorizontalInterval( - endLine, area.left, area.right, segmentIterator, /* getStart= */ false); + end = getStartOrEndOffsetForAreaWithinLine( + endLine, area, segmentFinder, inclusionStrategy, /* getStart= */ false); } if (end == -1) { // All lines were checked, the area does not contain any text. @@ -1893,33 +1915,39 @@ public abstract class Layout { } // If a text segment spans multiple lines or multiple directional runs (e.g. a hyphenated - // word), then getStartOrEndOffsetForHorizontalInterval() can return an offset in the middle - // of a text segment. Adjust the range to include the rest of any partial text segments. If + // word), then getStartOrEndOffsetForAreaWithinLine() can return an offset in the middle of + // a text segment. Adjust the range to include the rest of any partial text segments. If // start is already the start boundary of a text segment, then this is a no-op. - start = segmentIterator.previousStartBoundary(start + 1); - end = segmentIterator.nextEndBoundary(end - 1); + start = segmentFinder.previousStartBoundary(start + 1); + end = segmentFinder.nextEndBoundary(end - 1); return new int[] {start, end}; } /** - * Finds the start character offset of the first text segment inside a horizontal interval - * within a line, or the end character offset of the last text segment inside the horizontal - * interval. + * Finds the start character offset of the first text segment within a line inside the specified + * rectangle area, or the end character offset of the last text segment inside the area. * * @param line index of the line to search - * @param left left bound of the horizontal interval - * @param right right bound of the horizontal interval - * @param segmentIterator iterator for determining the ranges of text to be considered as a text - * segment - * @param getStart true to find the start of the first text segment inside the horizontal - * interval, false to find the end of the last text segment - * @return the start character offset of the first text segment inside the horizontal interval, - * or the end character offset of the last text segment inside the horizontal interval. - */ - private int getStartOrEndOffsetForHorizontalInterval( - @IntRange(from = 0) int line, float left, float right, - @NonNull SegmentIterator segmentIterator, boolean getStart) { + * @param area area inside which text segments will be found + * @param segmentFinder SegmentFinder for determining the ranges of text to be considered as a + * text segment + * @param inclusionStrategy strategy for determining whether a text segment is inside the + * specified area + * @param getStart true to find the start of the first text segment inside the area, false to + * find the end of the last text segment + * @return the start character offset of the first text segment inside the area, or the end + * character offset of the last text segment inside the area. + */ + private int getStartOrEndOffsetForAreaWithinLine( + @IntRange(from = 0) int line, + @NonNull RectF area, + @NonNull SegmentFinder segmentFinder, + @NonNull TextInclusionStrategy inclusionStrategy, + boolean getStart) { + int lineTop = getLineTop(line); + int lineBottom = getLineBottom(line, /* includeLineSpacing= */ false); + int lineStartOffset = getLineStart(line); int lineEndOffset = getLineEnd(line); if (lineStartOffset == lineEndOffset) { @@ -1952,14 +1980,16 @@ public abstract class Layout { int result = getStart - ? getStartOffsetForHorizontalIntervalWithinRun( - left, right, lineStartOffset, lineStartPos, horizontalBounds, - runStartOffset, runEndOffset, runLeft, runRight, isRtl, - segmentIterator) - : getEndOffsetForHorizontalIntervalWithinRun( - left, right, lineStartOffset, lineStartPos, horizontalBounds, + ? getStartOffsetForAreaWithinRun( + area, lineTop, lineBottom, + lineStartOffset, lineStartPos, horizontalBounds, + runStartOffset, runEndOffset, runLeft, runRight, isRtl, + segmentFinder, inclusionStrategy) + : getEndOffsetForAreaWithinRun( + area, lineTop, lineBottom, + lineStartOffset, lineStartPos, horizontalBounds, runStartOffset, runEndOffset, runLeft, runRight, isRtl, - segmentIterator); + segmentFinder, inclusionStrategy); if (result >= 0) { return result; } @@ -1970,11 +2000,12 @@ public abstract class Layout { } /** - * Finds the start character offset of the first text segment inside a horizontal interval - * within a directional run. + * Finds the start character offset of the first text segment within a directional run inside + * the specified rectangle area. * - * @param left left bound of the horizontal interval - * @param right right bound of the horizontal interval + * @param area area inside which text segments will be found + * @param lineTop top of the line containing this run + * @param lineBottom bottom (not including line spacing) of the line containing this run * @param lineStartOffset start character offset of the line containing this run * @param lineStartPos start position of the line containing this run * @param horizontalBounds array containing the signed horizontal bounds of the characters in @@ -1985,28 +2016,32 @@ public abstract class Layout { * @param runLeft left bound of the run * @param runRight right bound of the run * @param isRtl whether the run is right-to-left - * @param segmentIterator iterator for determining the ranges of text to be considered as a text - * segment - * @return the start character offset of the first text segment inside the horizontal interval - */ - private static int getStartOffsetForHorizontalIntervalWithinRun( - float left, float right, + * @param segmentFinder SegmentFinder for determining the ranges of text to be considered as a + * text segment + * @param inclusionStrategy strategy for determining whether a text segment is inside the + * specified area + * @return the start character offset of the first text segment inside the area + */ + private static int getStartOffsetForAreaWithinRun( + @NonNull RectF area, + int lineTop, int lineBottom, @IntRange(from = 0) int lineStartOffset, @IntRange(from = 0) int lineStartPos, @NonNull float[] horizontalBounds, @IntRange(from = 0) int runStartOffset, @IntRange(from = 0) int runEndOffset, float runLeft, float runRight, boolean isRtl, - @NonNull SegmentIterator segmentIterator) { - if (runRight < left || runLeft > right) { - // The run does not overlap the interval. + @NonNull SegmentFinder segmentFinder, + @NonNull TextInclusionStrategy inclusionStrategy) { + if (runRight < area.left || runLeft > area.right) { + // The run does not overlap the area. return -1; } - // Find the first character in the run whose bounds overlap with the interval. + // Find the first character in the run whose bounds overlap with the area. // firstCharOffset is an offset index within the line. int firstCharOffset; - if ((!isRtl && left <= runLeft) || (isRtl && right >= runRight)) { + if ((!isRtl && area.left <= runLeft) || (isRtl && area.right >= runRight)) { firstCharOffset = runStartOffset; } else { int low = runStartOffset; @@ -2016,73 +2051,78 @@ public abstract class Layout { guess = (high + low) / 2; // Left edge of the character at guess float pos = lineStartPos + horizontalBounds[2 * guess]; - if ((!isRtl && pos > left) || (isRtl && pos < right)) { + if ((!isRtl && pos > area.left) || (isRtl && pos < area.right)) { high = guess; } else { low = guess; } } - // The interval bound is between the left edge of the character at low and the left edge - // of the character at high. For LTR text, this is within the character at low. For RTL + // The area edge is between the left edge of the character at low and the left edge of + // the character at high. For LTR text, this is within the character at low. For RTL // text, this is within the character at high. firstCharOffset = isRtl ? high : low; } // Find the first text segment containing this character (or, if no text segment contains // this character, the first text segment after this character). All previous text segments - // in this run are to the left (for LTR) of the interval. - segmentIterator.setRunLimits( - lineStartOffset + runStartOffset, lineStartOffset + runEndOffset); + // in this run are to the left (for LTR) of the area. int segmentEndOffset = - segmentIterator.nextEndBoundaryOrRunEnd(lineStartOffset + firstCharOffset); - if (segmentEndOffset == SegmentIterator.DONE) { + segmentFinder.nextEndBoundary(lineStartOffset + firstCharOffset); + if (segmentEndOffset == SegmentFinder.DONE) { // There are no text segments containing or after firstCharOffset, so no text segments - // in this run overlap the interval. - return -1; - } - int segmentStartOffset = segmentIterator.previousStartBoundaryOrRunStart(segmentEndOffset); - float segmentCenter = lineStartPos - + (horizontalBounds[2 * (segmentStartOffset - lineStartOffset)] - + horizontalBounds[2 * (segmentEndOffset - lineStartOffset) - 1]) / 2; - if ((!isRtl && segmentCenter > right) || (isRtl && segmentCenter < left)) { - // The entire interval is to the left (for LTR) of the text segment's center. So the - // interval does not contain any text segments within this run. + // in this run overlap the area. return -1; } - if ((!isRtl && segmentCenter >= left) || (isRtl && segmentCenter <= right)) { - // The center is within the interval, so return the start offset of this text segment. - return segmentStartOffset; - } - - // If first text segment's center is not within the interval, try the next text segment. - segmentStartOffset = segmentIterator.nextStartBoundaryWithinRunLimits(segmentStartOffset); - if (segmentStartOffset == SegmentIterator.DONE - || segmentStartOffset == lineStartOffset + runEndOffset) { - // No more text segments within this run. + int segmentStartOffset = segmentFinder.previousStartBoundary(segmentEndOffset); + if (segmentStartOffset >= lineStartOffset + runEndOffset) { + // The text segment is after the end of this run, so no text segments in this run + // overlap the area. return -1; } - segmentEndOffset = segmentIterator.nextEndBoundaryOrRunEnd(segmentStartOffset); - segmentCenter = lineStartPos - + (horizontalBounds[2 * (segmentStartOffset - lineStartOffset)] - + horizontalBounds[2 * (segmentEndOffset - lineStartOffset) - 1]) / 2; - // We already know that segmentCenter >= left (for LTR) since the previous word contains the - // left point. - if ((!isRtl && segmentCenter <= right) || (isRtl && segmentCenter >= left)) { - return segmentStartOffset; - } - - // If the second text segment is also not within the interval, then this means that the - // interval is between the centers of the first and second text segments, so it does not - // contain any text segments on this line. - return -1; - } - - /** - * Finds the end character offset of the last text segment inside a horizontal interval within a - * directional run. + // If the segment extends outside of this run, only consider the piece of the segment within + // this run. + segmentStartOffset = Math.max(segmentStartOffset, lineStartOffset + runStartOffset); + segmentEndOffset = Math.min(segmentEndOffset, lineStartOffset + runEndOffset); + + RectF segmentBounds = new RectF(0, lineTop, 0, lineBottom); + while (true) { + // Start (left for LTR, right for RTL) edge of the character at segmentStartOffset. + float segmentStart = lineStartPos + horizontalBounds[ + 2 * (segmentStartOffset - lineStartOffset) + (isRtl ? 1 : 0)]; + if ((!isRtl && segmentStart > area.right) || (isRtl && segmentStart < area.left)) { + // The entire area is to the left (for LTR) of the text segment. So the area does + // not contain any text segments within this run. + return -1; + } + // End (right for LTR, left for RTL) edge of the character at (segmentStartOffset - 1). + float segmentEnd = lineStartPos + horizontalBounds[ + 2 * (segmentEndOffset - lineStartOffset - 1) + (isRtl ? 0 : 1)]; + segmentBounds.left = isRtl ? segmentEnd : segmentStart; + segmentBounds.right = isRtl ? segmentStart : segmentEnd; + if (inclusionStrategy.isSegmentInside(segmentBounds, area)) { + return segmentStartOffset; + } + // Try the next text segment. + segmentStartOffset = segmentFinder.nextStartBoundary(segmentStartOffset); + if (segmentStartOffset == SegmentFinder.DONE + || segmentStartOffset >= lineStartOffset + runEndOffset) { + // No more text segments within this run. + return -1; + } + segmentEndOffset = segmentFinder.nextEndBoundary(segmentStartOffset); + // If the segment extends past the end of this run, only consider the piece of the + // segment within this run. + segmentEndOffset = Math.min(segmentEndOffset, lineStartOffset + runEndOffset); + } + } + + /** + * Finds the end character offset of the last text segment within a directional run inside the + * specified rectangle area. * - * @param left left bound of the horizontal interval - * @param right right bound of the horizontal interval + * @param area area inside which text segments will be found + * @param lineTop top of the line containing this run + * @param lineBottom bottom (not including line spacing) of the line containing this run * @param lineStartOffset start character offset of the line containing this run * @param lineStartPos start position of the line containing this run * @param horizontalBounds array containing the signed horizontal bounds of the characters in @@ -2093,28 +2133,32 @@ public abstract class Layout { * @param runLeft left bound of the run * @param runRight right bound of the run * @param isRtl whether the run is right-to-left - * @param segmentIterator iterator for determining the ranges of text to be considered as a text - * segment - * @return the end character offset of the last text segment inside the horizontal interval - */ - private static int getEndOffsetForHorizontalIntervalWithinRun( - float left, float right, + * @param segmentFinder SegmentFinder for determining the ranges of text to be considered as a + * text segment + * @param inclusionStrategy strategy for determining whether a text segment is inside the + * specified area + * @return the end character offset of the last text segment inside the area + */ + private static int getEndOffsetForAreaWithinRun( + @NonNull RectF area, + int lineTop, int lineBottom, @IntRange(from = 0) int lineStartOffset, @IntRange(from = 0) int lineStartPos, @NonNull float[] horizontalBounds, @IntRange(from = 0) int runStartOffset, @IntRange(from = 0) int runEndOffset, float runLeft, float runRight, boolean isRtl, - @NonNull SegmentIterator segmentIterator) { - if (runRight < left || runLeft > right) { - // The run does not overlap the interval. + @NonNull SegmentFinder segmentFinder, + @NonNull TextInclusionStrategy inclusionStrategy) { + if (runRight < area.left || runLeft > area.right) { + // The run does not overlap the area. return -1; } - // Find the last character in the run whose bounds overlap with the interval. + // Find the last character in the run whose bounds overlap with the area. // firstCharOffset is an offset index within the line. int lastCharOffset; - if ((!isRtl && right >= runRight) || (isRtl && left <= runLeft)) { + if ((!isRtl && area.right >= runRight) || (isRtl && area.left <= runLeft)) { lastCharOffset = runEndOffset - 1; } else { int low = runStartOffset; @@ -2124,67 +2168,70 @@ public abstract class Layout { guess = (high + low) / 2; // Left edge of the character at guess float pos = lineStartPos + horizontalBounds[2 * guess]; - if ((!isRtl && pos > right) || (isRtl && pos < left)) { + if ((!isRtl && pos > area.right) || (isRtl && pos < area.left)) { high = guess; } else { low = guess; } } - // The interval bound is between the left edge of the character at low and the left edge - // of the character at high. For LTR text, this is within the character at low. For RTL + // The area edge is between the left edge of the character at low and the left edge of + // the character at high. For LTR text, this is within the character at low. For RTL // text, this is within the character at high. lastCharOffset = isRtl ? high : low; } - // Find the last text segment containing this character (or, if no text segment - // contains this character, the first text segment before this character). All - // following text segments in this run are to the right (for LTR) of the interval. - segmentIterator.setRunLimits( - lineStartOffset + runStartOffset, lineStartOffset + runEndOffset); + // Find the last text segment containing this character (or, if no text segment contains + // this character, the first text segment before this character). All following text + // segments in this run are to the right (for LTR) of the area. // + 1 to allow segmentStartOffset = lineStartOffset + lastCharOffset int segmentStartOffset = - segmentIterator.previousStartBoundaryOrRunStart( - lineStartOffset + lastCharOffset + 1); - if (segmentStartOffset == SegmentIterator.DONE) { + segmentFinder.previousStartBoundary(lineStartOffset + lastCharOffset + 1); + if (segmentStartOffset == SegmentFinder.DONE) { // There are no text segments containing or before lastCharOffset, so no text segments - // in this run overlap the interval. + // in this run overlap the area. return -1; } - int segmentEndOffset = segmentIterator.nextEndBoundaryOrRunEnd(segmentStartOffset); - float segmentCenter = lineStartPos - + (horizontalBounds[2 * (segmentStartOffset - lineStartOffset)] - + horizontalBounds[2 * (segmentEndOffset - lineStartOffset) - 1]) / 2; - if ((!isRtl && segmentCenter < left) || (isRtl && segmentCenter > right)) { - // The entire interval is to the right (for LTR) of the text segment's center. So the - // interval does not contain any text segments within this run. + int segmentEndOffset = segmentFinder.nextEndBoundary(segmentStartOffset); + if (segmentEndOffset <= lineStartOffset + runStartOffset) { + // The text segment is before the start of this run, so no text segments in this run + // overlap the area. return -1; } - if ((!isRtl && segmentCenter <= right) || (isRtl && segmentCenter >= left)) { - // The center is within the interval, so return the end offset of this text segment. - return segmentEndOffset; - } + // If the segment extends outside of this run, only consider the piece of the segment within + // this run. + segmentStartOffset = Math.max(segmentStartOffset, lineStartOffset + runStartOffset); + segmentEndOffset = Math.min(segmentEndOffset, lineStartOffset + runEndOffset); - // If first text segment's center is not within the interval, try the next text segment. - segmentEndOffset = segmentIterator.previousEndBoundaryWithinRunLimits(segmentEndOffset); - if (segmentEndOffset == SegmentIterator.DONE - || segmentEndOffset == lineStartOffset + runStartOffset) { - // No more text segments within this run. - return -1; - } - segmentStartOffset = segmentIterator.previousStartBoundaryOrRunStart(segmentEndOffset); - segmentCenter = lineStartPos - + (horizontalBounds[2 * (segmentStartOffset - lineStartOffset)] - + horizontalBounds[2 * (segmentEndOffset - lineStartOffset) - 1]) / 2; - // We already know that segmentCenter <= right (for LTR) since the following word - // contains the right point. - if ((!isRtl && segmentCenter >= left) || (isRtl && segmentCenter <= right)) { - return segmentEndOffset; + RectF segmentBounds = new RectF(0, lineTop, 0, lineBottom); + while (true) { + // End (right for LTR, left for RTL) edge of the character at (segmentStartOffset - 1). + float segmentEnd = lineStartPos + horizontalBounds[ + 2 * (segmentEndOffset - lineStartOffset - 1) + (isRtl ? 0 : 1)]; + if ((!isRtl && segmentEnd < area.left) || (isRtl && segmentEnd > area.right)) { + // The entire area is to the right (for LTR) of the text segment. So the + // area does not contain any text segments within this run. + return -1; + } + // Start (left for LTR, right for RTL) edge of the character at segmentStartOffset. + float segmentStart = lineStartPos + horizontalBounds[ + 2 * (segmentStartOffset - lineStartOffset) + (isRtl ? 1 : 0)]; + segmentBounds.left = isRtl ? segmentEnd : segmentStart; + segmentBounds.right = isRtl ? segmentStart : segmentEnd; + if (inclusionStrategy.isSegmentInside(segmentBounds, area)) { + return segmentEndOffset; + } + // Try the previous text segment. + segmentEndOffset = segmentFinder.previousEndBoundary(segmentEndOffset); + if (segmentEndOffset == SegmentFinder.DONE + || segmentEndOffset <= lineStartOffset + runStartOffset) { + // No more text segments within this run. + return -1; + } + segmentStartOffset = segmentFinder.previousStartBoundary(segmentEndOffset); + // If the segment extends past the start of this run, only consider the piece of the + // segment within this run. + segmentStartOffset = Math.max(segmentStartOffset, lineStartOffset + runStartOffset); } - - // If the second text segment is also not within the interval, then this means that the - // interval is between the centers of the first and second text segments, so it does not - // contain any text segments on this line. - return -1; } /** @@ -3158,4 +3205,22 @@ public abstract class Layout { @TextSelectionLayout int textSelectionLayout); } + /** + * Strategy for determining whether a text segment is inside a rectangle area. + * + * @see #getRangeForRect(RectF, SegmentFinder, TextInclusionStrategy) + */ + @FunctionalInterface + public interface TextInclusionStrategy { + /** + * Returns true if this {@link TextInclusionStrategy} considers the segment with bounds + * {@code segmentBounds} to be inside {@code area}. + * + * <p>The segment is a range of text which does not cross line boundaries or directional run + * boundaries. The horizontal bounds of the segment are the start bound of the first + * character to the end bound of the last character. The vertical bounds match the line + * bounds ({@code getLineTop(line)} and {@code getLineBottom(line, false)}). + */ + boolean isSegmentInside(@NonNull RectF segmentBounds, @NonNull RectF area); + } } diff --git a/core/java/android/text/SegmentIterator.java b/core/java/android/text/SegmentFinder.java index 00522e542eec..d34365dbef22 100644 --- a/core/java/android/text/SegmentIterator.java +++ b/core/java/android/text/SegmentFinder.java @@ -17,26 +17,24 @@ package android.text; import android.annotation.IntRange; +import android.graphics.RectF; /** * Finds text segment boundaries within text. Subclasses can implement different types of text - * segments. Grapheme clusters and words are examples of possible text segments. + * segments. Grapheme clusters and words are examples of possible text segments. These are + * implemented by {@link GraphemeClusterSegmentFinder} and {@link WordSegmentFinder}. * - * <p>Granular units may not overlap, so every character belongs to at most one text segment. A + * <p>Text segments may not overlap, so every character belongs to at most one text segment. A * character may belong to no text segments. * - * <p>For example, a word level text segment iterator may subdivide the text "Hello, World!" into - * four text segments: "Hello", ",", "World", "!". The space character does not belong to any text - * segments. + * <p>For example, WordSegmentFinder subdivides the text "Hello, World!" into four text segments: + * "Hello", ",", "World", "!". The space character does not belong to any text segments. * - * @hide + * @see Layout#getRangeForRect(RectF, SegmentFinder, Layout.TextInclusionStrategy) */ -public abstract class SegmentIterator { +public abstract class SegmentFinder { public static final int DONE = -1; - private int mRunStartOffset; - private int mRunEndOffset; - /** * Returns the character offset of the previous text segment start boundary before the specified * character offset, or {@code DONE} if there are none. @@ -60,52 +58,4 @@ public abstract class SegmentIterator { * character offset, or {@code DONE} if there are none. */ public abstract int nextEndBoundary(@IntRange(from = 0) int offset); - - /** - * Sets the start and end of a run which can be used to constrain the scope of the iterator's - * search. - * - * @hide - */ - void setRunLimits( - @IntRange(from = 0) int runStartOffset, @IntRange(from = 0) int runEndOffset) { - mRunStartOffset = runStartOffset; - mRunEndOffset = runEndOffset; - } - - /** @hide */ - int previousStartBoundaryOrRunStart(@IntRange(from = 0) int offset) { - int start = previousStartBoundary(offset); - if (start == DONE) { - return DONE; - } - return Math.max(start, mRunStartOffset); - } - - /** @hide */ - int previousEndBoundaryWithinRunLimits(@IntRange(from = 0) int offset) { - int end = previousEndBoundary(offset); - if (end <= mRunStartOffset) { - return DONE; - } - return end; - } - - /** @hide */ - int nextStartBoundaryWithinRunLimits(@IntRange(from = 0) int offset) { - int start = nextStartBoundary(offset); - if (start >= mRunEndOffset) { - return DONE; - } - return start; - } - - /** @hide */ - int nextEndBoundaryOrRunEnd(@IntRange(from = 0) int offset) { - int end = nextEndBoundary(offset); - if (end == DONE) { - return DONE; - } - return Math.min(end, mRunEndOffset); - } } diff --git a/core/java/android/text/WordSegmentIterator.java b/core/java/android/text/WordSegmentFinder.java index 11153192b286..bf31e4cb1706 100644 --- a/core/java/android/text/WordSegmentIterator.java +++ b/core/java/android/text/WordSegmentFinder.java @@ -18,21 +18,36 @@ package android.text; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.icu.text.BreakIterator; import android.text.method.WordIterator; +import java.util.Locale; + /** - * Implementation of {@code SegmentIterator} using words as the text segment. Word boundaries are - * found using {@code WordIterator}. Whitespace characters are excluded, so they are not included in + * Implementation of {@link SegmentFinder} using words as the text segment. Word boundaries are + * found using {@link WordIterator}. Whitespace characters are excluded, so they are not included in * any text segments. * - * @hide + * <p>For example, the text "Hello, World!" would be subdivided into four text segments: "Hello", + * ",", "World", "!". The space character does not belong to any text segments. + * + * @see <a href="https://unicode.org/reports/tr29/#Word_Boundaries">Unicode Text Segmentation - Word + * Boundaries</a> */ -public class WordSegmentIterator extends SegmentIterator { +public class WordSegmentFinder extends SegmentFinder { private final CharSequence mText; private final WordIterator mWordIterator; - public WordSegmentIterator(@NonNull CharSequence text, @NonNull WordIterator wordIterator) { + public WordSegmentFinder( + @NonNull CharSequence text, @SuppressLint("UseIcu") @NonNull Locale locale) { + mText = text; + mWordIterator = new WordIterator(locale); + mWordIterator.setCharSequence(text, 0, text.length()); + } + + /** @hide */ + public WordSegmentFinder(@NonNull CharSequence text, @NonNull WordIterator wordIterator) { mText = text; mWordIterator = wordIterator; } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 5b760a41aabd..c1fe6866fb4d 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -549,7 +549,10 @@ public final class InputMethodManager { */ @Deprecated @GuardedBy("mH") - @UnsupportedAppUsage + @UnsupportedAppUsage(trackingBug = 236937383, + maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, + publicAlternatives = "Apps should not change behavior based on the currently connected" + + " IME. If absolutely needed, use {@link InputMethodInfo#getId()} instead.") String mCurId; /** @@ -560,7 +563,9 @@ public final class InputMethodManager { @Deprecated @GuardedBy("mH") @Nullable - @UnsupportedAppUsage + @UnsupportedAppUsage(trackingBug = 236937383, + maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, + publicAlternatives = "Use methods on {@link InputMethodManager} instead.") IInputMethodSession mCurMethod; /** @@ -718,14 +723,8 @@ public final class InputMethodManager { ImeTracing.getInstance().triggerClientDump( "InputMethodManager.DelegateImpl#startInput", InputMethodManager.this, null /* icProto */); - synchronized (mH) { - mCurrentEditorInfo = null; - mCompletions = null; - mServedConnecting = true; - } - return startInputInner(startInputReason, - focusedView != null ? focusedView.getWindowToken() : null, startInputFlags, - softInputMode, windowFlags); + return startInputOnWindowFocusGainInternal(startInputReason, focusedView, + startInputFlags, softInputMode, windowFlags); } /** @@ -800,7 +799,7 @@ public final class InputMethodManager { // should be done in conjunction with telling the system service // about the window gaining focus, to help make the transition // smooth. - if (startInput(StartInputReason.WINDOW_FOCUS_GAIN, + if (startInputOnWindowFocusGainInternal(StartInputReason.WINDOW_FOCUS_GAIN, focusedView, startInputFlags, softInputMode, windowFlags)) { return; } @@ -924,6 +923,19 @@ public final class InputMethodManager { } } + private boolean startInputOnWindowFocusGainInternal(@StartInputReason int startInputReason, + View focusedView, @StartInputFlags int startInputFlags, + @SoftInputModeFlags int softInputMode, int windowFlags) { + synchronized (mH) { + mCurrentEditorInfo = null; + mCompletions = null; + mServedConnecting = true; + } + return startInputInner(startInputReason, + focusedView != null ? focusedView.getWindowToken() : null, startInputFlags, + softInputMode, windowFlags); + } + @GuardedBy("mH") private View getServedViewLocked() { return mCurRootView != null ? mCurRootView.getImeFocusController().getServedViewLocked() @@ -1136,7 +1148,7 @@ public final class InputMethodManager { .checkFocus(mRestartOnNextWindowFocus, false)) { final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS : StartInputReason.DEACTIVATED_BY_IMMS; - mDelegate.startInput(reason, null, 0, 0, 0); + startInputOnWindowFocusGainInternal(reason, null, 0, 0, 0); } } } @@ -2357,7 +2369,7 @@ public final class InputMethodManager { // The view is running on a different thread than our own, so // we need to reschedule our work for over there. if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread"); - vh.post(() -> mDelegate.startInput(startInputReason, null, 0, 0, 0)); + vh.post(() -> startInputOnWindowFocusGainInternal(startInputReason, null, 0, 0, 0)); return false; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index d11fa5f01ac1..da786f3a95be 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -99,14 +99,14 @@ import android.text.BoringLayout; import android.text.DynamicLayout; import android.text.Editable; import android.text.GetChars; -import android.text.GraphemeClusterSegmentIterator; +import android.text.GraphemeClusterSegmentFinder; import android.text.GraphicsOperations; import android.text.InputFilter; import android.text.InputType; import android.text.Layout; import android.text.ParcelableSpan; import android.text.PrecomputedText; -import android.text.SegmentIterator; +import android.text.SegmentFinder; import android.text.Selection; import android.text.SpanWatcher; import android.text.Spannable; @@ -120,7 +120,7 @@ import android.text.TextPaint; import android.text.TextUtils; import android.text.TextUtils.TruncateAt; import android.text.TextWatcher; -import android.text.WordSegmentIterator; +import android.text.WordSegmentFinder; import android.text.method.AllCapsTransformationMethod; import android.text.method.ArrowKeyMovementMethod; import android.text.method.DateKeyListener; @@ -9515,16 +9515,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Nullable private int[] getRangeForRect(@NonNull RectF area, int granularity) { - SegmentIterator segmentIterator; + SegmentFinder segmentFinder; if (granularity == HandwritingGesture.GRANULARITY_WORD) { WordIterator wordIterator = getWordIterator(); wordIterator.setCharSequence(mText, 0, mText.length()); - segmentIterator = new WordSegmentIterator(mText, wordIterator); + segmentFinder = new WordSegmentFinder(mText, wordIterator); } else { - segmentIterator = new GraphemeClusterSegmentIterator(mText, mTextPaint); + segmentFinder = new GraphemeClusterSegmentFinder(mText, mTextPaint); } - return mLayout.getRangeForRect(area, segmentIterator); + return mLayout.getRangeForRect( + area, segmentFinder, Layout.INCLUSION_STRATEGY_CONTAINS_CENTER); } private Pattern getWhitespacePattern() { diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 3732ea5abaa5..787b594af6bb 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -84,6 +84,11 @@ interface IBatteryStats { @RequiresNoPermission long computeChargeTimeRemaining(); + @EnforcePermission("BATTERY_STATS") + long computeBatteryScreenOffRealtimeMs(); + @EnforcePermission("BATTERY_STATS") + long getScreenOffDischargeMah(); + @EnforcePermission("UPDATE_DEVICE_STATS") void noteEvent(int code, String name, int uid); diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 9f15469f4046..f4c3928ac72f 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -84,6 +84,7 @@ interface IInputMethodManager { void showInputMethodPickerFromSystem(in IInputMethodClient client, int auxiliarySubtypeMode, int displayId); + @EnforcePermission("TEST_INPUT_METHOD") @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + "android.Manifest.permission.TEST_INPUT_METHOD)") boolean isInputMethodPickerShownForTest(); diff --git a/core/jni/android_media_AudioVolumeGroups.cpp b/core/jni/android_media_AudioVolumeGroups.cpp index 7098451901c4..1252e89935e5 100644 --- a/core/jni/android_media_AudioVolumeGroups.cpp +++ b/core/jni/android_media_AudioVolumeGroups.cpp @@ -94,6 +94,11 @@ static jint convertAudioVolumeGroupsFromNative( for (size_t j = 0; j < static_cast<size_t>(numAttributes); j++) { auto attributes = group.getAudioAttributes()[j]; + // Native & Java audio attributes default initializers are not aligned for the source. + // Given the volume group class concerns only playback, this field must be equal to the + // default java initializer. + attributes.source = AUDIO_SOURCE_INVALID; + jStatus = JNIAudioAttributeHelper::nativeToJava(env, &jAudioAttribute, attributes); if (jStatus != AUDIO_JAVA_SUCCESS) { goto exit; diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp index 1c61c7be4414..ffacd9c8a95b 100644 --- a/core/jni/android_os_Trace.cpp +++ b/core/jni/android_os_Trace.cpp @@ -22,6 +22,8 @@ #include <array> +static constexpr const char* kNullReplacement = "(null)"; + namespace android { inline static void sanitizeString(char* str) { @@ -36,6 +38,11 @@ inline static void sanitizeString(char* str) { template<typename F> inline static void withString(JNIEnv* env, jstring jstr, F callback) { + if (CC_UNLIKELY(jstr == nullptr)) { + callback(kNullReplacement); + return; + } + // We need to handle the worst case of 1 character -> 4 bytes // So make a buffer of size 4097 and let it hold a string with a maximum length // of 1024. The extra last byte for the null terminator. @@ -52,14 +59,14 @@ inline static void withString(JNIEnv* env, jstring jstr, F callback) { static void android_os_Trace_nativeTraceCounter(JNIEnv* env, jclass, jlong tag, jstring nameStr, jlong value) { - withString(env, nameStr, [tag, value](char* str) { + withString(env, nameStr, [tag, value](const char* str) { atrace_int64(tag, str, value); }); } static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass, jlong tag, jstring nameStr) { - withString(env, nameStr, [tag](char* str) { + withString(env, nameStr, [tag](const char* str) { atrace_begin(tag, str); }); } @@ -70,22 +77,22 @@ static void android_os_Trace_nativeTraceEnd(JNIEnv*, jclass, jlong tag) { static void android_os_Trace_nativeAsyncTraceBegin(JNIEnv* env, jclass, jlong tag, jstring nameStr, jint cookie) { - withString(env, nameStr, [tag, cookie](char* str) { + withString(env, nameStr, [tag, cookie](const char* str) { atrace_async_begin(tag, str, cookie); }); } static void android_os_Trace_nativeAsyncTraceEnd(JNIEnv* env, jclass, jlong tag, jstring nameStr, jint cookie) { - withString(env, nameStr, [tag, cookie](char* str) { + withString(env, nameStr, [tag, cookie](const char* str) { atrace_async_end(tag, str, cookie); }); } static void android_os_Trace_nativeAsyncTraceForTrackBegin(JNIEnv* env, jclass, jlong tag, jstring trackStr, jstring nameStr, jint cookie) { - withString(env, trackStr, [env, tag, nameStr, cookie](char* track) { - withString(env, nameStr, [tag, track, cookie](char* name) { + withString(env, trackStr, [env, tag, nameStr, cookie](const char* track) { + withString(env, nameStr, [tag, track, cookie](const char* name) { atrace_async_for_track_begin(tag, track, name, cookie); }); }); @@ -93,7 +100,7 @@ static void android_os_Trace_nativeAsyncTraceForTrackBegin(JNIEnv* env, jclass, static void android_os_Trace_nativeAsyncTraceForTrackEnd(JNIEnv* env, jclass, jlong tag, jstring trackStr, jint cookie) { - withString(env, trackStr, [tag, cookie](char* track) { + withString(env, trackStr, [tag, cookie](const char* track) { atrace_async_for_track_end(tag, track, cookie); }); } @@ -108,15 +115,15 @@ static void android_os_Trace_nativeSetTracingEnabled(JNIEnv*, jclass, jboolean e static void android_os_Trace_nativeInstant(JNIEnv* env, jclass, jlong tag, jstring nameStr) { - withString(env, nameStr, [tag](char* str) { + withString(env, nameStr, [tag](const char* str) { atrace_instant(tag, str); }); } static void android_os_Trace_nativeInstantForTrack(JNIEnv* env, jclass, jlong tag, jstring trackStr, jstring nameStr) { - withString(env, trackStr, [env, tag, nameStr](char* track) { - withString(env, nameStr, [tag, track](char* name) { + withString(env, trackStr, [env, tag, nameStr](const char* track) { + withString(env, nameStr, [tag, track](const char* name) { atrace_instant_for_track(tag, track, name); }); }); diff --git a/core/tests/coretests/src/android/os/TraceTest.java b/core/tests/coretests/src/android/os/TraceTest.java index 5cad549d39bf..d07187c2c9d8 100644 --- a/core/tests/coretests/src/android/os/TraceTest.java +++ b/core/tests/coretests/src/android/os/TraceTest.java @@ -33,7 +33,21 @@ public class TraceTest extends AndroidTestCase private int eMethodCalls = 0; private int fMethodCalls = 0; private int gMethodCalls = 0; - + + public void testNullStrings() { + Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER, null, 42); + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, null); + + Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, null, 42); + Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, null, 42); + + Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, null, null, 42); + Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, null, 42); + + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, null); + Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, null, null); + } + @SmallTest public void testNativeTracingFromJava() { diff --git a/core/tests/coretests/src/android/text/LayoutGetRangeForRectTest.java b/core/tests/coretests/src/android/text/LayoutGetRangeForRectTest.java deleted file mode 100644 index 787a4055b49d..000000000000 --- a/core/tests/coretests/src/android/text/LayoutGetRangeForRectTest.java +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.text; - -import static com.google.common.truth.Truth.assertThat; - -import android.graphics.RectF; -import android.graphics.Typeface; -import android.text.method.WordIterator; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class LayoutGetRangeForRectTest { - - private static final int WIDTH = 200; - private static final int HEIGHT = 1000; - private static final String DEFAULT_TEXT = "" - // Line 0 (offset 0 to 18) - // - Word 0 (offset 0 to 4) has bounds [0, 40], center 20 - // - Word 1 (offset 5 to 11) has bounds [50, 110], center 80 - // - Word 2 (offset 12 to 17) has bounds [120, 170], center 145 - + "XXXX XXXXXX XXXXX " - // Line 1 (offset 18 to 36) - // - Word 3 (offset 18 to 23) has bounds [0, 50], center 25 - // - Word 4 (offset 24 to 26, RTL) has bounds [100, 110], center 105 - // - Word 5 (offset 27 to 29, RTL) has bounds [80, 90], center 85 - // - Word 6 start part (offset 30 to 32, RTL) has bounds [60, 70], center 65 - // - Word 6 end part (offset 32 to 35) has bounds [110, 140], center 125 - + "XXXXX \u05D1\u05D1 \u05D1\u05D1 \u05D1\u05D1XXX\n" - // Line 2 (offset 36 to 38) - // - Word 7 start part (offset 36 to 38) has bounds [0, 150], center 75 - // Line 3 (offset 38 to 40) - // - Word 7 middle part (offset 38 to 40) has bounds [0, 150], center 75 - // Line 4 (offset 40 to 46) - // - Word 7 end part (offset 40 to 41) has bounds [0, 100], center 50 - // - Word 8 (offset 42 to 44) has bounds [110, 130], center 120 - + "CLCLC XX \n"; - - private Layout mLayout; - private float[] mLineCenters; - private GraphemeClusterSegmentIterator mGraphemeClusterSegmentIterator; - private WordSegmentIterator mWordSegmentIterator; - - @Before - public void setup() { - // The test font includes the following characters: - // U+0020 ( ): 10em - // U+002E (.): 10em - // U+0049 (I): 1em - // U+0056 (V): 5em - // U+0058 (X): 10em - // U+004C (L): 50em - // U+0043 (C): 100em - // U+005F (_): 0em - // U+05D0 : 1em // HEBREW LETTER ALEF - // U+05D1 : 5em // HEBREW LETTER BET - // U+FFFD (invalid surrogate will be replaced to this): 7em - // U+10331 (\uD800\uDF31): 10em - // Undefined : 0.5em - TextPaint textPaint = new TextPaint(); - textPaint.setTypeface( - Typeface.createFromAsset( - InstrumentationRegistry.getInstrumentation().getTargetContext().getAssets(), - "fonts/StaticLayoutLineBreakingTestFont.ttf")); - // Make 1 em equal to 1 pixel. - textPaint.setTextSize(1.0f); - - mLayout = StaticLayout.Builder.obtain( - DEFAULT_TEXT, 0, DEFAULT_TEXT.length(), textPaint, WIDTH).build(); - - mLineCenters = new float[mLayout.getLineCount()]; - for (int i = 0; i < mLayout.getLineCount(); ++i) { - mLineCenters[i] = (mLayout.getLineTop(i) - + mLayout.getLineBottom(i, /* includeLineSpacing= */ false)) / 2f; - } - - mGraphemeClusterSegmentIterator = - new GraphemeClusterSegmentIterator(DEFAULT_TEXT, textPaint); - WordIterator wordIterator = new WordIterator(); - wordIterator.setCharSequence(DEFAULT_TEXT, 0, DEFAULT_TEXT.length()); - mWordSegmentIterator = new WordSegmentIterator(DEFAULT_TEXT, wordIterator); - } - - @Test - public void getRangeForRect_character() { - // Character 1 on line 0 has center 15. - // Character 2 on line 0 has center 25. - RectF area = new RectF(14f, mLineCenters[0] - 1f, 26f, mLineCenters[0] + 1f); - - int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator); - - assertThat(range).asList().containsExactly(1, 3).inOrder(); - } - - @Test - public void getRangeForRect_character_partialCharacterButNotCenter() { - // Character 0 on line 0 has center 5. - // Character 1 on line 0 has center 15. - // Character 2 on line 0 has center 25. - // Character 3 on line 0 has center 35. - RectF area = new RectF(6f, mLineCenters[0] - 1f, 34f, mLineCenters[0] + 1f); - int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator); - - // Area partially overlaps characters 0 and 3 but does not contain their centers. - assertThat(range).asList().containsExactly(1, 3).inOrder(); - } - - @Test - public void getRangeForRect_character_rtl() { - // Character 25 on line 1 has center 102.5. - // Character 26 on line 1 has center 95. - RectF area = new RectF(94f, mLineCenters[1] - 1f, 103f, mLineCenters[1] + 1f); - int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator); - - assertThat(range).asList().containsExactly(25, 27).inOrder(); - } - - @Test - public void getRangeForRect_character_ltrAndRtl() { - // Character 22 on line 1 has center 45. - // The end of the RTL run (offset 24 to 32) on line 1 is at 60. - RectF area = new RectF(44f, mLineCenters[1] - 1f, 93f, mLineCenters[1] + 1f); - int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator); - - assertThat(range).asList().containsExactly(22, 32).inOrder(); - } - - @Test - public void getRangeForRect_character_rtlAndLtr() { - // The start of the RTL run (offset 24 to 32) on line 1 is at 110. - // Character 33 on line 1 has center 125. - RectF area = new RectF(93f, mLineCenters[1] - 1f, 131f, mLineCenters[1] + 1f); - int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator); - - assertThat(range).asList().containsExactly(24, 34).inOrder(); - } - - @Test - public void getRangeForRect_character_betweenCharacters_shouldFallback() { - // Character 1 on line 0 has center 15. - // Character 2 on line 0 has center 25. - RectF area = new RectF(16f, mLineCenters[0] - 1f, 24f, mLineCenters[0] + 1f); - int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator); - - assertThat(range).isNull(); - } - - @Test - public void getRangeForRect_character_betweenLines_shouldFallback() { - // Area top is below the center of line 0. - // Area bottom is above the center of line 1. - RectF area = new RectF(0f, mLineCenters[0] + 1f, WIDTH, mLineCenters[1] - 1f); - int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator); - - // Area partially covers two lines but does not contain the center of any characters. - assertThat(range).isNull(); - } - - @Test - public void getRangeForRect_character_multiLine() { - // Character 9 on line 0 has center 95. - // Character 42 on line 4 has center 115. - RectF area = new RectF(93f, 0, 118f, HEIGHT); - int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator); - - assertThat(range).asList().containsExactly(9, 43).inOrder(); - } - - @Test - public void getRangeForRect_character_multiLine_betweenCharactersOnSomeLines() { - // Character 6 on line 0 has center 65. - // Character 7 on line 0 has center 75. - // Character 30 on line 1 has center 67.5. - // Character 36 on line 2 has center 50. - // Character 37 on line 2 has center 125. - // Character 38 on line 3 has center 50. - // Character 39 on line 3 has center 125. - // Character 40 on line 4 has center 50. - // Character 41 on line 4 has center 105. - RectF area = new RectF(66f, 0, 69f, HEIGHT); - int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator); - - // Area crosses all lines but does not contain the center of any characters on lines 0, 2, - // 3, or 4. So the only included character is character 30 on line 1. - assertThat(range).asList().containsExactly(30, 31).inOrder(); - } - - @Test - public void getRangeForRect_character_multiLine_betweenCharactersOnAllLines_shouldFallback() { - // Character 6 on line 0 has center 65. - // Character 7 on line 0 has center 75. - // Character 30 on line 1 has center 67.5. - // Character 31 on line 1 has center 62.5. - // Character 36 on line 2 has center 50. - // Character 37 on line 2 has center 125. - // Character 38 on line 3 has center 50. - // Character 39 on line 3 has center 125. - // Character 40 on line 4 has center 50. - // Character 41 on line 4 has center 105. - RectF area = new RectF(66f, 0, 67f, HEIGHT); - int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator); - - // Area crosses all lines but does not contain the center of any characters. - assertThat(range).isNull(); - } - - @Test - public void getRangeForRect_character_all() { - // Entire area, should include all text. - RectF area = new RectF(0f, 0f, WIDTH, HEIGHT); - int[] range = mLayout.getRangeForRect(area, mGraphemeClusterSegmentIterator); - - assertThat(range).asList().containsExactly(0, DEFAULT_TEXT.length()).inOrder(); - } - - @Test - public void getRangeForRect_word() { - // Word 1 (offset 5 to 11) on line 0 has center 80. - RectF area = new RectF(79f, mLineCenters[0] - 1f, 81f, mLineCenters[0] + 1f); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - assertThat(range).asList().containsExactly(5, 11).inOrder(); - } - - @Test - public void getRangeForRect_word_partialWordButNotCenter() { - // Word 0 (offset 0 to 4) on line 0 has center 20. - // Word 1 (offset 5 to 11) on line 0 has center 80. - // Word 2 (offset 12 to 17) on line 0 center 145 - RectF area = new RectF(21f, mLineCenters[0] - 1f, 144f, mLineCenters[0] + 1f); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - // Area partially overlaps words 0 and 2 but does not contain their centers, so only word 1 - // is included. Whitespace between words is not included. - assertThat(range).asList().containsExactly(5, 11).inOrder(); - } - - @Test - public void getRangeForRect_word_rtl() { - // Word 4 (offset 24 to 26, RTL) on line 1 center 105 - RectF area = new RectF(88f, mLineCenters[1] - 1f, 119f, mLineCenters[1] + 1f); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - assertThat(range).asList().containsExactly(24, 26).inOrder(); - } - - @Test - public void getRangeForRect_word_ltrAndRtl() { - // Word 3 (offset 18 to 23) on line 1 has center 25 - // The end of the RTL run (offset 24 to 32) on line 1 is at 60. - RectF area = new RectF(24f, mLineCenters[1] - 1f, 93f, mLineCenters[1] + 1f); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - // Selects all of word 6, not just the first RTL part. - assertThat(range).asList().containsExactly(18, 35).inOrder(); - } - - @Test - public void getRangeForRect_word_rtlAndLtr() { - // The start of the RTL run (offset 24 to 32) on line 1 is at 110. - // End part of word 6 (offset 32 to 35) on line 1 has center 125. - RectF area = new RectF(93f, mLineCenters[1] - 1f, 174f, mLineCenters[1] + 1f); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - assertThat(range).asList().containsExactly(24, 35).inOrder(); - } - - @Test - public void getRangeForRect_word_betweenWords_shouldFallback() { - // Word 1 on line 0 has center 80. - // Word 2 on line 0 has center 145. - RectF area = new RectF(81f, mLineCenters[0] - 1f, 144f, mLineCenters[0] + 1f); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - assertThat(range).isNull(); - } - - @Test - public void getRangeForRect_word_betweenLines_shouldFallback() { - // Area top is below the center of line 0. - // Area bottom is above the center of line 1. - RectF area = new RectF(0f, mLineCenters[0] + 1f, WIDTH, mLineCenters[1] - 1f); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - // Area partially covers two lines but does not contain the center of any words. - assertThat(range).isNull(); - } - - @Test - public void getRangeForRect_word_multiLine() { - // Word 1 (offset 5 to 11) on line 0 has center 80. - // End part of word 7 (offset 40 to 41) on line 4 has center 50. - RectF area = new RectF(42f, 0, 91f, HEIGHT); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - assertThat(range).asList().containsExactly(5, 41).inOrder(); - } - - @Test - public void getRangeForRect_word_multiLine_betweenWordsOnSomeLines() { - // Word 1 on line 0 has center 80. - // Word 2 on line 0 has center 145. - // Word 5 (offset 27 to 29) on line 1 has center 85. - // Word 7 on line 2 has center 50. - // Word 37 on line 2 has center 125. - // Word 38 on line 3 has center 50. - // Word 39 on line 3 has center 125. - // Word 40 on line 4 has center 50. - // Word 41 on line 4 has center 105. - RectF area = new RectF(84f, 0, 86f, HEIGHT); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - // Area crosses all lines but does not contain the center of any words on lines 0, 2, 3, or - // 4. So the only included word is word 5 on line 1. - assertThat(range).asList().containsExactly(27, 29).inOrder(); - } - - @Test - public void getRangeForRect_word_multiLine_betweenCharactersOnAllLines_shouldFallback() { - // Word 1 on line 0 has center 80. - // Word 2 on line 0 has center 145. - // Word 4 on line 1 has center 105. - // Word 5 on line 1 has center 85. - // Word 7 on line 2 has center 50. - // Word 37 on line 2 has center 125. - // Word 38 on line 3 has center 50. - // Word 39 on line 3 has center 125. - // Word 40 on line 4 has center 50. - // Word 41 on line 4 has center 105. - RectF area = new RectF(86f, 0, 89f, HEIGHT); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - // Area crosses all lines but does not contain the center of any words. - assertThat(range).isNull(); - } - - @Test - public void getRangeForRect_word_wordSpansMultipleLines_firstPartInsideArea() { - // Word 5 (offset 27 to 29) on line 1 has center 85. - // First part of word 7 (offset 36 to 38) on line 2 has center 75. - RectF area = new RectF(74f, mLineCenters[1] - 1f, 86f, mLineCenters[2] + 1f); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - // Selects all of word 7, not just the first part on line 2. - assertThat(range).asList().containsExactly(27, 41).inOrder(); - } - - @Test - public void getRangeForRect_word_wordSpansMultipleLines_middlePartInsideArea() { - // Middle part of word 7 (offset 38 to 40) on line 2 has center 75. - RectF area = new RectF(74f, mLineCenters[3] - 1f, 75f, mLineCenters[3] + 1f); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - // Selects all of word 7, not just the middle part on line 3. - assertThat(range).asList().containsExactly(36, 41).inOrder(); - } - - @Test - public void getRangeForRect_word_wordSpansMultipleLines_endPartInsideArea() { - // End part of word 7 (offset 40 to 41) on line 4 has center 50. - // Word 8 (offset 42 to 44) on line 4 has center 120 - RectF area = new RectF(49f, mLineCenters[4] - 1f, 121f, mLineCenters[4] + 1f); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - // Selects all of word 7, not just the middle part on line 3. - assertThat(range).asList().containsExactly(36, 44).inOrder(); - } - - @Test - public void getRangeForRect_word_wordSpansMultipleRuns_firstPartInsideArea() { - // Word 5 (offset 27 to 29) on line 1 has center 85. - // First part of word 6 (offset 30 to 32) on line 1 has center 65. - RectF area = new RectF(64f, mLineCenters[1] - 1f, 86f, mLineCenters[1] + 1f); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - // Selects all of word 6, not just the first RTL part. - assertThat(range).asList().containsExactly(27, 35).inOrder(); - } - - @Test - public void getRangeForRect_word_all() { - // Entire area, should include all text except the last two whitespace characters. - RectF area = new RectF(0f, 0f, WIDTH, HEIGHT); - int[] range = mLayout.getRangeForRect(area, mWordSegmentIterator); - - assertThat(range).asList().containsExactly(0, DEFAULT_TEXT.length() - 2).inOrder(); - } -} diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java index b631999c2c54..4e73bd9d3c82 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java @@ -18,13 +18,19 @@ package android.security.keystore2; import android.annotation.NonNull; import android.security.KeyStoreSecurityLevel; +import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; +import android.system.keystore2.Authorization; import android.system.keystore2.KeyDescriptor; import android.system.keystore2.KeyMetadata; +import java.security.AlgorithmParameters; +import java.security.NoSuchAlgorithmException; import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; +import java.security.spec.InvalidParameterSpecException; /** * {@link ECPublicKey} backed by keystore. @@ -56,11 +62,45 @@ public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey impleme } } + private static String getEcCurveFromKeymaster(int ecCurve) { + switch (ecCurve) { + case android.hardware.security.keymint.EcCurve.P_224: + return "secp224r1"; + case android.hardware.security.keymint.EcCurve.P_256: + return "secp256r1"; + case android.hardware.security.keymint.EcCurve.P_384: + return "secp384r1"; + case android.hardware.security.keymint.EcCurve.P_521: + return "secp521r1"; + } + return ""; + } + + private ECParameterSpec getCurveSpec(String name) + throws NoSuchAlgorithmException, InvalidParameterSpecException { + AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC"); + parameters.init(new ECGenParameterSpec(name)); + return parameters.getParameterSpec(ECParameterSpec.class); + } + @Override public AndroidKeyStorePrivateKey getPrivateKey() { + ECParameterSpec params = mParams; + for (Authorization a : getAuthorizations()) { + try { + if (a.keyParameter.tag == KeymasterDefs.KM_TAG_EC_CURVE) { + params = getCurveSpec(getEcCurveFromKeymaster( + a.keyParameter.value.getEcCurve())); + break; + } + } catch (Exception e) { + throw new RuntimeException("Unable to parse EC curve " + + a.keyParameter.value.getEcCurve()); + } + } return new AndroidKeyStoreECPrivateKey( getUserKeyDescriptor(), getKeyIdDescriptor().nspace, getAuthorizations(), - getSecurityLevel(), mParams); + getSecurityLevel(), params); } @Override diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java index b1338d164055..4caa47f2078b 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java @@ -31,6 +31,8 @@ import java.security.NoSuchAlgorithmException; import java.security.ProviderException; import java.security.PublicKey; import java.security.SecureRandom; +import java.security.interfaces.ECKey; +import java.security.interfaces.XECKey; import java.security.spec.AlgorithmParameterSpec; import java.util.ArrayList; import java.util.List; @@ -132,6 +134,15 @@ public class AndroidKeyStoreKeyAgreementSpi extends KeyAgreementSpi throw new InvalidKeyException("key == null"); } else if (!(key instanceof PublicKey)) { throw new InvalidKeyException("Only public keys supported. Key: " + key); + } else if (!(mKey instanceof ECKey && key instanceof ECKey) + && !(mKey instanceof XECKey && key instanceof XECKey)) { + throw new InvalidKeyException( + "Public and Private key should be of the same type:"); + } else if (mKey instanceof ECKey + && !((ECKey) key).getParams().getCurve() + .equals(((ECKey) mKey).getParams().getCurve())) { + throw new InvalidKeyException( + "Public and Private key parameters should be same."); } else if (!lastPhase) { throw new IllegalStateException( "Only one other party supported. lastPhase must be set to true."); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreXDHPrivateKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreXDHPrivateKey.java index 42589640d2b7..e392c8dcca93 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreXDHPrivateKey.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreXDHPrivateKey.java @@ -22,16 +22,18 @@ import android.system.keystore2.Authorization; import android.system.keystore2.KeyDescriptor; import java.security.PrivateKey; -import java.security.interfaces.EdECKey; +import java.security.interfaces.XECPrivateKey; import java.security.spec.NamedParameterSpec; +import java.util.Optional; /** * X25519 Private Key backed by Keystore. - * instance of {@link PrivateKey} and {@link EdECKey} + * instance of {@link PrivateKey} and {@link XECPrivateKey} * * @hide */ -public class AndroidKeyStoreXDHPrivateKey extends AndroidKeyStorePrivateKey implements EdECKey { +public class AndroidKeyStoreXDHPrivateKey extends AndroidKeyStorePrivateKey + implements XECPrivateKey { public AndroidKeyStoreXDHPrivateKey( @NonNull KeyDescriptor descriptor, long keyId, @NonNull Authorization[] authorizations, @@ -44,4 +46,12 @@ public class AndroidKeyStoreXDHPrivateKey extends AndroidKeyStorePrivateKey impl public NamedParameterSpec getParams() { return NamedParameterSpec.X25519; } + + @Override + public Optional<byte[]> getScalar() { + /* An empty Optional if the scalar cannot be extracted (e.g. if the provider is a hardware + * token and the private key is not allowed to leave the crypto boundary). + */ + return Optional.empty(); + } } 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 c4accde23d93..3b735fce3fa9 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 @@ -69,7 +69,6 @@ import com.android.wm.shell.floating.FloatingTasksController; import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.fullscreen.FullscreenTaskListener; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; -import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; @@ -192,25 +191,6 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static KidsModeTaskOrganizer provideKidsModeTaskOrganizer( - Context context, - ShellInit shellInit, - ShellCommandHandler shellCommandHandler, - SyncTransactionQueue syncTransactionQueue, - DisplayController displayController, - DisplayInsetsController displayInsetsController, - Optional<UnfoldAnimationController> unfoldAnimationController, - Optional<RecentTasksController> recentTasksOptional, - @ShellMainThread ShellExecutor mainExecutor, - @ShellMainThread Handler mainHandler - ) { - return new KidsModeTaskOrganizer(context, shellInit, shellCommandHandler, - syncTransactionQueue, displayController, displayInsetsController, - unfoldAnimationController, recentTasksOptional, mainExecutor, mainHandler); - } - - @WMSingleton - @Provides static CompatUIController provideCompatUIController(Context context, ShellInit shellInit, ShellController shellController, @@ -782,7 +762,6 @@ public abstract class WMShellBaseModule { DisplayInsetsController displayInsetsController, DragAndDropController dragAndDropController, ShellTaskOrganizer shellTaskOrganizer, - KidsModeTaskOrganizer kidsModeTaskOrganizer, Optional<BubbleController> bubblesOptional, Optional<SplitScreenController> splitScreenOptional, Optional<Pip> pipOptional, 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 37a50b611039..47b665970d28 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 @@ -56,6 +56,7 @@ import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.freeform.FreeformTaskTransitionHandler; import com.android.wm.shell.freeform.FreeformTaskTransitionObserver; import com.android.wm.shell.fullscreen.FullscreenTaskListener; +import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; @@ -620,6 +621,28 @@ public abstract class WMShellModule { } // + // Kids mode + // + @WMSingleton + @Provides + static KidsModeTaskOrganizer provideKidsModeTaskOrganizer( + Context context, + ShellInit shellInit, + ShellCommandHandler shellCommandHandler, + SyncTransactionQueue syncTransactionQueue, + DisplayController displayController, + DisplayInsetsController displayInsetsController, + Optional<UnfoldAnimationController> unfoldAnimationController, + Optional<RecentTasksController> recentTasksOptional, + @ShellMainThread ShellExecutor mainExecutor, + @ShellMainThread Handler mainHandler + ) { + return new KidsModeTaskOrganizer(context, shellInit, shellCommandHandler, + syncTransactionQueue, displayController, displayInsetsController, + unfoldAnimationController, recentTasksOptional, mainExecutor, mainHandler); + } + + // // Misc // @@ -630,6 +653,7 @@ public abstract class WMShellModule { @Provides static Object provideIndependentShellComponentsToCreate( DefaultMixedHandler defaultMixedHandler, + KidsModeTaskOrganizer kidsModeTaskOrganizer, Optional<DesktopModeController> desktopModeController) { return new Object(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl index 4def15db2f52..2624ee536b58 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl @@ -59,10 +59,15 @@ interface IPip { /** * Sets listener to get pinned stack animation callbacks. */ - oneway void setPinnedStackAnimationListener(IPipAnimationListener listener) = 3; + oneway void setPipAnimationListener(IPipAnimationListener listener) = 3; /** * Sets the shelf height and visibility. */ oneway void setShelfHeight(boolean visible, int shelfHeight) = 4; + + /** + * Sets the next pip animation type to be the alpha animation. + */ + oneway void setPipAnimationTypeToAlpha() = 5; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index c06881ae6ad7..72b9dd37ac7d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -51,15 +51,6 @@ public interface Pip { } /** - * Sets both shelf visibility and its height. - * - * @param visible visibility of shelf. - * @param height to specify the height for shelf. - */ - default void setShelfHeight(boolean visible, int height) { - } - - /** * Set the callback when {@link PipTaskOrganizer#isInPip()} state is changed. * * @param callback The callback accepts the result of {@link PipTaskOrganizer#isInPip()} @@ -68,14 +59,6 @@ public interface Pip { default void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {} /** - * Set the pinned stack with {@link PipAnimationController.AnimationType} - * - * @param animationType The pre-defined {@link PipAnimationController.AnimationType} - */ - default void setPinnedStackAnimationType(int animationType) { - } - - /** * Called when showing Pip menu. */ default void showPictureInPictureMenu() {} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index af47666efa5a..3345b1b2d0e8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -23,6 +23,7 @@ import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static com.android.internal.jank.InteractionJankMonitor.CUJ_PIP_TRANSITION; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; +import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN; @@ -1065,13 +1066,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override - public void setShelfHeight(boolean visible, int height) { - mMainExecutor.execute(() -> { - PipController.this.setShelfHeight(visible, height); - }); - } - - @Override public void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) { mMainExecutor.execute(() -> { PipController.this.setOnIsInPipStateChangedListener(callback); @@ -1079,13 +1073,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override - public void setPinnedStackAnimationType(int animationType) { - mMainExecutor.execute(() -> { - PipController.this.setPinnedStackAnimationType(animationType); - }); - } - - @Override public void addPipExclusionBoundsChangeListener(Consumer<Rect> listener) { mMainExecutor.execute(() -> { mPipBoundsState.addPipExclusionBoundsChangeCallback(listener); @@ -1178,8 +1165,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override - public void setPinnedStackAnimationListener(IPipAnimationListener listener) { - executeRemoteCallWithTaskPermission(mController, "setPinnedStackAnimationListener", + public void setPipAnimationListener(IPipAnimationListener listener) { + executeRemoteCallWithTaskPermission(mController, "setPipAnimationListener", (controller) -> { if (listener != null) { mListener.register(listener); @@ -1188,5 +1175,13 @@ public class PipController implements PipTransitionController.PipTransitionCallb } }); } + + @Override + public void setPipAnimationTypeToAlpha() { + executeRemoteCallWithTaskPermission(mController, "setPipAnimationTypeToAlpha", + (controller) -> { + controller.setPinnedStackAnimationType(ANIM_TYPE_ALPHA); + }); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index 85a544b2e8de..4c262249dfa2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -240,12 +240,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: onConfigurationChanged(), state=%s", TAG, stateToName(mState)); - if (isPipShown()) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: > closing Pip.", TAG); - closePip(); - } - loadConfigurations(); mPipNotificationController.onConfigurationChanged(mContext); mTvPipBoundsAlgorithm.onConfigurationChanged(mContext); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt index 2b162aec34ca..c9c894317ff8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt @@ -38,50 +38,44 @@ import org.junit.Test /** * Base test class containing common assertions for [ComponentMatcher.NAV_BAR], - * [ComponentMatcher.TASK_BAR], [ComponentMatcher.STATUS_BAR], and general assertions - * (layers visible in consecutive states, entire screen covered, etc.) + * [ComponentMatcher.TASK_BAR], [ComponentMatcher.STATUS_BAR], and general assertions (layers + * visible in consecutive states, entire screen covered, etc.) */ -abstract class BaseTest @JvmOverloads constructor( +abstract class BaseTest +@JvmOverloads +constructor( protected val testSpec: FlickerTestParameter, protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(), protected val tapl: LauncherInstrumentation = LauncherInstrumentation() ) { init { testSpec.setIsTablet( - WindowManagerStateHelper( - instrumentation, - clearCacheAfterParsing = false - ).currentState.wmState.isTablet + WindowManagerStateHelper(instrumentation, clearCacheAfterParsing = false) + .currentState + .wmState + .isTablet ) } - /** - * Specification of the test transition to execute - */ + /** Specification of the test transition to execute */ abstract val transition: FlickerBuilder.() -> Unit /** - * Entry point for the test runner. It will use this method to initialize and cache - * flicker executions + * Entry point for the test runner. It will use this method to initialize and cache flicker + * executions */ @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { - setup { - testSpec.setIsTablet(wmHelper.currentState.wmState.isTablet) - } + setup { testSpec.setIsTablet(wmHelper.currentState.wmState.isTablet) } transition() } } - /** - * Checks that all parts of the screen are covered during the transition - */ + /** Checks that all parts of the screen are covered during the transition */ open fun entireScreenCovered() = testSpec.entireScreenCovered() - /** - * Checks that the [ComponentMatcher.NAV_BAR] layer is visible during the whole transition - */ + /** Checks that the [ComponentMatcher.NAV_BAR] layer is visible during the whole transition */ @Presubmit @Test open fun navBarLayerIsVisibleAtStartAndEnd() { @@ -111,9 +105,7 @@ abstract class BaseTest @JvmOverloads constructor( testSpec.navBarWindowIsAlwaysVisible() } - /** - * Checks that the [ComponentMatcher.TASK_BAR] layer is visible during the whole transition - */ + /** Checks that the [ComponentMatcher.TASK_BAR] layer is visible during the whole transition */ @Presubmit @Test open fun taskBarLayerIsVisibleAtStartAndEnd() { @@ -142,7 +134,8 @@ abstract class BaseTest @JvmOverloads constructor( testSpec.statusBarLayerIsVisibleAtStartAndEnd() /** - * Checks the position of the [ComponentMatcher.STATUS_BAR] at the start and end of the transition + * Checks the position of the [ComponentMatcher.STATUS_BAR] at the start and end of the + * transition */ @Presubmit @Test @@ -156,26 +149,22 @@ abstract class BaseTest @JvmOverloads constructor( open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() /** - * Checks that all layers that are visible on the trace, are visible for at least 2 - * consecutive entries. + * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive + * entries. */ @Presubmit @Test open fun visibleLayersShownMoreThanOneConsecutiveEntry() { - testSpec.assertLayers { - this.visibleLayersShownMoreThanOneConsecutiveEntry() - } + testSpec.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry() } } /** - * Checks that all windows that are visible on the trace, are visible for at least 2 - * consecutive entries. + * Checks that all windows that are visible on the trace, are visible for at least 2 consecutive + * entries. */ @Presubmit @Test open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { - testSpec.assertWm { - this.visibleWindowsShownMoreThanOneConsecutiveEntry() - } + testSpec.assertWm { this.visibleWindowsShownMoreThanOneConsecutiveEntry() } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index 6d133779ea60..6f1ff99cb0b3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -15,6 +15,7 @@ */ @file:JvmName("CommonAssertions") + package com.android.wm.shell.flicker import android.view.Surface @@ -26,15 +27,11 @@ import com.android.server.wm.traces.common.IComponentMatcher import com.android.server.wm.traces.common.region.Region fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() { - assertLayersEnd { - this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT) - } + assertLayersEnd { this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT) } } fun FlickerTestParameter.appPairsDividerIsInvisibleAtEnd() { - assertLayersEnd { - this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT) - } + assertLayersEnd { this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT) } } fun FlickerTestParameter.appPairsDividerBecomesVisible() { @@ -82,27 +79,19 @@ fun FlickerTestParameter.splitScreenDismissed( } fun FlickerTestParameter.splitScreenDividerIsVisibleAtStart() { - assertLayersStart { - this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) - } + assertLayersStart { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) } } fun FlickerTestParameter.splitScreenDividerIsVisibleAtEnd() { - assertLayersEnd { - this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) - } + assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) } } fun FlickerTestParameter.splitScreenDividerIsInvisibleAtStart() { - assertLayersStart { - this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) - } + assertLayersStart { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) } } fun FlickerTestParameter.splitScreenDividerIsInvisibleAtEnd() { - assertLayersEnd { - this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) - } + assertLayersEnd { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) } } fun FlickerTestParameter.splitScreenDividerBecomesVisible() { @@ -117,40 +106,20 @@ fun FlickerTestParameter.splitScreenDividerBecomesInvisible() { } } -fun FlickerTestParameter.layerBecomesVisible( - component: IComponentMatcher -) { - assertLayers { - this.isInvisible(component) - .then() - .isVisible(component) - } +fun FlickerTestParameter.layerBecomesVisible(component: IComponentMatcher) { + assertLayers { this.isInvisible(component).then().isVisible(component) } } -fun FlickerTestParameter.layerBecomesInvisible( - component: IComponentMatcher -) { - assertLayers { - this.isVisible(component) - .then() - .isInvisible(component) - } +fun FlickerTestParameter.layerBecomesInvisible(component: IComponentMatcher) { + assertLayers { this.isVisible(component).then().isInvisible(component) } } -fun FlickerTestParameter.layerIsVisibleAtEnd( - component: IComponentMatcher -) { - assertLayersEnd { - this.isVisible(component) - } +fun FlickerTestParameter.layerIsVisibleAtEnd(component: IComponentMatcher) { + assertLayersEnd { this.isVisible(component) } } -fun FlickerTestParameter.layerKeepVisible( - component: IComponentMatcher -) { - assertLayers { - this.isVisible(component) - } +fun FlickerTestParameter.layerKeepVisible(component: IComponentMatcher) { + assertLayers { this.isVisible(component) } } fun FlickerTestParameter.splitAppLayerBoundsBecomesVisible( @@ -164,13 +133,15 @@ fun FlickerTestParameter.splitAppLayerBoundsBecomesVisible( .isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component)) .then() .splitAppLayerBoundsSnapToDivider( - component, landscapePosLeft, portraitPosTop, endRotation) + component, + landscapePosLeft, + portraitPosTop, + endRotation + ) } } -fun FlickerTestParameter.splitAppLayerBoundsBecomesVisibleByDrag( - component: IComponentMatcher -) { +fun FlickerTestParameter.splitAppLayerBoundsBecomesVisibleByDrag(component: IComponentMatcher) { assertLayers { this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true) .then() @@ -188,7 +159,11 @@ fun FlickerTestParameter.splitAppLayerBoundsBecomesInvisible( ) { assertLayers { this.splitAppLayerBoundsSnapToDivider( - component, landscapePosLeft, portraitPosTop, endRotation) + component, + landscapePosLeft, + portraitPosTop, + endRotation + ) .then() .isVisible(component, true) .then() @@ -224,15 +199,27 @@ fun FlickerTestParameter.splitAppLayerBoundsChanges( assertLayers { if (landscapePosLeft) { this.splitAppLayerBoundsSnapToDivider( - component, landscapePosLeft, portraitPosTop, endRotation) + component, + landscapePosLeft, + portraitPosTop, + endRotation + ) } else { this.splitAppLayerBoundsSnapToDivider( - component, landscapePosLeft, portraitPosTop, endRotation) + component, + landscapePosLeft, + portraitPosTop, + endRotation + ) .then() .isInvisible(component) .then() .splitAppLayerBoundsSnapToDivider( - component, landscapePosLeft, portraitPosTop, endRotation) + component, + landscapePosLeft, + portraitPosTop, + endRotation + ) } } } @@ -257,45 +244,46 @@ fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider( val displayBounds = WindowUtils.getDisplayBounds(rotation) return invoke { val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region - visibleRegion(component).coversAtMost( - if (displayBounds.width > displayBounds.height) { - if (landscapePosLeft) { - Region.from( - 0, - 0, - (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2, - displayBounds.bounds.bottom) - } else { - Region.from( - (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2, - 0, - displayBounds.bounds.right, - displayBounds.bounds.bottom - ) - } - } else { - if (portraitPosTop) { - Region.from( - 0, - 0, - displayBounds.bounds.right, - (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2) + visibleRegion(component) + .coversAtMost( + if (displayBounds.width > displayBounds.height) { + if (landscapePosLeft) { + Region.from( + 0, + 0, + (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2, + displayBounds.bounds.bottom + ) + } else { + Region.from( + (dividerRegion.bounds.left + dividerRegion.bounds.right) / 2, + 0, + displayBounds.bounds.right, + displayBounds.bounds.bottom + ) + } } else { - Region.from( - 0, - (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2, - displayBounds.bounds.right, - displayBounds.bounds.bottom - ) + if (portraitPosTop) { + Region.from( + 0, + 0, + displayBounds.bounds.right, + (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2 + ) + } else { + Region.from( + 0, + (dividerRegion.bounds.top + dividerRegion.bounds.bottom) / 2, + displayBounds.bounds.right, + displayBounds.bounds.bottom + ) + } } - } - ) + ) } } -fun FlickerTestParameter.appWindowBecomesVisible( - component: IComponentMatcher -) { +fun FlickerTestParameter.appWindowBecomesVisible(component: IComponentMatcher) { assertWm { this.isAppWindowInvisible(component) .then() @@ -307,60 +295,32 @@ fun FlickerTestParameter.appWindowBecomesVisible( } } -fun FlickerTestParameter.appWindowBecomesInvisible( - component: IComponentMatcher -) { - assertWm { - this.isAppWindowVisible(component) - .then() - .isAppWindowInvisible(component) - } +fun FlickerTestParameter.appWindowBecomesInvisible(component: IComponentMatcher) { + assertWm { this.isAppWindowVisible(component).then().isAppWindowInvisible(component) } } -fun FlickerTestParameter.appWindowIsVisibleAtStart( - component: IComponentMatcher -) { - assertWmStart { - this.isAppWindowVisible(component) - } +fun FlickerTestParameter.appWindowIsVisibleAtStart(component: IComponentMatcher) { + assertWmStart { this.isAppWindowVisible(component) } } -fun FlickerTestParameter.appWindowIsVisibleAtEnd( - component: IComponentMatcher -) { - assertWmEnd { - this.isAppWindowVisible(component) - } +fun FlickerTestParameter.appWindowIsVisibleAtEnd(component: IComponentMatcher) { + assertWmEnd { this.isAppWindowVisible(component) } } -fun FlickerTestParameter.appWindowIsInvisibleAtStart( - component: IComponentMatcher -) { - assertWmStart { - this.isAppWindowInvisible(component) - } +fun FlickerTestParameter.appWindowIsInvisibleAtStart(component: IComponentMatcher) { + assertWmStart { this.isAppWindowInvisible(component) } } -fun FlickerTestParameter.appWindowIsInvisibleAtEnd( - component: IComponentMatcher -) { - assertWmEnd { - this.isAppWindowInvisible(component) - } +fun FlickerTestParameter.appWindowIsInvisibleAtEnd(component: IComponentMatcher) { + assertWmEnd { this.isAppWindowInvisible(component) } } -fun FlickerTestParameter.appWindowKeepVisible( - component: IComponentMatcher -) { - assertWm { - this.isAppWindowVisible(component) - } +fun FlickerTestParameter.appWindowKeepVisible(component: IComponentMatcher) { + assertWm { this.isAppWindowVisible(component) } } fun FlickerTestParameter.dockedStackDividerIsVisibleAtEnd() { - assertLayersEnd { - this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) - } + assertLayersEnd { this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) } } fun FlickerTestParameter.dockedStackDividerBecomesVisible() { @@ -380,9 +340,7 @@ fun FlickerTestParameter.dockedStackDividerBecomesInvisible() { } fun FlickerTestParameter.dockedStackDividerNotExistsAtEnd() { - assertLayersEnd { - this.notContains(DOCKED_STACK_DIVIDER_COMPONENT) - } + assertLayersEnd { this.notContains(DOCKED_STACK_DIVIDER_COMPONENT) } } fun FlickerTestParameter.appPairsPrimaryBoundsIsVisibleAtEnd( @@ -391,8 +349,7 @@ fun FlickerTestParameter.appPairsPrimaryBoundsIsVisibleAtEnd( ) { assertLayersEnd { val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region - visibleRegion(primaryComponent) - .overlaps(getPrimaryRegion(dividerRegion, rotation)) + visibleRegion(primaryComponent).overlaps(getPrimaryRegion(dividerRegion, rotation)) } } @@ -402,8 +359,7 @@ fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisibleAtEnd( ) { assertLayersEnd { val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region - visibleRegion(primaryComponent) - .overlaps(getPrimaryRegion(dividerRegion, rotation)) + visibleRegion(primaryComponent).overlaps(getPrimaryRegion(dividerRegion, rotation)) } } @@ -413,8 +369,7 @@ fun FlickerTestParameter.appPairsSecondaryBoundsIsVisibleAtEnd( ) { assertLayersEnd { val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region - visibleRegion(secondaryComponent) - .overlaps(getSecondaryRegion(dividerRegion, rotation)) + visibleRegion(secondaryComponent).overlaps(getSecondaryRegion(dividerRegion, rotation)) } } @@ -424,8 +379,7 @@ fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd( ) { assertLayersEnd { val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region - visibleRegion(secondaryComponent) - .overlaps(getSecondaryRegion(dividerRegion, rotation)) + visibleRegion(secondaryComponent).overlaps(getSecondaryRegion(dividerRegion, rotation)) } } @@ -433,12 +387,16 @@ fun getPrimaryRegion(dividerRegion: Region, rotation: Int): Region { val displayBounds = WindowUtils.getDisplayBounds(rotation) return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { Region.from( - 0, 0, displayBounds.bounds.right, + 0, + 0, + displayBounds.bounds.right, dividerRegion.bounds.top + WindowUtils.dockedStackDividerInset ) } else { Region.from( - 0, 0, dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset, + 0, + 0, + dividerRegion.bounds.left + WindowUtils.dockedStackDividerInset, displayBounds.bounds.bottom ) } @@ -448,13 +406,17 @@ fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region { val displayBounds = WindowUtils.getDisplayBounds(rotation) return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { Region.from( - 0, dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.bounds.right, displayBounds.bounds.bottom + 0, + dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset, + displayBounds.bounds.right, + displayBounds.bounds.bottom ) } else { Region.from( - dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset, 0, - displayBounds.bounds.right, displayBounds.bounds.bottom + dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset, + 0, + displayBounds.bounds.right, + displayBounds.bounds.bottom ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt index 53dd8b04afeb..79978929cf3e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt @@ -15,6 +15,7 @@ */ @file:JvmName("CommonConstants") + package com.android.wm.shell.flicker import com.android.server.wm.traces.common.ComponentNameMatcher @@ -26,5 +27,8 @@ val SPLIT_SCREEN_DIVIDER_COMPONENT = ComponentNameMatcher("", "StageCoordinatorS val SPLIT_DECOR_MANAGER = ComponentNameMatcher("", "SplitDecorManager#") enum class Direction { - UP, DOWN, LEFT, RIGHT + UP, + DOWN, + LEFT, + RIGHT } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt index c045325f19c3..87b94ff8668b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt @@ -33,17 +33,23 @@ object MultiWindowUtils { } fun getDevEnableNonResizableMultiWindow(context: Context): Int = - Settings.Global.getInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW) + Settings.Global.getInt( + context.contentResolver, + Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW + ) fun setDevEnableNonResizableMultiWindow(context: Context, configValue: Int) = - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, configValue) + Settings.Global.putInt( + context.contentResolver, + Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, + configValue + ) fun setSupportsNonResizableMultiWindow(instrumentation: Instrumentation, configValue: Int) = executeShellCommand( instrumentation, - createConfigSupportsNonResizableMultiWindowCommand(configValue)) + createConfigSupportsNonResizableMultiWindowCommand(configValue) + ) fun resetMultiWindowConfig(instrumentation: Instrumentation) = executeShellCommand(instrumentation, resetMultiWindowConfigCommand) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt index 51f7a18f60dd..e0ef92457f58 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NotificationListener.kt @@ -51,7 +51,7 @@ class NotificationListener : NotificationListenerService() { private const val CMD_NOTIFICATION_ALLOW_LISTENER = "cmd notification allow_listener %s" private const val CMD_NOTIFICATION_DISALLOW_LISTENER = - "cmd notification disallow_listener %s" + "cmd notification disallow_listener %s" private const val COMPONENT_NAME = "com.android.wm.shell.flicker/.NotificationListener" private var instance: NotificationListener? = null @@ -79,25 +79,23 @@ class NotificationListener : NotificationListenerService() { ): StatusBarNotification? { instance?.run { return notifications.values.firstOrNull(predicate) - } ?: throw IllegalStateException("NotificationListenerService is not connected") + } + ?: throw IllegalStateException("NotificationListenerService is not connected") } fun waitForNotificationToAppear( predicate: (StatusBarNotification) -> Boolean ): StatusBarNotification? { instance?.let { - return waitForResult(extractor = { - it.notifications.values.firstOrNull(predicate) - }).second - } ?: throw IllegalStateException("NotificationListenerService is not connected") + return waitForResult(extractor = { it.notifications.values.firstOrNull(predicate) }) + .second + } + ?: throw IllegalStateException("NotificationListenerService is not connected") } - fun waitForNotificationToDisappear( - predicate: (StatusBarNotification) -> Boolean - ): Boolean { - return instance?.let { - wait { it.notifications.values.none(predicate) } - } ?: throw IllegalStateException("NotificationListenerService is not connected") + fun waitForNotificationToDisappear(predicate: (StatusBarNotification) -> Boolean): Boolean { + return instance?.let { wait { it.notifications.values.none(predicate) } } + ?: throw IllegalStateException("NotificationListenerService is not connected") } } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt index 4d87ec9e872f..556cb06f3ca1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt @@ -15,6 +15,7 @@ */ @file:JvmName("WaitUtils") + package com.android.wm.shell.flicker import android.os.SystemClock diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt index cbe085be8952..0fc2004ce7f9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt @@ -34,21 +34,21 @@ import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE import com.android.wm.shell.flicker.BaseTest import org.junit.runners.Parameterized -/** - * Base configurations for Bubble flicker tests - */ -abstract class BaseBubbleScreen( - testSpec: FlickerTestParameter -) : BaseTest(testSpec) { +/** Base configurations for Bubble flicker tests */ +abstract class BaseBubbleScreen(testSpec: FlickerTestParameter) : BaseTest(testSpec) { protected val context: Context = instrumentation.context protected val testApp = LaunchBubbleHelper(instrumentation) - private val notifyManager = INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)) + private val notifyManager = + INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE) + ) - private val uid = context.packageManager.getApplicationInfo( - testApp.`package`, PackageManager.ApplicationInfoFlags.of(0)).uid + private val uid = + context.packageManager + .getApplicationInfo(testApp.`package`, PackageManager.ApplicationInfoFlags.of(0)) + .uid @JvmOverloads protected open fun buildTransition( @@ -56,16 +56,22 @@ abstract class BaseBubbleScreen( ): FlickerBuilder.() -> Unit { return { setup { - notifyManager.setBubblesAllowed(testApp.`package`, - uid, NotificationManager.BUBBLE_PREFERENCE_ALL) + notifyManager.setBubblesAllowed( + testApp.`package`, + uid, + NotificationManager.BUBBLE_PREFERENCE_ALL + ) testApp.launchViaIntent(wmHelper) waitAndGetAddBubbleBtn() waitAndGetCancelAllBtn() } teardown { - notifyManager.setBubblesAllowed(testApp.`package`, - uid, NotificationManager.BUBBLE_PREFERENCE_NONE) + notifyManager.setBubblesAllowed( + testApp.`package`, + uid, + NotificationManager.BUBBLE_PREFERENCE_NONE + ) testApp.exit() } @@ -73,17 +79,17 @@ abstract class BaseBubbleScreen( } } - protected fun Flicker.waitAndGetAddBubbleBtn(): UiObject2? = device.wait(Until.findObject( - By.text("Add Bubble")), FIND_OBJECT_TIMEOUT) - protected fun Flicker.waitAndGetCancelAllBtn(): UiObject2? = device.wait(Until.findObject( - By.text("Cancel All Bubble")), FIND_OBJECT_TIMEOUT) + protected fun Flicker.waitAndGetAddBubbleBtn(): UiObject2? = + device.wait(Until.findObject(By.text("Add Bubble")), FIND_OBJECT_TIMEOUT) + protected fun Flicker.waitAndGetCancelAllBtn(): UiObject2? = + device.wait(Until.findObject(By.text("Cancel All Bubble")), FIND_OBJECT_TIMEOUT) companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) } const val FIND_OBJECT_TIMEOUT = 2000L diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt index 4a914a3eaf5c..ab721173763e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt @@ -38,7 +38,9 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:DismissBubbleScreen` * * Actions: + * ``` * Dismiss a bubble notification + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -58,11 +60,11 @@ open class DismissBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScree transitions { wm.run { wm.defaultDisplay.getMetrics(displaySize) } val dist = Point((displaySize.widthPixels / 2), displaySize.heightPixels) - val showBubble = device.wait( - Until.findObject( - By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME) - ), FIND_OBJECT_TIMEOUT - ) + val showBubble = + device.wait( + Until.findObject(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), + FIND_OBJECT_TIMEOUT + ) showBubble?.run { drag(dist, 1000) } ?: error("Show bubble not found") } } @@ -70,22 +72,18 @@ open class DismissBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScree @Presubmit @Test open fun testAppIsAlwaysVisible() { - testSpec.assertLayers { - this.isVisible(testApp) - } + testSpec.assertLayers { this.isVisible(testApp) } } /** {@inheritDoc} */ @Postsubmit @Test - override fun taskBarLayerIsVisibleAtStartAndEnd() = - super.taskBarLayerIsVisibleAtStartAndEnd() + override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ @Postsubmit @Test - override fun taskBarWindowIsAlwaysVisible() = - super.taskBarWindowIsAlwaysVisible() + override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() /** {@inheritDoc} */ @Postsubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt index d48175e5f49a..226eab89920f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt @@ -33,9 +33,11 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:ExpandBubbleScreen` * * Actions: + * ``` * Launch an app and enable app's bubble notification * Send a bubble notification * The activity for the bubble is launched + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -50,11 +52,11 @@ open class ExpandBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen addBubbleBtn?.click() ?: error("Add Bubble not found") } transitions { - val showBubble = device.wait( - Until.findObject( - By.res("com.android.systemui", "bubble_view") - ), FIND_OBJECT_TIMEOUT - ) + val showBubble = + device.wait( + Until.findObject(By.res("com.android.systemui", "bubble_view")), + FIND_OBJECT_TIMEOUT + ) showBubble?.run { showBubble.click() } ?: error("Bubble notify not found") } } @@ -62,8 +64,6 @@ open class ExpandBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen @Presubmit @Test open fun testAppIsAlwaysVisible() { - testSpec.assertLayers { - this.isVisible(testApp) - } + testSpec.assertLayers { this.isVisible(testApp) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt index 83dcb5a3fb8b..275f6c802293 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt @@ -36,7 +36,9 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:LaunchBubbleFromLockScreen` * * Actions: + * ``` * Launch an bubble from notification on lock screen + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -50,36 +52,32 @@ class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScr val addBubbleBtn = waitAndGetAddBubbleBtn() addBubbleBtn?.click() ?: error("Bubble widget not found") device.sleep() - wmHelper.StateSyncBuilder() - .withoutTopVisibleAppWindows() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify() device.wakeUp() } transitions { // Swipe & wait for the notification shade to expand so all can be seen - val wm = context.getSystemService(WindowManager::class.java) - ?: error("Unable to obtain WM service") + val wm = + context.getSystemService(WindowManager::class.java) + ?: error("Unable to obtain WM service") val metricInsets = wm.currentWindowMetrics.windowInsets - val insets = metricInsets.getInsetsIgnoringVisibility( - WindowInsets.Type.statusBars() - or WindowInsets.Type.displayCutout() - ) + val insets = + metricInsets.getInsetsIgnoringVisibility( + WindowInsets.Type.statusBars() or WindowInsets.Type.displayCutout() + ) device.swipe(100, insets.top + 100, 100, device.displayHeight / 2, 4) device.waitForIdle(2000) instrumentation.uiAutomation.syncInputTransactions() - val notification = device.wait( - Until.findObject( - By.text("BubbleChat") - ), FIND_OBJECT_TIMEOUT - ) + val notification = + device.wait(Until.findObject(By.text("BubbleChat")), FIND_OBJECT_TIMEOUT) notification?.click() ?: error("Notification not found") instrumentation.uiAutomation.syncInputTransactions() - val showBubble = device.wait( - Until.findObject( - By.res("com.android.systemui", "bubble_view") - ), FIND_OBJECT_TIMEOUT - ) + val showBubble = + device.wait( + Until.findObject(By.res("com.android.systemui", "bubble_view")), + FIND_OBJECT_TIMEOUT + ) showBubble?.click() ?: error("Bubble notify not found") instrumentation.uiAutomation.syncInputTransactions() val cancelAllBtn = waitAndGetCancelAllBtn() @@ -90,9 +88,7 @@ class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScr @FlakyTest(bugId = 242088970) @Test fun testAppIsVisibleAtEnd() { - testSpec.assertLayersEnd { - this.isVisible(testApp) - } + testSpec.assertLayersEnd { this.isVisible(testApp) } } /** {@inheritDoc} */ @@ -104,32 +100,27 @@ class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScr /** {@inheritDoc} */ @FlakyTest(bugId = 206753786) @Test - override fun navBarLayerIsVisibleAtStartAndEnd() = - super.navBarLayerIsVisibleAtStartAndEnd() + override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ @FlakyTest(bugId = 206753786) @Test - override fun navBarLayerPositionAtStartAndEnd() = - super.navBarLayerPositionAtStartAndEnd() + override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ @FlakyTest(bugId = 206753786) @Test - override fun navBarWindowIsAlwaysVisible() = - super.navBarWindowIsAlwaysVisible() + override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** {@inheritDoc} */ @FlakyTest(bugId = 242088970) @Test - override fun taskBarLayerIsVisibleAtStartAndEnd() = - super.taskBarLayerIsVisibleAtStartAndEnd() + override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ @Postsubmit @Test - override fun taskBarWindowIsAlwaysVisible() = - super.taskBarWindowIsAlwaysVisible() + override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() /** {@inheritDoc} */ @FlakyTest(bugId = 242088970) @@ -140,14 +131,12 @@ class LaunchBubbleFromLockScreen(testSpec: FlickerTestParameter) : BaseBubbleScr /** {@inheritDoc} */ @FlakyTest(bugId = 242088970) @Test - override fun statusBarLayerPositionAtStartAndEnd() = - super.statusBarLayerPositionAtStartAndEnd() + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ @FlakyTest(bugId = 242088970) @Test - override fun statusBarWindowIsAlwaysVisible() = - super.statusBarWindowIsAlwaysVisible() + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ @FlakyTest(bugId = 242088970) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt index 81204f49d03b..b86599913649 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt @@ -32,8 +32,10 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:LaunchBubbleScreen` * * Actions: + * ``` * Launch an app and enable app's bubble notification * Send a bubble notification + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -48,17 +50,15 @@ open class LaunchBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen addBubbleBtn?.click() ?: error("Bubble widget not found") device.wait( - Until.findObjects( - By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME) - ), FIND_OBJECT_TIMEOUT - ) ?: error("No bubbles found") + Until.findObjects(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), + FIND_OBJECT_TIMEOUT + ) + ?: error("No bubbles found") } } @Test open fun testAppIsAlwaysVisible() { - testSpec.assertLayers { - this.isVisible(testApp) - } + testSpec.assertLayers { this.isVisible(testApp) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt index 364090510138..bf4d7d4e7734 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt @@ -38,7 +38,9 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:MultiBubblesScreen` * * Actions: + * ``` * Switch in different bubble notifications + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -59,20 +61,22 @@ open class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen addBubbleBtn.click() SystemClock.sleep(1000) } - val showBubble = device.wait( - Until.findObject( - By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME) - ), FIND_OBJECT_TIMEOUT - ) ?: error("Show bubble not found") + val showBubble = + device.wait( + Until.findObject(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), + FIND_OBJECT_TIMEOUT + ) + ?: error("Show bubble not found") showBubble.click() SystemClock.sleep(1000) } transitions { - val bubbles: List<UiObject2> = device.wait( - Until.findObjects( - By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME) - ), FIND_OBJECT_TIMEOUT - ) ?: error("No bubbles found") + val bubbles: List<UiObject2> = + device.wait( + Until.findObjects(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), + FIND_OBJECT_TIMEOUT + ) + ?: error("No bubbles found") for (entry in bubbles) { entry.click() SystemClock.sleep(1000) @@ -83,8 +87,6 @@ open class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen @Presubmit @Test open fun testAppIsAlwaysVisible() { - testSpec.assertLayers { - this.isVisible(testApp) - } + testSpec.assertLayers { this.isVisible(testApp) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreenShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreenShellTransit.kt index 839fef904244..57adeab7b070 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreenShellTransit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreenShellTransit.kt @@ -30,9 +30,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FlakyTest(bugId = 217777115) -class MultiBubblesScreenShellTransit( - testSpec: FlickerTestParameter -) : MultiBubblesScreen(testSpec) { +class MultiBubblesScreenShellTransit(testSpec: FlickerTestParameter) : + MultiBubblesScreen(testSpec) { @Before override fun before() { Assume.assumeTrue(isShellTransitionsEnabled) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt index 1be93527e0e5..1fc0375e9feb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt @@ -40,16 +40,19 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:AutoEnterPipOnGoToHomeTest` * * Actions: + * ``` * Launch an app in full screen * Select "Auto-enter PiP" radio button * Press Home button or swipe up to go Home and put [pipApp] in pip mode - * + * ``` * Notes: + * ``` * 1. All assertions are inherited from [EnterPipTest] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -57,9 +60,7 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @FlakyTest(bugId = 238367575) class AutoEnterPipOnGoToHomeTest(testSpec: FlickerTestParameter) : EnterPipTest(testSpec) { - /** - * Defines the transition used to run the test - */ + /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit get() = { setup { @@ -76,9 +77,7 @@ class AutoEnterPipOnGoToHomeTest(testSpec: FlickerTestParameter) : EnterPipTest( RemoveAllTasksButHomeRule.removeAllTasksButHome() pipApp.exit(wmHelper) } - transitions { - tapl.goHome() - } + transitions { tapl.goHome() } } @FlakyTest @@ -92,9 +91,7 @@ class AutoEnterPipOnGoToHomeTest(testSpec: FlickerTestParameter) : EnterPipTest( } } - /** - * Checks that [pipApp] window is animated towards default position in right bottom corner - */ + /** Checks that [pipApp] window is animated towards default position in right bottom corner */ @Presubmit @Test fun pipLayerMovesTowardsRightBottomCorner() { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt index 75543cfed1ac..c8aa6d20c91b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt @@ -16,14 +16,12 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule @@ -40,25 +38,26 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:EnterPipOnUserLeaveHintTest` * * Actions: + * ``` * Launch an app in full screen * Select "Via code behind" radio button * Press Home button or swipe up to go Home and put [pipApp] in pip mode - * + * ``` * Notes: + * ``` * 1. All assertions are inherited from [EnterPipTest] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class EnterPipOnUserLeaveHintTest(testSpec: FlickerTestParameter) : EnterPipTest(testSpec) { - /** - * Defines the transition used to run the test - */ + /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit get() = { setup { @@ -73,20 +72,17 @@ class EnterPipOnUserLeaveHintTest(testSpec: FlickerTestParameter) : EnterPipTest RemoveAllTasksButHomeRule.removeAllTasksButHome() pipApp.exit(wmHelper) } - transitions { - tapl.goHome() - } + transitions { tapl.goHome() } } @Presubmit @Test override fun pipAppLayerAlwaysVisible() { - if (!testSpec.isGesturalNavigation) super.pipAppLayerAlwaysVisible() else { + if (!testSpec.isGesturalNavigation) super.pipAppLayerAlwaysVisible() + else { // pip layer in gesture nav will disappear during transition testSpec.assertLayers { - this.isVisible(pipApp) - .then().isInvisible(pipApp) - .then().isVisible(pipApp) + this.isVisible(pipApp).then().isInvisible(pipApp).then().isVisible(pipApp) } } } @@ -110,28 +106,17 @@ class EnterPipOnUserLeaveHintTest(testSpec: FlickerTestParameter) : EnterPipTest @Presubmit @Test override fun entireScreenCovered() { - Assume.assumeFalse(isShellTransitionsEnabled) - super.entireScreenCovered() - } - - @FlakyTest(bugId = 227313015) - @Test - fun entireScreenCovered_ShellTransit() { - Assume.assumeTrue(isShellTransitionsEnabled) super.entireScreenCovered() } @Presubmit @Test override fun pipLayerRemainInsideVisibleBounds() { - if (!testSpec.isGesturalNavigation) super.pipLayerRemainInsideVisibleBounds() else { + if (!testSpec.isGesturalNavigation) super.pipLayerRemainInsideVisibleBounds() + else { // pip layer in gesture nav will disappear during transition - testSpec.assertLayersStart { - this.visibleRegion(pipApp).coversAtMost(displayBounds) - } - testSpec.assertLayersEnd { - this.visibleRegion(pipApp).coversAtMost(displayBounds) - } + testSpec.assertLayersStart { this.visibleRegion(pipApp).coversAtMost(displayBounds) } + testSpec.assertLayersEnd { this.visibleRegion(pipApp).coversAtMost(displayBounds) } } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index 239d337be991..2b629e73e750 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice @@ -40,16 +39,19 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:EnterPipTest` * * Actions: + * ``` * Launch an app in full screen * Press an "enter pip" button to put [pipApp] in pip mode - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited from [PipTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -57,7 +59,7 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { - /** {@inheritDoc} */ + /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { setup { @@ -70,31 +72,23 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec RemoveAllTasksButHomeRule.removeAllTasksButHome() pipApp.exit(wmHelper) } - transitions { - pipApp.clickEnterPipButton(wmHelper) - } + transitions { pipApp.clickEnterPipButton(wmHelper) } } - /** - * Checks [pipApp] window remains visible throughout the animation - */ + /** Checks [pipApp] window remains visible throughout the animation */ @Presubmit @Test open fun pipAppWindowAlwaysVisible() { - testSpec.assertWm { - this.isAppWindowVisible(pipApp) - } + testSpec.assertWm { this.isAppWindowVisible(pipApp) } } /** * Checks [pipApp] layer remains visible throughout the animation */ - @FlakyTest(bugId = 239807171) + @Presubmit @Test open fun pipAppLayerAlwaysVisible() { - testSpec.assertLayers { - this.isVisible(pipApp) - } + testSpec.assertLayers { this.isVisible(pipApp) } } /** @@ -104,26 +98,20 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec @Presubmit @Test fun pipWindowRemainInsideVisibleBounds() { - testSpec.assertWmVisibleRegion(pipApp) { - coversAtMost(displayBounds) - } + testSpec.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) } } /** * Checks that the pip app layer remains inside the display bounds throughout the whole * animation */ - @FlakyTest(bugId = 239807171) + @Presubmit @Test open fun pipLayerRemainInsideVisibleBounds() { - testSpec.assertLayersVisibleRegion(pipApp) { - coversAtMost(displayBounds) - } + testSpec.assertLayersVisibleRegion(pipApp) { coversAtMost(displayBounds) } } - /** - * Checks that the visible region of [pipApp] always reduces during the animation - */ + /** Checks that the visible region of [pipApp] always reduces during the animation */ @Presubmit @Test open fun pipLayerReduces() { @@ -135,9 +123,7 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec } } - /** - * Checks that [pipApp] window becomes pinned - */ + /** Checks that [pipApp] window becomes pinned */ @Presubmit @Test fun pipWindowBecomesPinned() { @@ -148,9 +134,7 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec } } - /** - * Checks [ComponentMatcher.LAUNCHER] layer remains visible throughout the animation - */ + /** Checks [ComponentMatcher.LAUNCHER] layer remains visible throughout the animation */ @Presubmit @Test fun launcherLayerBecomesVisible() { @@ -162,31 +146,27 @@ open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec } /** - * Checks that the focus changes between the [pipApp] window and the launcher when - * closing the pip window + * Checks that the focus changes between the [pipApp] window and the launcher when closing the + * pip window */ @Presubmit @Test open fun focusChanges() { - testSpec.assertEventLog { - this.focusChanges(pipApp.`package`, "NexusLauncherActivity") - } + testSpec.assertEventLog { this.focusChanges(pipApp.`package`, "NexusLauncherActivity") } } companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests( - supportedRotations = listOf(Surface.ROTATION_0) - ) + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt index 11bc1f7ea406..104b409731dd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt @@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.pip import android.app.Activity import android.platform.test.annotations.FlakyTest -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice @@ -225,7 +224,7 @@ class EnterPipToOtherOrientationTest( } /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt index 628599160c65..3d8525b3d4ad 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt @@ -21,9 +21,7 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper import org.junit.Test -/** - * Base class for pip expand tests - */ +/** Base class for pip expand tests */ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) { protected val testApp = FixedOrientationAppHelper(instrumentation) @@ -34,9 +32,7 @@ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTrans @Presubmit @Test open fun pipAppWindowRemainInsideVisibleBounds() { - testSpec.assertWmVisibleRegion(pipApp) { - coversAtMost(displayBounds) - } + testSpec.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) } } /** @@ -46,9 +42,7 @@ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTrans @Presubmit @Test open fun pipAppLayerRemainInsideVisibleBounds() { - testSpec.assertLayersVisibleRegion(pipApp) { - coversAtMost(displayBounds) - } + testSpec.assertLayersVisibleRegion(pipApp) { coversAtMost(displayBounds) } } /** @@ -78,44 +72,34 @@ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTrans @Test open fun showBothAppLayersThenHidePip() { testSpec.assertLayers { - isVisible(testApp) - .isVisible(pipApp) - .then() - .isInvisible(testApp) - .isVisible(pipApp) + isVisible(testApp).isVisible(pipApp).then().isInvisible(testApp).isVisible(pipApp) } } /** - * Checks that the visible region of [testApp] plus the visible region of [pipApp] - * cover the full display area at the start of the transition + * Checks that the visible region of [testApp] plus the visible region of [pipApp] cover the + * full display area at the start of the transition */ @Presubmit @Test open fun testPlusPipAppsCoverFullScreenAtStart() { testSpec.assertLayersStart { val pipRegion = visibleRegion(pipApp).region - visibleRegion(testApp) - .plus(pipRegion) - .coversExactly(displayBounds) + visibleRegion(testApp).plus(pipRegion).coversExactly(displayBounds) } } /** - * Checks that the visible region oft [pipApp] covers the full display area at the end of - * the transition + * Checks that the visible region oft [pipApp] covers the full display area at the end of the + * transition */ @Presubmit @Test open fun pipAppCoversFullScreenAtEnd() { - testSpec.assertLayersEnd { - visibleRegion(pipApp).coversExactly(displayBounds) - } + testSpec.assertLayersEnd { visibleRegion(pipApp).coversExactly(displayBounds) } } - /** - * Checks that the visible region of [pipApp] always expands during the animation - */ + /** Checks that the visible region of [pipApp] always expands during the animation */ @Presubmit @Test open fun pipLayerExpands() { @@ -127,8 +111,6 @@ abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTrans } } - /** {@inheritDoc} */ - @Presubmit - @Test - override fun entireScreenCovered() = super.entireScreenCovered() + /** {@inheritDoc} */ + @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt index 39be89d2592b..3b8bb90988f9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt @@ -25,24 +25,18 @@ import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.traces.common.ComponentNameMatcher.Companion.LAUNCHER import org.junit.Test -/** - * Base class for exiting pip (closing pip window) without returning to the app - */ +/** Base class for exiting pip (closing pip window) without returning to the app */ abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) { override val transition: FlickerBuilder.() -> Unit get() = buildTransition { - setup { - this.setRotation(testSpec.startRotation) - } - teardown { - this.setRotation(Surface.ROTATION_0) - } + setup { this.setRotation(testSpec.startRotation) } + teardown { this.setRotation(Surface.ROTATION_0) } } /** - * Checks that [pipApp] window is pinned and visible at the start and then becomes - * unpinned and invisible at the same moment, and remains unpinned and invisible - * until the end of the transition + * Checks that [pipApp] window is pinned and visible at the start and then becomes unpinned and + * invisible at the same moment, and remains unpinned and invisible until the end of the + * transition */ @Presubmit @Test @@ -53,30 +47,24 @@ abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition // and isAppWindowInvisible in the same assertion block. testSpec.assertWm { this.invoke("hasPipWindow") { - it.isPinned(pipApp) - .isAppWindowVisible(pipApp) - .isAppWindowOnTop(pipApp) - }.then().invoke("!hasPipWindow") { - it.isNotPinned(pipApp) - .isAppWindowNotOnTop(pipApp) - } + it.isPinned(pipApp).isAppWindowVisible(pipApp).isAppWindowOnTop(pipApp) + } + .then() + .invoke("!hasPipWindow") { it.isNotPinned(pipApp).isAppWindowNotOnTop(pipApp) } } testSpec.assertWmEnd { isAppWindowInvisible(pipApp) } } else { testSpec.assertWm { - this.invoke("hasPipWindow") { - it.isPinned(pipApp).isAppWindowVisible(pipApp) - }.then().invoke("!hasPipWindow") { - it.isNotPinned(pipApp).isAppWindowInvisible(pipApp) - } + this.invoke("hasPipWindow") { it.isPinned(pipApp).isAppWindowVisible(pipApp) } + .then() + .invoke("!hasPipWindow") { it.isNotPinned(pipApp).isAppWindowInvisible(pipApp) } } } } /** - * Checks that [pipApp] and [LAUNCHER] layers are visible at the start - * of the transition. Then [pipApp] layer becomes invisible, and remains invisible - * until the end of the transition + * Checks that [pipApp] and [LAUNCHER] layers are visible at the start of the transition. Then + * [pipApp] layer becomes invisible, and remains invisible until the end of the transition */ @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt index 8cb68facab08..5e3194ca4ee2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt @@ -16,7 +16,7 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.FlakyTest +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -76,12 +76,12 @@ class ExitPipViaExpandButtonClickTest( } /** {@inheritDoc} */ - @FlakyTest(bugId = 227313015) + @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ - @FlakyTest(bugId = 197726610) + @Presubmit @Test override fun pipLayerExpands() = super.pipLayerExpands() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt index 7ef332399246..3356d3e2ab2b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt @@ -38,17 +38,20 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:ExitPipViaIntentTest` * * Actions: + * ``` * Launch an app in pip mode [pipApp], * Launch another full screen mode [testApp] * Expand [pipApp] app to full screen via an intent - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited from [PipTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -56,9 +59,7 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransition(testSpec) { - /** - * Defines the transition used to run the test - */ + /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit get() = buildTransition { setup { @@ -69,18 +70,16 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit // This will bring PipApp to fullscreen pipApp.exitPipToFullScreenViaIntent(wmHelper) // Wait until the other app is no longer visible - wmHelper.StateSyncBuilder() - .withWindowSurfaceDisappeared(testApp) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify() } } /** {@inheritDoc} */ - @FlakyTest + @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() - /** {@inheritDoc} */ + /** {@inheritDoc} */ @Presubmit @Test override fun statusBarLayerPositionAtStartAndEnd() { @@ -95,7 +94,7 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit super.statusBarLayerPositionAtStartAndEnd() } - /** {@inheritDoc} */ + /** {@inheritDoc} */ @FlakyTest(bugId = 197726610) @Test override fun pipLayerExpands() { @@ -114,14 +113,14 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - supportedRotations = listOf(Surface.ROTATION_0)) + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt index a33d9144fbbb..d195abb2aaec 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt @@ -35,17 +35,20 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:ExitPipWithDismissButtonTest` * * Actions: + * ``` * Launch an app in pip mode [pipApp], * Click on the pip window * Click on dismiss button and wait window disappear - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [PipTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -56,9 +59,7 @@ class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTran override val transition: FlickerBuilder.() -> Unit get() = { super.transition(this) - transitions { - pipApp.closePipWindow(wmHelper) - } + transitions { pipApp.closePipWindow(wmHelper) } } /** @@ -68,23 +69,21 @@ class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTran @Presubmit @Test fun focusChanges() { - testSpec.assertEventLog { - this.focusChanges("PipMenuView", "NexusLauncherActivity") - } + testSpec.assertEventLog { this.focusChanges("PipMenuView", "NexusLauncherActivity") } } companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt index e5695694dfbc..f7a244717141 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt @@ -36,16 +36,19 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:ExitPipWithSwipeDownTest` * * Actions: + * ``` * Launch an app in pip mode [pipApp], * Swipe the pip window to the bottom-center of the screen and wait it disappear - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [PipTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -60,20 +63,24 @@ class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransiti val pipCenterX = pipRegion.centerX() val pipCenterY = pipRegion.centerY() val displayCenterX = device.displayWidth / 2 - val barComponent = if (testSpec.isTablet) { - ComponentNameMatcher.TASK_BAR - } else { - ComponentNameMatcher.NAV_BAR - } - val barLayerHeight = wmHelper.currentState.layerState - .getLayerWithBuffer(barComponent) - ?.visibleRegion - ?.height ?: error("Couldn't find Nav or Task bar layer") + val barComponent = + if (testSpec.isTablet) { + ComponentNameMatcher.TASK_BAR + } else { + ComponentNameMatcher.NAV_BAR + } + val barLayerHeight = + wmHelper.currentState.layerState + .getLayerWithBuffer(barComponent) + ?.visibleRegion + ?.height + ?: error("Couldn't find Nav or Task bar layer") // The dismiss button doesn't appear at the complete bottom of the screen, val displayY = device.displayHeight - barLayerHeight device.swipe(pipCenterX, pipCenterY, displayCenterX, displayY, 50) // Wait until the other app is no longer visible - wmHelper.StateSyncBuilder() + wmHelper + .StateSyncBuilder() .withPipGone() .withWindowSurfaceDisappeared(pipApp) .withAppTransitionIdle() @@ -81,29 +88,25 @@ class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransiti } } - /** - * Checks that the focus doesn't change between windows during the transition - */ + /** Checks that the focus doesn't change between windows during the transition */ @Presubmit @Test fun focusDoesNotChange() { - testSpec.assertEventLog { - this.focusDoesNotChange() - } + testSpec.assertEventLog { this.focusDoesNotChange() } } companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt index 5f9419694c13..b40106790b6c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt @@ -23,34 +23,23 @@ import com.android.server.wm.flicker.traces.region.RegionSubject import com.android.wm.shell.flicker.Direction import org.junit.Test -/** - * Base class for pip tests with Launcher shelf height change - */ -abstract class MovePipShelfHeightTransition( - testSpec: FlickerTestParameter -) : PipTransition(testSpec) { +/** Base class for pip tests with Launcher shelf height change */ +abstract class MovePipShelfHeightTransition(testSpec: FlickerTestParameter) : + PipTransition(testSpec) { protected val testApp = FixedOrientationAppHelper(instrumentation) - /** - * Checks [pipApp] window remains visible throughout the animation - */ + /** Checks [pipApp] window remains visible throughout the animation */ @Presubmit @Test open fun pipWindowIsAlwaysVisible() { - testSpec.assertWm { - isAppWindowVisible(pipApp) - } + testSpec.assertWm { isAppWindowVisible(pipApp) } } - /** - * Checks [pipApp] layer remains visible throughout the animation - */ + /** Checks [pipApp] layer remains visible throughout the animation */ @Presubmit @Test open fun pipLayerIsAlwaysVisible() { - testSpec.assertLayers { - isVisible(pipApp) - } + testSpec.assertLayers { isVisible(pipApp) } } /** @@ -60,9 +49,7 @@ abstract class MovePipShelfHeightTransition( @Presubmit @Test open fun pipWindowRemainInsideVisibleBounds() { - testSpec.assertWmVisibleRegion(pipApp) { - coversAtMost(displayBounds) - } + testSpec.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) } } /** @@ -72,9 +59,7 @@ abstract class MovePipShelfHeightTransition( @Presubmit @Test open fun pipLayerRemainInsideVisibleBounds() { - testSpec.assertLayersVisibleRegion(pipApp) { - coversAtMost(displayBounds) - } + testSpec.assertLayersVisibleRegion(pipApp) { coversAtMost(displayBounds) } } /** @@ -83,9 +68,8 @@ abstract class MovePipShelfHeightTransition( */ protected fun pipWindowMoves(direction: Direction) { testSpec.assertWm { - val pipWindowFrameList = this.windowStates { - pipApp.windowMatchesAnyOf(it) && it.isVisible - }.map { it.frame } + val pipWindowFrameList = + this.windowStates { pipApp.windowMatchesAnyOf(it) && it.isVisible }.map { it.frame } when (direction) { Direction.UP -> assertRegionMovementUp(pipWindowFrameList) Direction.DOWN -> assertRegionMovementDown(pipWindowFrameList) @@ -100,9 +84,9 @@ abstract class MovePipShelfHeightTransition( */ protected fun pipLayerMoves(direction: Direction) { testSpec.assertLayers { - val pipLayerRegionList = this.layers { - pipApp.layerMatchesAnyOf(it) && it.isVisible - }.map { it.visibleRegion } + val pipLayerRegionList = + this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible } + .map { it.visibleRegion } when (direction) { Direction.UP -> assertRegionMovementUp(pipLayerRegionList) Direction.DOWN -> assertRegionMovementDown(pipLayerRegionList) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt index ca086e850f52..3b64d218a73d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt @@ -36,10 +36,7 @@ import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized -/** - * Test Pip launch. - * To run this test: `atest WMShellFlickerTests:PipKeyboardTest` - */ +/** Test Pip launch. To run this test: `atest WMShellFlickerTests:PipKeyboardTest` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @@ -52,7 +49,7 @@ open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testS assumeFalse(isShellTransitionsEnabled) } - /** {@inheritDoc} */ + /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = buildTransition { setup { @@ -73,9 +70,7 @@ open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testS } } - /** - * Ensure the pip window remains visible throughout any keyboard interactions - */ + /** Ensure the pip window remains visible throughout any keyboard interactions */ @Presubmit @Test open fun pipInVisibleBounds() { @@ -85,15 +80,11 @@ open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testS } } - /** - * Ensure that the pip window does not obscure the keyboard - */ + /** Ensure that the pip window does not obscure the keyboard */ @Presubmit @Test open fun pipIsAboveAppWindow() { - testSpec.assertWmTag(TAG_IME_VISIBLE) { - isAboveWindow(ComponentNameMatcher.IME, pipApp) - } + testSpec.assertWmTag(TAG_IME_VISIBLE) { isAboveWindow(ComponentNameMatcher.IME, pipApp) } } companion object { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt index 3b6d7da1eaa8..2a82c00bebd3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt @@ -42,6 +42,5 @@ class PipKeyboardTestShellTransit(testSpec: FlickerTestParameter) : PipKeyboardT @Presubmit @Test - override fun statusBarLayerPositionAtStartAndEnd() = - super.statusBarLayerPositionAtStartAndEnd() + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index 61cb2b0c0ca8..7de5494a7733 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -42,18 +42,21 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:PipRotationTest` * * Actions: + * ``` * Launch a [pipApp] in pip mode * Launch another app [fixedApp] (appears below pip) * Rotate the screen from [testSpec.startRotation] to [testSpec.endRotation] * (usually, 0->90 and 90->0) - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited from [PipTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -75,43 +78,31 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS testApp.launchViaIntent(wmHelper) setRotation(testSpec.startRotation) } - transitions { - setRotation(testSpec.endRotation) - } + transitions { setRotation(testSpec.endRotation) } } - /** - * Checks the position of the navigation bar at the start and end of the transition - */ + /** Checks the position of the navigation bar at the start and end of the transition */ @FlakyTest(bugId = 240499181) @Test override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() - /** - * Checks that [testApp] layer is within [screenBoundsStart] at the start of the transition - */ + /** Checks that [testApp] layer is within [screenBoundsStart] at the start of the transition */ @Presubmit @Test fun fixedAppLayer_StartingBounds() { - testSpec.assertLayersStart { - visibleRegion(testApp).coversAtMost(screenBoundsStart) - } + testSpec.assertLayersStart { visibleRegion(testApp).coversAtMost(screenBoundsStart) } } - /** - * Checks that [testApp] layer is within [screenBoundsEnd] at the end of the transition - */ + /** Checks that [testApp] layer is within [screenBoundsEnd] at the end of the transition */ @Presubmit @Test fun fixedAppLayer_EndingBounds() { - testSpec.assertLayersEnd { - visibleRegion(testApp).coversAtMost(screenBoundsEnd) - } + testSpec.assertLayersEnd { visibleRegion(testApp).coversAtMost(screenBoundsEnd) } } /** - * Checks that [testApp] plus [pipApp] layers are within [screenBoundsEnd] at the start - * of the transition + * Checks that [testApp] plus [pipApp] layers are within [screenBoundsEnd] at the start of the + * transition */ @Presubmit @Test @@ -122,8 +113,8 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS } /** - * Checks that [testApp] plus [pipApp] layers are within [screenBoundsEnd] at the end - * of the transition + * Checks that [testApp] plus [pipApp] layers are within [screenBoundsEnd] at the end of the + * transition */ @Presubmit @Test @@ -133,60 +124,44 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS } } - /** - * Checks that [pipApp] layer is within [screenBoundsStart] at the start of the transition - */ + /** Checks that [pipApp] layer is within [screenBoundsStart] at the start of the transition */ private fun pipLayerRotates_StartingBounds_internal() { - testSpec.assertLayersStart { - visibleRegion(pipApp).coversAtMost(screenBoundsStart) - } + testSpec.assertLayersStart { visibleRegion(pipApp).coversAtMost(screenBoundsStart) } } - /** - * Checks that [pipApp] layer is within [screenBoundsStart] at the start of the transition - */ + /** Checks that [pipApp] layer is within [screenBoundsStart] at the start of the transition */ @Presubmit @Test fun pipLayerRotates_StartingBounds() { pipLayerRotates_StartingBounds_internal() } - /** - * Checks that [pipApp] layer is within [screenBoundsEnd] at the end of the transition - */ + /** Checks that [pipApp] layer is within [screenBoundsEnd] at the end of the transition */ @Presubmit @Test fun pipLayerRotates_EndingBounds() { - testSpec.assertLayersEnd { - visibleRegion(pipApp).coversAtMost(screenBoundsEnd) - } + testSpec.assertLayersEnd { visibleRegion(pipApp).coversAtMost(screenBoundsEnd) } } /** - * Ensure that the [pipApp] window does not obscure the [testApp] at the start of the - * transition + * Ensure that the [pipApp] window does not obscure the [testApp] at the start of the transition */ @Presubmit @Test fun pipIsAboveFixedAppWindow_Start() { - testSpec.assertWmStart { - isAboveWindow(pipApp, testApp) - } + testSpec.assertWmStart { isAboveWindow(pipApp, testApp) } } /** - * Ensure that the [pipApp] window does not obscure the [testApp] at the end of the - * transition + * Ensure that the [pipApp] window does not obscure the [testApp] at the end of the transition */ @Presubmit @Test fun pipIsAboveFixedAppWindow_End() { - testSpec.assertWmEnd { - isAboveWindow(pipApp, testApp) - } + testSpec.assertWmEnd { isAboveWindow(pipApp, testApp) } } - @FlakyTest(bugId = 240499181) + @Presubmit @Test override fun navBarLayerIsVisibleAtStartAndEnd() { super.navBarLayerIsVisibleAtStartAndEnd() @@ -196,15 +171,16 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigRotationTests( - supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90) - ) + return FlickerTestParameterFactory.getInstance() + .getConfigRotationTests( + supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90) + ) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest_ShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest_ShellTransit.kt index d868e98d0cf8..983cb1c6bafd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest_ShellTransit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest_ShellTransit.kt @@ -35,18 +35,21 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTests:PipRotationTest_ShellTransit` * * Actions: + * ``` * Launch a [pipApp] in pip mode * Launch another app [fixedApp] (appears below pip) * Rotate the screen from [testSpec.startRotation] to [testSpec.endRotation] * (usually, 0->90 and 90->0) - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited from [PipTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -59,9 +62,8 @@ class PipRotationTest_ShellTransit(testSpec: FlickerTestParameter) : PipRotation Assume.assumeTrue(isShellTransitionsEnabled) } - /** {@inheritDoc} */ + /** {@inheritDoc} */ @FlakyTest @Test - override fun navBarLayerPositionAtStartAndEnd() = - super.navBarLayerPositionAtStartAndEnd() + override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index ff505a04290b..dfa25104ccc8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -40,24 +40,21 @@ abstract class PipTransition(testSpec: FlickerTestParameter) : BaseTest(testSpec } fun doAction(broadcastAction: String) { - instrumentation.context - .sendBroadcast(createIntentWithAction(broadcastAction)) + instrumentation.context.sendBroadcast(createIntentWithAction(broadcastAction)) } companion object { // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE - @JvmStatic - val ORIENTATION_LANDSCAPE = 0 + @JvmStatic val ORIENTATION_LANDSCAPE = 0 // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - @JvmStatic - val ORIENTATION_PORTRAIT = 1 + @JvmStatic val ORIENTATION_PORTRAIT = 1 } } /** - * Gets a configuration that handles basic setup and teardown of pip tests and that - * launches the Pip app for test + * Gets a configuration that handles basic setup and teardown of pip tests and that launches the + * Pip app for test * * @param eachRun If the pip app should be launched in each run (otherwise only 1x per test) * @param stringExtras Arguments to pass to the PIP launch intent diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt index cdd768abd5bd..36909dd74245 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt @@ -24,9 +24,7 @@ import androidx.test.uiautomator.Until import com.android.server.wm.flicker.helpers.PipAppHelper import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper -/** - * Helper class for PIP app on AndroidTV - */ +/** Helper class for PIP app on AndroidTV */ open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instrumentation) { private val appSelector = By.pkg(`package`).depth(0) @@ -61,17 +59,12 @@ open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instr uiDevice.closeTvPipWindow() } - /** - * Taps the pip window and dismisses it by clicking on the X button. - */ + /** Taps the pip window and dismisses it by clicking on the X button. */ override fun closePipWindow(wmHelper: WindowManagerStateHelper) { uiDevice.closeTvPipWindow() // Wait for animation to complete. - wmHelper.StateSyncBuilder() - .withPipGone() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withPipGone().withHomeActivityVisible().waitForAndVerify() } fun waitUntilClosed(): Boolean { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt index a16f5f6f1620..2cb18f948f0e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt @@ -24,10 +24,7 @@ import androidx.test.uiautomator.UiDevice import org.junit.Before import org.junit.runners.Parameterized -abstract class PipTestBase( - protected val rotationName: String, - protected val rotation: Int -) { +abstract class PipTestBase(protected val rotationName: String, protected val rotation: Int) { val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() val uiDevice = UiDevice.getInstance(instrumentation) val packageManager: PackageManager = instrumentation.context.packageManager diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt index 31fb16ffbd3e..8a073abf032c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt @@ -25,16 +25,11 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized -/** - * Test Pip Menu on TV. - * To run this test: `atest WMShellFlickerTests:TvPipBasicTest` - */ +/** Test Pip Menu on TV. To run this test: `atest WMShellFlickerTests:TvPipBasicTest` */ @RequiresDevice @RunWith(Parameterized::class) -class TvPipBasicTest( - private val radioButtonId: String, - private val pipWindowRatio: Rational? -) : TvPipTestBase() { +class TvPipBasicTest(private val radioButtonId: String, private val pipWindowRatio: Rational?) : + TvPipTestBase() { @Test fun enterPip_openMenu_pressBack_closePip() { @@ -45,8 +40,8 @@ class TvPipBasicTest( testApp.clickObject(radioButtonId) testApp.clickEnterPipButton(wmHelper) - val actualRatio: Float = testApp.ui?.visibleBounds?.ratio - ?: fail("Application UI not found") + val actualRatio: Float = + testApp.ui?.visibleBounds?.ratio ?: fail("Application UI not found") pipWindowRatio?.let { expectedRatio -> assertEquals("Wrong Pip window ratio", expectedRatio.toFloat(), actualRatio) } @@ -62,7 +57,8 @@ class TvPipBasicTest( // Make sure Pip Window ration remained the same after Pip menu was closed testApp.ui?.visibleBounds?.let { newBounds -> assertEquals("Pip window ratio has changed", actualRatio, newBounds.ratio) - } ?: fail("Application UI not found") + } + ?: fail("Application UI not found") // Close Pip testApp.closePipWindow() @@ -77,10 +73,10 @@ class TvPipBasicTest( fun getParams(): Collection<Array<Any?>> { infix fun Int.to(denominator: Int) = Rational(this, denominator) return listOf( - arrayOf("ratio_default", null), - arrayOf("ratio_square", 1 to 1), - arrayOf("ratio_wide", 2 to 1), - arrayOf("ratio_tall", 1 to 2) + arrayOf("ratio_default", null), + arrayOf("ratio_square", 1 to 1), + arrayOf("ratio_wide", 2 to 1), + arrayOf("ratio_tall", 1 to 2) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt index 68dbbfb89b6c..7403aab7d4c0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt @@ -27,28 +27,26 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test -/** - * Test Pip Menu on TV. - * To run this test: `atest WMShellFlickerTests:TvPipMenuTests` - */ +/** Test Pip Menu on TV. To run this test: `atest WMShellFlickerTests:TvPipMenuTests` */ @RequiresDevice class TvPipMenuTests : TvPipTestBase() { private val systemUiResources = - packageManager.getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME) - private val pipBoundsWhileInMenu: Rect = systemUiResources.run { - val bounds = getString(getIdentifier("pip_menu_bounds", "string", - SYSTEM_UI_PACKAGE_NAME)) - Rect.unflattenFromString(bounds) ?: error("Could not retrieve PiP menu bounds") - } - private val playButtonDescription = systemUiResources.run { - getString(getIdentifier("pip_play", "string", - SYSTEM_UI_PACKAGE_NAME)) - } - private val pauseButtonDescription = systemUiResources.run { - getString(getIdentifier("pip_pause", "string", - SYSTEM_UI_PACKAGE_NAME)) - } + packageManager.getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME) + private val pipBoundsWhileInMenu: Rect = + systemUiResources.run { + val bounds = + getString(getIdentifier("pip_menu_bounds", "string", SYSTEM_UI_PACKAGE_NAME)) + Rect.unflattenFromString(bounds) ?: error("Could not retrieve PiP menu bounds") + } + private val playButtonDescription = + systemUiResources.run { + getString(getIdentifier("pip_play", "string", SYSTEM_UI_PACKAGE_NAME)) + } + private val pauseButtonDescription = + systemUiResources.run { + getString(getIdentifier("pip_pause", "string", SYSTEM_UI_PACKAGE_NAME)) + } @Before fun tvPipMenuTestsTestUp() { @@ -61,20 +59,29 @@ class TvPipMenuTests : TvPipTestBase() { enterPip_openMenu_assertShown() // Make sure the PiP task is positioned where it should be. - val activityBounds: Rect = testApp.ui?.visibleBounds - ?: error("Could not retrieve Pip Activity bounds") - assertTrue("Pip Activity is positioned correctly while Pip menu is shown", - pipBoundsWhileInMenu == activityBounds) + val activityBounds: Rect = + testApp.ui?.visibleBounds ?: error("Could not retrieve Pip Activity bounds") + assertTrue( + "Pip Activity is positioned correctly while Pip menu is shown", + pipBoundsWhileInMenu == activityBounds + ) // Make sure the Pip Menu Actions are positioned correctly. uiDevice.findTvPipMenuControls()?.visibleBounds?.run { - assertTrue("Pip Menu Actions should be positioned below the Activity in Pip", - top >= activityBounds.bottom) - assertTrue("Pip Menu Actions should be positioned central horizontally", - centerX() == uiDevice.displayWidth / 2) - assertTrue("Pip Menu Actions should be fully shown on the screen", - left >= 0 && right <= uiDevice.displayWidth && bottom <= uiDevice.displayHeight) - } ?: error("Could not retrieve Pip Menu Actions bounds") + assertTrue( + "Pip Menu Actions should be positioned below the Activity in Pip", + top >= activityBounds.bottom + ) + assertTrue( + "Pip Menu Actions should be positioned central horizontally", + centerX() == uiDevice.displayWidth / 2 + ) + assertTrue( + "Pip Menu Actions should be fully shown on the screen", + left >= 0 && right <= uiDevice.displayWidth && bottom <= uiDevice.displayHeight + ) + } + ?: error("Could not retrieve Pip Menu Actions bounds") testApp.closePipWindow() } @@ -107,7 +114,7 @@ class TvPipMenuTests : TvPipTestBase() { // PiP menu should contain the Close button uiDevice.findTvPipMenuCloseButton() - ?: fail("\"Close PIP\" button should be shown in Pip menu") + ?: fail("\"Close PIP\" button should be shown in Pip menu") // Clicking on the Close button should close the app uiDevice.clickTvPipMenuCloseButton() @@ -120,13 +127,15 @@ class TvPipMenuTests : TvPipTestBase() { // PiP menu should contain the Fullscreen button uiDevice.findTvPipMenuFullscreenButton() - ?: fail("\"Full screen\" button should be shown in Pip menu") + ?: fail("\"Full screen\" button should be shown in Pip menu") // Clicking on the fullscreen button should return app to the fullscreen mode. // Click, wait for the app to go fullscreen uiDevice.clickTvPipMenuFullscreenButton() - assertTrue("\"Full screen\" button should open the app fullscreen", - wait { testApp.ui?.isFullscreen(uiDevice) ?: false }) + assertTrue( + "\"Full screen\" button should open the app fullscreen", + wait { testApp.ui?.isFullscreen(uiDevice) ?: false } + ) // Close the app uiDevice.pressBack() @@ -143,8 +152,10 @@ class TvPipMenuTests : TvPipTestBase() { // PiP menu should contain the Pause button uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription) - ?: fail("\"Pause\" button should be shown in Pip menu if there is an active " + - "playing media session.") + ?: fail( + "\"Pause\" button should be shown in Pip menu if there is an active " + + "playing media session." + ) // When we pause media, the button should change from Pause to Play uiDevice.clickTvPipMenuElementWithDescription(pauseButtonDescription) @@ -152,8 +163,10 @@ class TvPipMenuTests : TvPipTestBase() { assertFullscreenAndCloseButtonsAreShown() // PiP menu should contain the Play button now uiDevice.waitForTvPipMenuElementWithDescription(playButtonDescription) - ?: fail("\"Play\" button should be shown in Pip menu if there is an active " + - "paused media session.") + ?: fail( + "\"Play\" button should be shown in Pip menu if there is an active " + + "paused media session." + ) testApp.closePipWindow() } @@ -166,11 +179,11 @@ class TvPipMenuTests : TvPipTestBase() { // PiP menu should contain "No-Op", "Off" and "Clear" buttons... uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_NO_OP) - ?: fail("\"No-Op\" button should be shown in Pip menu") + ?: fail("\"No-Op\" button should be shown in Pip menu") uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_OFF) - ?: fail("\"Off\" button should be shown in Pip menu") + ?: fail("\"Off\" button should be shown in Pip menu") uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR) - ?: fail("\"Clear\" button should be shown in Pip menu") + ?: fail("\"Clear\" button should be shown in Pip menu") // ... and should also contain the "Full screen" and "Close" buttons. assertFullscreenAndCloseButtonsAreShown() @@ -178,31 +191,34 @@ class TvPipMenuTests : TvPipTestBase() { // Invoking the "Off" action should replace it with the "On" action/button and should // remove the "No-Op" action/button. "Clear" action/button should remain in the menu ... uiDevice.waitForTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_ON) - ?: fail("\"On\" button should be shown in Pip for a corresponding custom action") - assertNull("\"No-Op\" button should not be shown in Pip menu", - uiDevice.findTvPipMenuElementWithDescription( - ActivityOptions.Pip.MENU_ACTION_NO_OP)) + ?: fail("\"On\" button should be shown in Pip for a corresponding custom action") + assertNull( + "\"No-Op\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_NO_OP) + ) uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR) - ?: fail("\"Clear\" button should be shown in Pip menu") + ?: fail("\"Clear\" button should be shown in Pip menu") // ... as well as the "Full screen" and "Close" buttons. assertFullscreenAndCloseButtonsAreShown() uiDevice.clickTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR) // Invoking the "Clear" action should remove all the custom actions and their corresponding // buttons, ... - uiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone( - ActivityOptions.Pip.MENU_ACTION_ON)?.also { - isGone -> if (!isGone) fail("\"On\" button should not be shown in Pip menu") - } - assertNull("\"Off\" button should not be shown in Pip menu", - uiDevice.findTvPipMenuElementWithDescription( - ActivityOptions.Pip.MENU_ACTION_OFF)) - assertNull("\"Clear\" button should not be shown in Pip menu", - uiDevice.findTvPipMenuElementWithDescription( - ActivityOptions.Pip.MENU_ACTION_CLEAR)) - assertNull("\"No-Op\" button should not be shown in Pip menu", - uiDevice.findTvPipMenuElementWithDescription( - ActivityOptions.Pip.MENU_ACTION_NO_OP)) + uiDevice + .waitUntilTvPipMenuElementWithDescriptionIsGone(ActivityOptions.Pip.MENU_ACTION_ON) + ?.also { isGone -> if (!isGone) fail("\"On\" button should not be shown in Pip menu") } + assertNull( + "\"Off\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_OFF) + ) + assertNull( + "\"Clear\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR) + ) + assertNull( + "\"No-Op\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_NO_OP) + ) // ... but the menu should still contain the "Full screen" and "Close" buttons. assertFullscreenAndCloseButtonsAreShown() @@ -218,25 +234,31 @@ class TvPipMenuTests : TvPipTestBase() { // PiP menu should contain "No-Op", "Off" and "Clear" buttons for the custom actions... uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_NO_OP) - ?: fail("\"No-Op\" button should be shown in Pip menu") + ?: fail("\"No-Op\" button should be shown in Pip menu") uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_OFF) - ?: fail("\"Off\" button should be shown in Pip menu") + ?: fail("\"Off\" button should be shown in Pip menu") uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR) - ?: fail("\"Clear\" button should be shown in Pip menu") + ?: fail("\"Clear\" button should be shown in Pip menu") // ... should also contain the "Full screen" and "Close" buttons, ... assertFullscreenAndCloseButtonsAreShown() // ... but should not contain media buttons. - assertNull("\"Play\" button should not be shown in menu when there are custom actions", - uiDevice.findTvPipMenuElementWithDescription(playButtonDescription)) - assertNull("\"Pause\" button should not be shown in menu when there are custom actions", - uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription)) + assertNull( + "\"Play\" button should not be shown in menu when there are custom actions", + uiDevice.findTvPipMenuElementWithDescription(playButtonDescription) + ) + assertNull( + "\"Pause\" button should not be shown in menu when there are custom actions", + uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription) + ) uiDevice.clickTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR) // Invoking the "Clear" action should remove all the custom actions, which should bring up // media buttons... uiDevice.waitForTvPipMenuElementWithDescription(pauseButtonDescription) - ?: fail("\"Pause\" button should be shown in Pip menu if there is an active " + - "playing media session.") + ?: fail( + "\"Pause\" button should be shown in Pip menu if there is an active " + + "playing media session." + ) // ... while the "Full screen" and "Close" buttons should remain in the menu. assertFullscreenAndCloseButtonsAreShown() @@ -252,8 +274,8 @@ class TvPipMenuTests : TvPipTestBase() { private fun assertFullscreenAndCloseButtonsAreShown() { uiDevice.findTvPipMenuCloseButton() - ?: fail("\"Close PIP\" button should be shown in Pip menu") + ?: fail("\"Close PIP\" button should be shown in Pip menu") uiDevice.findTvPipMenuFullscreenButton() - ?: fail("\"Full screen\" button should be shown in Pip menu") + ?: fail("\"Full screen\" button should be shown in Pip menu") } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt index 134e97bd46e7..90406c510bad 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt @@ -34,8 +34,8 @@ import org.junit.Before import org.junit.Test /** - * Test Pip Notifications on TV. - * To run this test: `atest WMShellFlickerTests:TvPipNotificationTests` + * Test Pip Notifications on TV. To run this test: `atest + * WMShellFlickerTests:TvPipNotificationTests` */ @RequiresDevice class TvPipNotificationTests : TvPipTestBase() { @@ -58,13 +58,17 @@ class TvPipNotificationTests : TvPipTestBase() { testApp.launchViaIntent() testApp.clickEnterPipButton(wmHelper) - assertNotNull("Pip notification should have been posted", - waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.appName) }) + assertNotNull( + "Pip notification should have been posted", + waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.appName) } + ) testApp.closePipWindow() - assertTrue("Pip notification should have been dismissed", - waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.appName) }) + assertTrue( + "Pip notification should have been dismissed", + waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.appName) } + ) } @Test @@ -72,17 +76,20 @@ class TvPipNotificationTests : TvPipTestBase() { testApp.launchViaIntent() testApp.clickEnterPipButton(wmHelper) - val notification: StatusBarNotification = waitForNotificationToAppear { - it.isPipNotificationWithTitle(testApp.appName) - } ?: fail("Pip notification should have been posted") + val notification: StatusBarNotification = + waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.appName) } + ?: fail("Pip notification should have been posted") - notification.deleteIntent?.send() - ?: fail("Pip notification should contain `delete_intent`") + notification.deleteIntent?.send() ?: fail("Pip notification should contain `delete_intent`") - assertTrue("Pip should have closed by sending the `delete_intent`", - testApp.waitUntilClosed()) - assertTrue("Pip notification should have been dismissed", - waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.appName) }) + assertTrue( + "Pip should have closed by sending the `delete_intent`", + testApp.waitUntilClosed() + ) + assertTrue( + "Pip notification should have been dismissed", + waitForNotificationToDisappear { it.isPipNotificationWithTitle(testApp.appName) } + ) } @Test @@ -90,15 +97,17 @@ class TvPipNotificationTests : TvPipTestBase() { testApp.launchViaIntent(wmHelper) testApp.clickEnterPipButton(wmHelper) - val notification: StatusBarNotification = waitForNotificationToAppear { - it.isPipNotificationWithTitle(testApp.appName) - } ?: fail("Pip notification should have been posted") + val notification: StatusBarNotification = + waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.appName) } + ?: fail("Pip notification should have been posted") notification.contentIntent?.send() ?: fail("Pip notification should contain `content_intent`") - assertNotNull("Pip menu should have been shown after sending `content_intent`", - uiDevice.waitForTvPipMenu()) + assertNotNull( + "Pip menu should have been shown after sending `content_intent`", + uiDevice.waitForTvPipMenu() + ) uiDevice.pressBack() testApp.closePipWindow() @@ -112,35 +121,38 @@ class TvPipNotificationTests : TvPipTestBase() { testApp.clickEnterPipButton(wmHelper) // Wait for the correct notification to show up... - waitForNotificationToAppear { - it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING) - } ?: fail("Pip notification with media session title should have been posted") + waitForNotificationToAppear { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING) } + ?: fail("Pip notification with media session title should have been posted") // ... and make sure "regular" PiP notification is now shown - assertNull("Regular notification should not have been posted", - findNotification { it.isPipNotificationWithTitle(testApp.appName) }) + assertNull( + "Regular notification should not have been posted", + findNotification { it.isPipNotificationWithTitle(testApp.appName) } + ) // Pause the media session. When paused the application updates the title for the media // session. This change should be reflected in the notification. testApp.pauseMedia() // Wait for the "paused" notification to show up... - waitForNotificationToAppear { - it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED) - } ?: fail("Pip notification with media session title should have been posted") + waitForNotificationToAppear { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED) } + ?: fail("Pip notification with media session title should have been posted") // ... and make sure "playing" PiP notification is gone - assertNull("Regular notification should not have been posted", - findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING) }) + assertNull( + "Regular notification should not have been posted", + findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PLAYING) } + ) // Now stop the media session, which should revert the title to the "default" one. testApp.stopMedia() // Wait for the "regular" notification to show up... - waitForNotificationToAppear { - it.isPipNotificationWithTitle(testApp.appName) - } ?: fail("Pip notification with media session title should have been posted") + waitForNotificationToAppear { it.isPipNotificationWithTitle(testApp.appName) } + ?: fail("Pip notification with media session title should have been posted") // ... and make sure previous ("paused") notification is gone - assertNull("Regular notification should not have been posted", - findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED) }) + assertNull( + "Regular notification should not have been posted", + findNotification { it.isPipNotificationWithTitle(TITLE_MEDIA_SESSION_PAUSED) } + ) testApp.closePipWindow() } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt index aeff0ac9f4f2..dc1fe4761757 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt @@ -68,7 +68,8 @@ abstract class TvPipTestBase : PipTestBase(rotationToString(ROTATION_0), ROTATIO fun start() { hasDied = false uiAutomation.adoptShellPermissionIdentity( - android.Manifest.permission.SET_ACTIVITY_WATCHER) + android.Manifest.permission.SET_ACTIVITY_WATCHER + ) activityManager.registerProcessObserver(this) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt index 1c663409b913..b0adbe1d07ce 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt @@ -33,32 +33,31 @@ private const val TV_PIP_MENU_FULLSCREEN_BUTTON_ID = "tv_pip_menu_fullscreen_but private const val FOCUS_ATTEMPTS = 10 private const val WAIT_TIME_MS = 3_000L -private val TV_PIP_MENU_SELECTOR = - By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_ROOT_ID) +private val TV_PIP_MENU_SELECTOR = By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_ROOT_ID) private val TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR = - By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_BUTTONS_CONTAINER_ID) + By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_BUTTONS_CONTAINER_ID) private val TV_PIP_MENU_CLOSE_BUTTON_SELECTOR = - By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_CLOSE_BUTTON_ID) + By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_CLOSE_BUTTON_ID) private val TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR = - By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_FULLSCREEN_BUTTON_ID) + By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_FULLSCREEN_BUTTON_ID) fun UiDevice.waitForTvPipMenu(): UiObject2? = - wait(Until.findObject(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS) + wait(Until.findObject(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS) fun UiDevice.waitForTvPipMenuToClose(): Boolean = - wait(Until.gone(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS) + wait(Until.gone(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS) fun UiDevice.findTvPipMenuControls(): UiObject2? = - findTvPipMenuElement(TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR) + findTvPipMenuElement(TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR) fun UiDevice.findTvPipMenuCloseButton(): UiObject2? = - findTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) + findTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) fun UiDevice.findTvPipMenuFullscreenButton(): UiObject2? = - findTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) + findTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) fun UiDevice.findTvPipMenuElementWithDescription(desc: String): UiObject2? = - findTvPipMenuElement(By.desc(desc)) + findTvPipMenuElement(By.desc(desc)) private fun UiDevice.findTvPipMenuElement(selector: BySelector): UiObject2? = findObject(TV_PIP_MENU_SELECTOR)?.findObject(selector) @@ -70,11 +69,10 @@ fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? { // descendant and then retrieve the element from the menu and return to the caller of this // method. val elementSelector = By.desc(desc) - val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR) - .hasDescendant(elementSelector) + val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector) return wait(Until.findObject(menuContainingElementSelector), WAIT_TIME_MS) - ?.findObject(elementSelector) + ?.findObject(elementSelector) } fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? { @@ -86,18 +84,17 @@ fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boole fun UiDevice.clickTvPipMenuCloseButton() { focusOnAndClickTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) || - error("Could not focus on the Close button") + error("Could not focus on the Close button") } fun UiDevice.clickTvPipMenuFullscreenButton() { focusOnAndClickTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) || - error("Could not focus on the Fullscreen button") + error("Could not focus on the Fullscreen button") } fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) { - focusOnAndClickTvPipMenuElement(By.desc(desc) - .pkg(SYSTEM_UI_PACKAGE_NAME)) || - error("Could not focus on the Pip menu object with \"$desc\" description") + focusOnAndClickTvPipMenuElement(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) || + error("Could not focus on the Pip menu object with \"$desc\" description") // So apparently Accessibility framework on TV is not very reliable and sometimes the state of // the tree of accessibility nodes as seen by the accessibility clients kind of lags behind of // the "real" state of the "UI tree". It seems, however, that moving focus around the tree @@ -110,7 +107,8 @@ fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) { private fun UiDevice.focusOnAndClickTvPipMenuElement(selector: BySelector): Boolean { repeat(FOCUS_ATTEMPTS) { - val element = findTvPipMenuElement(selector) + val element = + findTvPipMenuElement(selector) ?: error("The Pip Menu element we try to focus on is gone.") if (element.isFocusedOrHasFocusedChild) { @@ -119,10 +117,11 @@ private fun UiDevice.focusOnAndClickTvPipMenuElement(selector: BySelector): Bool } findTvPipMenuElement(By.focused(true))?.let { focused -> - if (element.visibleCenter.x < focused.visibleCenter.x) - pressDPadLeft() else pressDPadRight() + if (element.visibleCenter.x < focused.visibleCenter.x) pressDPadLeft() + else pressDPadRight() waitForIdle() - } ?: error("Pip menu does not contain a focused element") + } + ?: error("Pip menu does not contain a focused element") } return false @@ -155,9 +154,8 @@ private fun UiDevice.moveFocus() { fun UiDevice.pressWindowKey() = pressKeyCode(KeyEvent.KEYCODE_WINDOW) -fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run { - height() == uiDevice.displayHeight && width() == uiDevice.displayWidth -} +fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = + visibleBounds.run { height() == uiDevice.displayHeight && width() == uiDevice.displayWidth } val UiObject2.isFocusedOrHasFocusedChild: Boolean get() = isFocused || findObject(By.focused(true)) != null diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt index 5bed66339ee5..9b1247abfb71 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.IwTest -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.WindowManagerPolicyConstants import androidx.test.filters.RequiresDevice @@ -54,12 +53,14 @@ class CopyContentInSplit(testSpec: FlickerTestParameter) : SplitScreenBase(testS override val transition: FlickerBuilder.() -> Unit get() = { super.transition(this) - setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, textEditApp) - } + setup { SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, textEditApp) } transitions { SplitScreenUtils.copyContentInSplit( - instrumentation, device, primaryApp, textEditApp) + instrumentation, + device, + primaryApp, + textEditApp + ) } } @@ -82,94 +83,87 @@ class CopyContentInSplit(testSpec: FlickerTestParameter) : SplitScreenBase(testS @Test fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) - @Presubmit - @Test - fun primaryAppLayerKeepVisible() = testSpec.layerKeepVisible(primaryApp) + @Presubmit @Test fun primaryAppLayerKeepVisible() = testSpec.layerKeepVisible(primaryApp) - @Presubmit - @Test - fun textEditAppLayerKeepVisible() = testSpec.layerKeepVisible(textEditApp) + @Presubmit @Test fun textEditAppLayerKeepVisible() = testSpec.layerKeepVisible(textEditApp) @Presubmit @Test - fun primaryAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible( - primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false) + fun primaryAppBoundsKeepVisible() = + testSpec.splitAppLayerBoundsKeepVisible( + primaryApp, + landscapePosLeft = tapl.isTablet, + portraitPosTop = false + ) @Presubmit @Test - fun textEditAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible( - textEditApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true) + fun textEditAppBoundsKeepVisible() = + testSpec.splitAppLayerBoundsKeepVisible( + textEditApp, + landscapePosLeft = !tapl.isTablet, + portraitPosTop = true + ) - @Presubmit - @Test - fun primaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(primaryApp) + @Presubmit @Test fun primaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(primaryApp) - @Presubmit - @Test - fun textEditAppWindowKeepVisible() = testSpec.appWindowKeepVisible(textEditApp) + @Presubmit @Test fun textEditAppWindowKeepVisible() = testSpec.appWindowKeepVisible(textEditApp) /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarLayerIsVisibleAtStartAndEnd() = - super.navBarLayerIsVisibleAtStartAndEnd() + override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarLayerPositionAtStartAndEnd() = - super.navBarLayerPositionAtStartAndEnd() + override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarWindowIsAlwaysVisible() = - super.navBarWindowIsAlwaysVisible() + override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun statusBarLayerIsVisibleAtStartAndEnd() = super.statusBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun statusBarLayerPositionAtStartAndEnd() = - super.statusBarLayerPositionAtStartAndEnd() + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun statusBarWindowIsAlwaysVisible() = - super.statusBarWindowIsAlwaysVisible() + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun taskBarLayerIsVisibleAtStartAndEnd() = - super.taskBarLayerIsVisibleAtStartAndEnd() + override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun taskBarWindowIsAlwaysVisible() = - super.taskBarWindowIsAlwaysVisible() + override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() @@ -178,10 +172,12 @@ class CopyContentInSplit(testSpec: FlickerTestParameter) : SplitScreenBase(testS @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = - listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)) + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY) + ) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt index 90e11f205ff1..cb49e18d672c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt @@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.WindowManagerPolicyConstants import androidx.test.filters.RequiresDevice @@ -107,67 +106,67 @@ class DismissSplitScreenByGoHome( fun secondaryAppWindowBecomesInvisible() = testSpec.appWindowBecomesInvisible(secondaryApp) /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun statusBarLayerIsVisibleAtStartAndEnd() = super.statusBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt index 6d821c491e82..504238f15b8c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt @@ -117,13 +117,13 @@ class EnterSplitScreenFromOverview(testSpec: FlickerTestParameter) : SplitScreen fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp) /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd() @@ -135,49 +135,49 @@ class EnterSplitScreenFromOverview(testSpec: FlickerTestParameter) : SplitScreen super.navBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun statusBarLayerIsVisibleAtStartAndEnd() = super.statusBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt index e57ac91617b7..6453ed869681 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt @@ -76,15 +76,15 @@ internal object SplitScreenUtils { fun getSendNotification(instrumentation: Instrumentation): NotificationAppHelper = NotificationAppHelper(instrumentation) - fun getIme(instrumentation: Instrumentation): ImeAppHelper = - ImeAppHelper(instrumentation) + fun getIme(instrumentation: Instrumentation): ImeAppHelper = ImeAppHelper(instrumentation) fun waitForSplitComplete( wmHelper: WindowManagerStateHelper, primaryApp: IComponentMatcher, secondaryApp: IComponentMatcher, ) { - wmHelper.StateSyncBuilder() + wmHelper + .StateSyncBuilder() .withWindowSurfaceAppeared(primaryApp) .withWindowSurfaceAppeared(secondaryApp) .withSplitDividerVisible() @@ -101,9 +101,7 @@ internal object SplitScreenUtils { primaryApp.launchViaIntent(wmHelper) secondaryApp.launchViaIntent(wmHelper) tapl.goHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() splitFromOverview(tapl) waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } @@ -113,12 +111,11 @@ internal object SplitScreenUtils { // In landscape, tablet will let the first app split to right side, and phone will // split to left side. if (tapl.isTablet) { - tapl.workspace.switchToOverview().overviewActions - .clickSplit() - .currentTask - .open() + tapl.workspace.switchToOverview().overviewActions.clickSplit().currentTask.open() } else { - tapl.workspace.switchToOverview().currentTask + tapl.workspace + .switchToOverview() + .currentTask .tapMenu() .tapSplitMenuItem() .currentTask @@ -132,28 +129,33 @@ internal object SplitScreenUtils { device: UiDevice, wmHelper: WindowManagerStateHelper ) { - val displayBounds = wmHelper.currentState.layerState - .displays.firstOrNull { !it.isVirtual } - ?.layerStackSpace - ?: error("Display not found") + val displayBounds = + wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace + ?: error("Display not found") // Pull down the notifications device.swipe( - displayBounds.centerX(), 5, - displayBounds.centerX(), displayBounds.bottom, 50 /* steps */ + displayBounds.centerX(), + 5, + displayBounds.centerX(), + displayBounds.bottom, + 50 /* steps */ ) SystemClock.sleep(TIMEOUT_MS) // Find the target notification - val notificationScroller = device.wait( - Until.findObject(notificationScrollerSelector), TIMEOUT_MS - ) ?: error ("Unable to find view $notificationScrollerSelector") + val notificationScroller = + device.wait(Until.findObject(notificationScrollerSelector), TIMEOUT_MS) + ?: error("Unable to find view $notificationScrollerSelector") var notificationContent = notificationScroller.findObject(notificationContentSelector) while (notificationContent == null) { device.swipe( - displayBounds.centerX(), displayBounds.centerY(), - displayBounds.centerX(), displayBounds.centerY() - 150, 20 /* steps */ + displayBounds.centerX(), + displayBounds.centerY(), + displayBounds.centerX(), + displayBounds.centerY() - 150, + 20 /* steps */ ) notificationContent = notificationScroller.findObject(notificationContentSelector) } @@ -164,24 +166,33 @@ internal object SplitScreenUtils { val dragEnd = Point(displayBounds.width / 4, displayBounds.width / 4) val downTime = SystemClock.uptimeMillis() - touch( - instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime, - TIMEOUT_MS, dragStart - ) + touch(instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime, TIMEOUT_MS, dragStart) // It needs a horizontal movement to trigger the drag touchMove( - instrumentation, downTime, SystemClock.uptimeMillis(), - DRAG_DURATION_MS, dragStart, dragMiddle + instrumentation, + downTime, + SystemClock.uptimeMillis(), + DRAG_DURATION_MS, + dragStart, + dragMiddle ) touchMove( - instrumentation, downTime, SystemClock.uptimeMillis(), - DRAG_DURATION_MS, dragMiddle, dragEnd + instrumentation, + downTime, + SystemClock.uptimeMillis(), + DRAG_DURATION_MS, + dragMiddle, + dragEnd ) // Wait for a while to start splitting SystemClock.sleep(TIMEOUT_MS) touch( - instrumentation, MotionEvent.ACTION_UP, downTime, SystemClock.uptimeMillis(), - GESTURE_STEP_MS, dragEnd + instrumentation, + MotionEvent.ACTION_UP, + downTime, + SystemClock.uptimeMillis(), + GESTURE_STEP_MS, + dragEnd ) SystemClock.sleep(TIMEOUT_MS) } @@ -194,9 +205,8 @@ internal object SplitScreenUtils { duration: Long, point: Point ) { - val motionEvent = MotionEvent.obtain( - downTime, eventTime, action, point.x.toFloat(), point.y.toFloat(), 0 - ) + val motionEvent = + MotionEvent.obtain(downTime, eventTime, action, point.x.toFloat(), point.y.toFloat(), 0) motionEvent.source = InputDevice.SOURCE_TOUCHSCREEN instrumentation.uiAutomation.injectInputEvent(motionEvent, true) motionEvent.recycle() @@ -219,9 +229,15 @@ internal object SplitScreenUtils { val stepY = (to.y.toFloat() - from.y.toFloat()) / steps.toFloat() for (i in 1..steps) { - val motionMove = MotionEvent.obtain( - downTime, currentTime, MotionEvent.ACTION_MOVE, currentX, currentY, 0 - ) + val motionMove = + MotionEvent.obtain( + downTime, + currentTime, + MotionEvent.ACTION_MOVE, + currentX, + currentY, + 0 + ) motionMove.source = InputDevice.SOURCE_TOUCHSCREEN instrumentation.uiAutomation.injectInputEvent(motionMove, true) motionMove.recycle() @@ -238,20 +254,14 @@ internal object SplitScreenUtils { } } - fun longPress( - instrumentation: Instrumentation, - point: Point - ) { + fun longPress(instrumentation: Instrumentation, point: Point) { val downTime = SystemClock.uptimeMillis() touch(instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime, TIMEOUT_MS, point) SystemClock.sleep(LONG_PRESS_TIME_MS) touch(instrumentation, MotionEvent.ACTION_UP, downTime, downTime, TIMEOUT_MS, point) } - fun createShortcutOnHotseatIfNotExist( - tapl: LauncherInstrumentation, - appName: String - ) { + fun createShortcutOnHotseatIfNotExist(tapl: LauncherInstrumentation, appName: String) { tapl.workspace.deleteAppIcon(tapl.workspace.getHotseatAppIcon(0)) val allApps = tapl.workspace.switchToAllApps() allApps.freeze() @@ -262,18 +272,15 @@ internal object SplitScreenUtils { } } - fun dragDividerToResizeAndWait( - device: UiDevice, - wmHelper: WindowManagerStateHelper - ) { - val displayBounds = wmHelper.currentState.layerState - .displays.firstOrNull { !it.isVirtual } - ?.layerStackSpace - ?: error("Display not found") + fun dragDividerToResizeAndWait(device: UiDevice, wmHelper: WindowManagerStateHelper) { + val displayBounds = + wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace + ?: error("Display not found") val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS) dividerBar.drag(Point(displayBounds.width * 1 / 3, displayBounds.height * 2 / 3)) - wmHelper.StateSyncBuilder() + wmHelper + .StateSyncBuilder() .withWindowSurfaceDisappeared(SPLIT_DECOR_MANAGER) .waitForAndVerify() } @@ -284,28 +291,30 @@ internal object SplitScreenUtils { dragToRight: Boolean, dragToBottom: Boolean ) { - val displayBounds = wmHelper.currentState.layerState - .displays.firstOrNull { !it.isVirtual } - ?.layerStackSpace - ?: error("Display not found") + val displayBounds = + wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace + ?: error("Display not found") val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS) - dividerBar.drag(Point( - if (dragToRight) { - displayBounds.width * 4 / 5 - } else { - displayBounds.width * 1 / 5 - }, - if (dragToBottom) { - displayBounds.height * 4 / 5 - } else { - displayBounds.height * 1 / 5 - })) + dividerBar.drag( + Point( + if (dragToRight) { + displayBounds.width * 4 / 5 + } else { + displayBounds.width * 1 / 5 + }, + if (dragToBottom) { + displayBounds.height * 4 / 5 + } else { + displayBounds.height * 1 / 5 + } + ) + ) } fun doubleTapDividerToSwitch(device: UiDevice) { val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS) - val interval = (ViewConfiguration.getDoubleTapTimeout() + - ViewConfiguration.getDoubleTapMinTime()) / 2 + val interval = + (ViewConfiguration.getDoubleTapTimeout() + ViewConfiguration.getDoubleTapMinTime()) / 2 dividerBar.click() SystemClock.sleep(interval.toLong()) dividerBar.click() @@ -318,16 +327,22 @@ internal object SplitScreenUtils { destinationApp: IComponentNameMatcher, ) { // Copy text from sourceApp - val textView = device.wait(Until.findObject( - By.res(sourceApp.packageName, "SplitScreenTest")), TIMEOUT_MS) + val textView = + device.wait( + Until.findObject(By.res(sourceApp.packageName, "SplitScreenTest")), + TIMEOUT_MS + ) longPress(instrumentation, textView.visibleCenter) val copyBtn = device.wait(Until.findObject(By.text("Copy")), TIMEOUT_MS) copyBtn.click() // Paste text to destinationApp - val editText = device.wait(Until.findObject( - By.res(destinationApp.packageName, "plain_text_input")), TIMEOUT_MS) + val editText = + device.wait( + Until.findObject(By.res(destinationApp.packageName, "plain_text_input")), + TIMEOUT_MS + ) longPress(instrumentation, editText.visibleCenter) val pasteBtn = device.wait(Until.findObject(By.text("Paste")), TIMEOUT_MS) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt index 274a58c136c8..2ecf81931e4a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.IwTest -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.WindowManagerPolicyConstants import androidx.test.filters.RequiresDevice @@ -55,9 +54,7 @@ class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScr SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, secondaryApp) thirdApp.launchViaIntent(wmHelper) - wmHelper.StateSyncBuilder() - .withWindowSurfaceAppeared(thirdApp) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify() } transitions { tapl.launchedAppState.quickSwitchToPreviousApp() @@ -74,9 +71,7 @@ class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScr @Test fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible() - @Presubmit - @Test - fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp) + @Presubmit @Test fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp) @Presubmit @Test @@ -84,13 +79,21 @@ class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScr @Presubmit @Test - fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( - primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false) + fun primaryAppBoundsIsVisibleAtEnd() = + testSpec.splitAppLayerBoundsIsVisibleAtEnd( + primaryApp, + landscapePosLeft = tapl.isTablet, + portraitPosTop = false + ) @Presubmit @Test - fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( - secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true) + fun secondaryAppBoundsIsVisibleAtEnd() = + testSpec.splitAppLayerBoundsIsVisibleAtEnd( + secondaryApp, + landscapePosLeft = !tapl.isTablet, + portraitPosTop = true + ) @Presubmit @Test @@ -101,67 +104,60 @@ class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScr fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp) /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarLayerIsVisibleAtStartAndEnd() = - super.navBarLayerIsVisibleAtStartAndEnd() + override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarLayerPositionAtStartAndEnd() = - super.navBarLayerPositionAtStartAndEnd() + override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarWindowIsAlwaysVisible() = - super.navBarWindowIsAlwaysVisible() + override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun statusBarLayerIsVisibleAtStartAndEnd() = super.statusBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun statusBarLayerPositionAtStartAndEnd() = - super.statusBarLayerPositionAtStartAndEnd() + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun statusBarWindowIsAlwaysVisible() = - super.statusBarWindowIsAlwaysVisible() + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun taskBarLayerIsVisibleAtStartAndEnd() = - super.taskBarLayerIsVisibleAtStartAndEnd() + override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun taskBarWindowIsAlwaysVisible() = - super.taskBarWindowIsAlwaysVisible() + override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() @@ -170,10 +166,12 @@ class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScr @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = - listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)) + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY) + ) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt index 3da4a943af4e..384489d99de3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.IwTest -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.WindowManagerPolicyConstants import androidx.test.filters.RequiresDevice @@ -54,9 +53,7 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, secondaryApp) tapl.goHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() } transitions { tapl.workspace.quickSwitchToPreviousApp() @@ -73,9 +70,7 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas @Test fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible() - @Presubmit - @Test - fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp) + @Presubmit @Test fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp) @Presubmit @Test @@ -83,13 +78,21 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas @Presubmit @Test - fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( - primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false) + fun primaryAppBoundsIsVisibleAtEnd() = + testSpec.splitAppLayerBoundsIsVisibleAtEnd( + primaryApp, + landscapePosLeft = tapl.isTablet, + portraitPosTop = false + ) @Presubmit @Test - fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( - secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true) + fun secondaryAppBoundsIsVisibleAtEnd() = + testSpec.splitAppLayerBoundsIsVisibleAtEnd( + secondaryApp, + landscapePosLeft = !tapl.isTablet, + portraitPosTop = true + ) @Presubmit @Test @@ -100,67 +103,60 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp) /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarLayerIsVisibleAtStartAndEnd() = - super.navBarLayerIsVisibleAtStartAndEnd() + override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarLayerPositionAtStartAndEnd() = - super.navBarLayerPositionAtStartAndEnd() + override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarWindowIsAlwaysVisible() = - super.navBarWindowIsAlwaysVisible() + override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun statusBarLayerIsVisibleAtStartAndEnd() = super.statusBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun statusBarLayerPositionAtStartAndEnd() = - super.statusBarLayerPositionAtStartAndEnd() + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun statusBarWindowIsAlwaysVisible() = - super.statusBarWindowIsAlwaysVisible() + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun taskBarLayerIsVisibleAtStartAndEnd() = - super.taskBarLayerIsVisibleAtStartAndEnd() + override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun taskBarWindowIsAlwaysVisible() = - super.taskBarWindowIsAlwaysVisible() + override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() @@ -169,10 +165,12 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = - listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)) + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY) + ) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt index f87d2951f265..04ebbf527b3d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.IwTest -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.WindowManagerPolicyConstants import androidx.test.filters.RequiresDevice @@ -54,14 +53,10 @@ class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenB SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, secondaryApp) tapl.goHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() } transitions { - tapl.workspace.switchToOverview() - .currentTask - .open() + tapl.workspace.switchToOverview().currentTask.open() SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp) } } @@ -75,9 +70,7 @@ class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenB @Test fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible() - @Presubmit - @Test - fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp) + @Presubmit @Test fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp) @Presubmit @Test @@ -85,13 +78,21 @@ class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenB @Presubmit @Test - fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( - primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false) + fun primaryAppBoundsIsVisibleAtEnd() = + testSpec.splitAppLayerBoundsIsVisibleAtEnd( + primaryApp, + landscapePosLeft = tapl.isTablet, + portraitPosTop = false + ) @Presubmit @Test - fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( - secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true) + fun secondaryAppBoundsIsVisibleAtEnd() = + testSpec.splitAppLayerBoundsIsVisibleAtEnd( + secondaryApp, + landscapePosLeft = !tapl.isTablet, + portraitPosTop = true + ) @Presubmit @Test @@ -102,67 +103,60 @@ class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenB fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp) /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarLayerIsVisibleAtStartAndEnd() = - super.navBarLayerIsVisibleAtStartAndEnd() + override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarLayerPositionAtStartAndEnd() = - super.navBarLayerPositionAtStartAndEnd() + override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun navBarWindowIsAlwaysVisible() = - super.navBarWindowIsAlwaysVisible() + override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun statusBarLayerIsVisibleAtStartAndEnd() = super.statusBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun statusBarLayerPositionAtStartAndEnd() = - super.statusBarLayerPositionAtStartAndEnd() + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun statusBarWindowIsAlwaysVisible() = - super.statusBarWindowIsAlwaysVisible() + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun taskBarLayerIsVisibleAtStartAndEnd() = - super.taskBarLayerIsVisibleAtStartAndEnd() + override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test - override fun taskBarWindowIsAlwaysVisible() = - super.taskBarWindowIsAlwaysVisible() + override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() /** {@inheritDoc} */ - @Postsubmit + @Presubmit @Test override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() @@ -171,10 +165,12 @@ class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenB @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = - listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)) + return FlickerTestParameterFactory.getInstance() + .getConfigNonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY) + ) } } } diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 283a41a81057..57815370e2ba 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -28,11 +28,13 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.Set; @@ -622,6 +624,58 @@ public final class MediaRoute2Info implements Parcelable { return true; } + /** + * Dumps the current state of the object to the given {@code pw} as a human-readable string. + * + * <p> Used in the context of dumpsys. </p> + * + * @hide + */ + public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { + pw.println(prefix + "MediaRoute2Info"); + + String indent = prefix + " "; + + pw.println(indent + "mId=" + mId); + pw.println(indent + "mName=" + mName); + pw.println(indent + "mFeatures=" + mFeatures); + pw.println(indent + "mIsSystem=" + mIsSystem); + pw.println(indent + "mIconUri=" + mIconUri); + pw.println(indent + "mDescription=" + mDescription); + pw.println(indent + "mConnectionState=" + mConnectionState); + pw.println(indent + "mClientPackageName=" + mClientPackageName); + pw.println(indent + "mPackageName=" + mPackageName); + + dumpVolume(pw, indent); + + pw.println(indent + "mAddress=" + mAddress); + pw.println(indent + "mDeduplicationIds=" + mDeduplicationIds); + pw.println(indent + "mExtras=" + mExtras); + pw.println(indent + "mProviderId=" + mProviderId); + } + + private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) { + String volumeHandlingName; + + switch (mVolumeHandling) { + case PLAYBACK_VOLUME_FIXED: + volumeHandlingName = "FIXED"; + break; + case PLAYBACK_VOLUME_VARIABLE: + volumeHandlingName = "VARIABLE"; + break; + default: + volumeHandlingName = "UNKNOWN"; + break; + } + + String volume = String.format(Locale.US, + "volume(current=%d, max=%d, handling=%s(%d))", + mVolume, mVolumeMax, volumeHandlingName, mVolumeHandling); + + pw.println(prefix + volume); + } + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index 4fb575b0ab09..9818ee78b2b1 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -45,6 +45,7 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.companion.AssociationInfo; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; @@ -81,6 +82,7 @@ import java.util.List; * A CompanionDevice activity response for showing the available * nearby devices to be associated with. */ +@SuppressLint("LongLogTag") public class CompanionDeviceActivity extends FragmentActivity implements CompanionVendorHelperDialogFragment.CompanionVendorHelperDialogListener { private static final boolean DEBUG = false; @@ -94,6 +96,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements private static final String EXTRA_APPLICATION_CALLBACK = "application_callback"; private static final String EXTRA_ASSOCIATION_REQUEST = "association_request"; private static final String EXTRA_RESULT_RECEIVER = "result_receiver"; + private static final String EXTRA_FORCE_CANCEL_CONFIRMATION = "cancel_confirmation"; private static final String FRAGMENT_DIALOG_TAG = "fragment_dialog"; @@ -162,6 +165,12 @@ public class CompanionDeviceActivity extends FragmentActivity implements @Override public void onCreate(Bundle savedInstanceState) { if (DEBUG) Log.d(TAG, "onCreate()"); + boolean forceCancelDialog = getIntent().getBooleanExtra("cancel_confirmation", false); + // Must handle the force cancel request in onNewIntent. + if (forceCancelDialog) { + Log.i(TAG, "The confirmation does not exist, skipping the cancel request"); + finish(); + } super.onCreate(savedInstanceState); getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); @@ -195,10 +204,23 @@ public class CompanionDeviceActivity extends FragmentActivity implements @Override protected void onNewIntent(Intent intent) { + // Force cancels the CDM dialog if this activity receives another intent with + // EXTRA_FORCE_CANCEL_CONFIRMATION. + boolean forCancelDialog = intent.getBooleanExtra(EXTRA_FORCE_CANCEL_CONFIRMATION, false); + + if (forCancelDialog) { + + Log.i(TAG, "Cancelling the user confirmation"); + + cancel(false, false); + return; + } + // Handle another incoming request (while we are not done with the original - mRequest - // yet). final AssociationRequest request = requireNonNull( intent.getParcelableExtra(EXTRA_ASSOCIATION_REQUEST)); + if (DEBUG) Log.d(TAG, "onNewIntent(), request=" + request); // We can only "process" one request at a time. diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java index 732e7340cf5a..75b0df5ac628 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java @@ -29,6 +29,7 @@ import static java.util.Objects.requireNonNull; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -68,6 +69,7 @@ import java.util.Objects; /** * A CompanionDevice service response for scanning nearby devices */ +@SuppressLint("LongLogTag") public class CompanionDeviceDiscoveryService extends Service { private static final boolean DEBUG = false; private static final String TAG = "CDM_CompanionDeviceDiscoveryService"; diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 9324e8f3babb..637ac1911a85 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -246,6 +246,16 @@ <string name="screenrecord_start_label">Start Recording?</string> <!-- Message reminding the user that sensitive information may be captured during a screen recording [CHAR_LIMIT=NONE]--> <string name="screenrecord_description">While recording, Android System can capture any sensitive information that\u2019s visible on your screen or played on your device. This includes passwords, payment info, photos, messages, and audio.</string> + <!-- Dropdown option to record the entire screen [CHAR_LIMIT=30]--> + <string name="screenrecord_option_entire_screen">Record entire screen</string> + <!-- Dropdown option to record a single app [CHAR_LIMIT=30]--> + <string name="screenrecord_option_single_app">Record a single app</string> + <!-- Message reminding the user that sensitive information may be captured during a full screen recording for the updated dialog that includes partial screen sharing option [CHAR_LIMIT=350]--> + <string name="screenrecord_warning_entire_screen">While you\'re recording, Android has access to anything visible on your screen or played on your device. So be careful with passwords, payment details, messages, or other sensitive information.</string> + <!-- Message reminding the user that sensitive information may be captured during a single app screen recording for the updated dialog that includes partial screen sharing option [CHAR_LIMIT=350]--> + <string name="screenrecord_warning_single_app">While you\'re recording an app, Android has access to anything shown or played on that app. So be careful with passwords, payment details, messages, or other sensitive information.</string> + <!-- Button to start a screen recording in the updated screen record dialog that allows to select an app to record [CHAR LIMIT=50]--> + <string name="screenrecord_start_recording">Start recording</string> <!-- Label for a switch to enable recording audio [CHAR LIMIT=NONE]--> <string name="screenrecord_audio_label">Record audio</string> <!-- Label for the option to record audio from the device [CHAR LIMIT=NONE]--> @@ -958,7 +968,26 @@ <!-- Media projection permission dialog warning title. [CHAR LIMIT=NONE] --> <string name="media_projection_dialog_title">Start recording or casting with <xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g>?</string> - <!-- Media projection permission dialog permanent grant check box. [CHAR LIMIT=NONE] --> + <!-- Media projection permission dialog title. [CHAR LIMIT=NONE] --> + <string name="media_projection_permission_dialog_title">Allow <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g> to share or record?</string> + + <!-- Media projection permission dropdown option for capturing the whole screen. [CHAR LIMIT=30] --> + <string name="media_projection_permission_dialog_option_entire_screen">Entire screen</string> + + <!-- Media projection permission dropdown option for capturing single app. [CHAR LIMIT=30] --> + <string name="media_projection_permission_dialog_option_single_app">A single app</string> + + <!-- Media projection permission warning for capturing the whole screen. [CHAR LIMIT=350] --> + <string name="media_projection_permission_dialog_warning_entire_screen">When you\'re sharing, recording, or casting, <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with passwords, payment details, messages, or other sensitive information.</string> + + <!-- Media projection permission warning for capturing an app. [CHAR LIMIT=350] --> + <string name="media_projection_permission_dialog_warning_single_app">When you\'re sharing, recording, or casting an app, <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g> has access to anything shown or played on that app. So be careful with passwords, payment details, messages, or other sensitive information.</string> + + <!-- Media projection permission button to continue with app selection or recording [CHAR LIMIT=60] --> + <string name="media_projection_permission_dialog_continue">Continue</string> + + <!-- Title of the dialog that allows to select an app to share or record [CHAR LIMIT=NONE] --> + <string name="media_projection_permission_app_selector_title">Share or record an app</string> <!-- The text to clear all notifications. [CHAR LIMIT=60] --> <string name="clear_all_notifications_text">Clear all</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index e77c65079456..2b2b05ce2fbf 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -81,11 +81,6 @@ interface ISystemUiProxy { */ void stopScreenPinning() = 17; - /* - * Notifies that the swipe-to-home (recents animation) is finished. - */ - void notifySwipeToHomeFinished() = 23; - /** * Notifies that quickstep will switch to a new task * @param rotation indicates which Surface.Rotation the gesture was started in diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java index 2ba2bb6edc18..ed6fbecd19fe 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java @@ -45,7 +45,7 @@ class SimpleMirrorWindowControl extends MirrorWindowControl implements View.OnCl private boolean mShouldSetTouchStart; @Nullable private MoveWindowTask mMoveWindowTask; - private PointF mLastDrag = new PointF(); + private final PointF mLastDrag = new PointF(); private final Handler mHandler; SimpleMirrorWindowControl(Context context, Handler handler) { @@ -92,8 +92,7 @@ class SimpleMirrorWindowControl extends MirrorWindowControl implements View.OnCl } private Point findOffset(View v, int moveFrameAmount) { - final Point offset = mTmpPoint; - offset.set(0, 0); + mTmpPoint.set(0, 0); if (v.getId() == R.id.left_control) { mTmpPoint.x = -moveFrameAmount; } else if (v.getId() == R.id.up_control) { @@ -184,7 +183,7 @@ class SimpleMirrorWindowControl extends MirrorWindowControl implements View.OnCl private final int mYOffset; private final Handler mHandler; /** Time in milliseconds between successive task executions.*/ - private long mPeriod; + private final long mPeriod; private boolean mCancel; MoveWindowTask(@NonNull MirrorWindowDelegate windowDelegate, Handler handler, int xOffset, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index e3c04a379fe4..a6e767ca27ee 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -104,7 +104,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private final Context mContext; private final Resources mResources; private final Handler mHandler; - private Rect mWindowBounds; + private final Rect mWindowBounds; private final int mDisplayId; @Surface.Rotation @VisibleForTesting @@ -193,11 +193,11 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; private final MagnificationGestureDetector mGestureDetector; private final int mBounceEffectDuration; - private Choreographer.FrameCallback mMirrorViewGeometryVsyncCallback; + private final Choreographer.FrameCallback mMirrorViewGeometryVsyncCallback; private Locale mLocale; private NumberFormat mPercentFormat; private float mBounceEffectAnimationScale; - private SysUiState mSysUiState; + private final SysUiState mSysUiState; // Set it to true when the view is overlapped with the gesture insets at the bottom. private boolean mOverlapWithGestureInsets; private boolean mIsDragging; @@ -215,7 +215,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private boolean mEditSizeEnable = false; @Nullable - private MirrorWindowControl mMirrorWindowControl; + private final MirrorWindowControl mMirrorWindowControl; WindowMagnificationController( @UiContext Context context, @@ -562,9 +562,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold /** Returns the rotation degree change of two {@link Surface.Rotation} */ private int getDegreeFromRotation(@Surface.Rotation int newRotation, @Surface.Rotation int oldRotation) { - final int rotationDiff = oldRotation - newRotation; - final int degree = (rotationDiff + 4) % 4 * 90; - return degree; + return (oldRotation - newRotation + 4) % 4 * 90; } private void createMirrorWindow() { diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 38d9d0210378..17445d319b9c 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -247,6 +247,13 @@ public class Flags { public static final SysPropBooleanFlag SHOW_FLOATING_TASKS_AS_BUBBLES = new SysPropBooleanFlag(1107, "persist.wm.debug.floating_tasks_as_bubbles", false); + @Keep + public static final SysPropBooleanFlag ENABLE_FLING_TO_DISMISS_BUBBLE = + new SysPropBooleanFlag(1108, "persist.wm.debug.fling_to_dismiss_bubble", true); + @Keep + public static final SysPropBooleanFlag ENABLE_FLING_TO_DISMISS_PIP = + new SysPropBooleanFlag(1109, "persist.wm.debug.fling_to_dismiss_pip", true); + // 1200 - predictive back @Keep public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag( 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 0f1338e4e872..b26b42c802f4 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -57,7 +57,6 @@ import android.window.BackEvent; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.policy.GestureNavigationSettingsObserver; -import com.android.internal.util.LatencyTracker; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; @@ -199,7 +198,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private final Rect mNavBarOverlayExcludedBounds = new Rect(); private final Region mExcludeRegion = new Region(); private final Region mUnrestrictedExcludeRegion = new Region(); - private final LatencyTracker mLatencyTracker; + private final Provider<NavigationBarEdgePanel> mNavBarEdgePanelProvider; private final Provider<BackGestureTfClassifierProvider> mBackGestureTfClassifierProviderProvider; private final FeatureFlags mFeatureFlags; @@ -339,7 +338,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker IWindowManager windowManagerService, Optional<Pip> pipOptional, FalsingManager falsingManager, - LatencyTracker latencyTracker, + Provider<NavigationBarEdgePanel> navigationBarEdgePanelProvider, Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider, FeatureFlags featureFlags) { super(broadcastDispatcher); @@ -358,7 +357,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker mWindowManagerService = windowManagerService; mPipOptional = pipOptional; mFalsingManager = falsingManager; - mLatencyTracker = latencyTracker; + mNavBarEdgePanelProvider = navigationBarEdgePanelProvider; mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider; mFeatureFlags = featureFlags; mLastReportedConfig.setTo(mContext.getResources().getConfiguration()); @@ -583,8 +582,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker setEdgeBackPlugin( mBackPanelControllerFactory.create(mContext)); } else { - setEdgeBackPlugin( - new NavigationBarEdgePanel(mContext, mLatencyTracker)); + setEdgeBackPlugin(mNavBarEdgePanelProvider.get()); } } @@ -1091,7 +1089,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private final IWindowManager mWindowManagerService; private final Optional<Pip> mPipOptional; private final FalsingManager mFalsingManager; - private final LatencyTracker mLatencyTracker; + private final Provider<NavigationBarEdgePanel> mNavBarEdgePanelProvider; private final Provider<BackGestureTfClassifierProvider> mBackGestureTfClassifierProviderProvider; private final FeatureFlags mFeatureFlags; @@ -1111,7 +1109,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker IWindowManager windowManagerService, Optional<Pip> pipOptional, FalsingManager falsingManager, - LatencyTracker latencyTracker, + Provider<NavigationBarEdgePanel> navBarEdgePanelProvider, Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider, FeatureFlags featureFlags) { @@ -1129,7 +1127,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker mWindowManagerService = windowManagerService; mPipOptional = pipOptional; mFalsingManager = falsingManager; - mLatencyTracker = latencyTracker; + mNavBarEdgePanelProvider = navBarEdgePanelProvider; mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider; mFeatureFlags = featureFlags; } @@ -1152,7 +1150,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker mWindowManagerService, mPipOptional, mFalsingManager, - mLatencyTracker, + mNavBarEdgePanelProvider, mBackGestureTfClassifierProviderProvider, mFeatureFlags); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java index 24efc762b39b..1230708d780a 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java @@ -52,9 +52,9 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.util.LatencyTracker; import com.android.settingslib.Utils; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.plugins.NavigationEdgeBackPlugin; import com.android.systemui.shared.navigationbar.RegionSamplingHelper; import com.android.systemui.statusbar.VibratorHelper; @@ -62,6 +62,8 @@ import com.android.systemui.statusbar.VibratorHelper; import java.io.PrintWriter; import java.util.concurrent.Executor; +import javax.inject.Inject; + public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPlugin { private static final String TAG = "NavigationBarEdgePanel"; @@ -282,11 +284,16 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl }; private BackCallback mBackCallback; - public NavigationBarEdgePanel(Context context, LatencyTracker latencyTracker) { + @Inject + public NavigationBarEdgePanel( + Context context, + LatencyTracker latencyTracker, + VibratorHelper vibratorHelper, + @Background Executor backgroundExecutor) { super(context); mWindowManager = context.getSystemService(WindowManager.class); - mVibratorHelper = Dependency.get(VibratorHelper.class); + mVibratorHelper = vibratorHelper; mDensity = context.getResources().getDisplayMetrics().density; @@ -358,7 +365,6 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl setVisibility(GONE); - Executor backgroundExecutor = Dependency.get(Dependency.BACKGROUND_EXECUTOR); boolean isPrimaryDisplay = mContext.getDisplayId() == DEFAULT_DISPLAY; mRegionSamplingHelper = new RegionSamplingHelper(this, new RegionSamplingHelper.SamplingCallback() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index b3e6f8ab3de6..7a44058a46c7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -60,6 +60,7 @@ import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder; import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.BrightnessMirrorController; @@ -82,7 +83,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private static final String EXTRA_VISIBLE = "visible"; private final Rect mQsBounds = new Rect(); - private final StatusBarStateController mStatusBarStateController; + private final SysuiStatusBarStateController mStatusBarStateController; private final FalsingManager mFalsingManager; private final KeyguardBypassController mBypassController; private boolean mQsExpanded; @@ -159,7 +160,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca * Progress of pull down from the center of the lock screen. * @see com.android.systemui.statusbar.LockscreenShadeTransitionController */ - private float mFullShadeProgress; + private float mLockscreenToShadeProgress; private boolean mOverScrolling; @@ -177,7 +178,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca @Inject public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler, QSTileHost qsTileHost, - StatusBarStateController statusBarStateController, CommandQueue commandQueue, + SysuiStatusBarStateController statusBarStateController, CommandQueue commandQueue, @Named(QS_PANEL) MediaHost qsMediaHost, @Named(QUICK_QS_PANEL) MediaHost qqsMediaHost, KeyguardBypassController keyguardBypassController, @@ -442,20 +443,19 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } private void updateQsState() { - final boolean expanded = mQsExpanded || mInSplitShade; - final boolean expandVisually = expanded || mStackScrollerOverscrolling + final boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating; - mQSPanelController.setExpanded(expanded); + mQSPanelController.setExpanded(mQsExpanded); boolean keyguardShowing = isKeyguardState(); - mHeader.setVisibility((expanded || !keyguardShowing || mHeaderAnimating + mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating || mShowCollapsedOnKeyguard) ? View.VISIBLE : View.INVISIBLE); mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard) - || (expanded && !mStackScrollerOverscrolling), mQuickQSPanelController); + || (mQsExpanded && !mStackScrollerOverscrolling), mQuickQSPanelController); boolean qsPanelVisible = !mQsDisabled && expandVisually; - boolean footerVisible = qsPanelVisible && (expanded || !keyguardShowing || mHeaderAnimating - || mShowCollapsedOnKeyguard); + boolean footerVisible = qsPanelVisible && (mQsExpanded || !keyguardShowing + || mHeaderAnimating || mShowCollapsedOnKeyguard); mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE); if (mQSFooterActionController != null) { mQSFooterActionController.setVisible(footerVisible); @@ -463,7 +463,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mQSFooterActionsViewModel.onVisibilityChangeRequested(footerVisible); } mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard) - || (expanded && !mStackScrollerOverscrolling)); + || (mQsExpanded && !mStackScrollerOverscrolling)); mQSPanelController.setVisibility(qsPanelVisible ? View.VISIBLE : View.INVISIBLE); if (DEBUG) { Log.d(TAG, "Footer: " + footerVisible + ", QS Panel: " + qsPanelVisible); @@ -586,7 +586,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mTransitioningToFullShade = isTransitioningToFullShade; updateShowCollapsedOnKeyguard(); } - mFullShadeProgress = qsTransitionFraction; + mLockscreenToShadeProgress = qsTransitionFraction; setQsExpansion(mLastQSExpansion, mLastPanelFraction, mLastHeaderTranslation, isTransitioningToFullShade ? qsSquishinessFraction : mSquishinessFraction); } @@ -710,10 +710,13 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } if (mInSplitShade) { // Large screens in landscape. - if (mTransitioningToFullShade || isKeyguardState()) { + // Need to check upcoming state as for unlocked -> AOD transition current state is + // not updated yet, but we're transitioning and UI should already follow KEYGUARD state + if (mTransitioningToFullShade || mStatusBarStateController.getCurrentOrUpcomingState() + == StatusBarState.KEYGUARD) { // Always use "mFullShadeProgress" on keyguard, because // "panelExpansionFractions" is always 1 on keyguard split shade. - return mFullShadeProgress; + return mLockscreenToShadeProgress; } else { return panelExpansionFraction; } @@ -722,7 +725,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca if (mTransitioningToFullShade) { // Only use this value during the standard lock screen shade expansion. During the // "quick" expansion from top, this value is 0. - return mFullShadeProgress; + return mLockscreenToShadeProgress; } else { return panelExpansionFraction; } @@ -930,7 +933,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca indentingPw.println("mLastHeaderTranslation: " + mLastHeaderTranslation); indentingPw.println("mInSplitShade: " + mInSplitShade); indentingPw.println("mTransitioningToFullShade: " + mTransitioningToFullShade); - indentingPw.println("mFullShadeProgress: " + mFullShadeProgress); + indentingPw.println("mLockscreenToShadeProgress: " + mLockscreenToShadeProgress); indentingPw.println("mOverScrolling: " + mOverScrolling); indentingPw.println("isCustomizing: " + mQSCustomizerController.isCustomizing()); View view = getView(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 7e2a5c51786d..899e57d7d0ae 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -342,14 +342,6 @@ public class OverviewProxyService extends CurrentUserTracker implements } @Override - public void notifySwipeToHomeFinished() { - verifyCallerAndClearCallingIdentity("notifySwipeToHomeFinished", () -> - mPipOptional.ifPresent( - pip -> pip.setPinnedStackAnimationType( - PipAnimationController.ANIM_TYPE_ALPHA))); - } - - @Override public void notifySwipeUpGestureStarted() { verifyCallerAndClearCallingIdentityPostMain("notifySwipeUpGestureStarted", () -> notifySwipeUpGestureStartedInternal()); diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java index a22fda7f9a47..6e9f859c202b 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; @@ -76,6 +77,12 @@ public class BrightnessDialog extends Activity { FrameLayout frame = findViewById(R.id.brightness_mirror_container); // The brightness mirror container is INVISIBLE by default. frame.setVisibility(View.VISIBLE); + ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) frame.getLayoutParams(); + int horizontalMargin = + getResources().getDimensionPixelSize(R.dimen.notification_side_paddings); + lp.leftMargin = horizontalMargin; + lp.rightMargin = horizontalMargin; + frame.setLayoutParams(lp); BrightnessSliderController controller = mToggleSliderFactory.create(this, frame); controller.init(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index d7e86b6e2919..7254e090e32f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -430,6 +430,7 @@ public final class NotificationPanelViewController extends PanelViewController { /** * Determines if QS should be already expanded when expanding shade. * Used for split shade, two finger gesture as well as accessibility shortcut to QS. + * It needs to be set when movement starts as it resets at the end of expansion/collapse. */ @VisibleForTesting boolean mQsExpandImmediate; @@ -1737,8 +1738,10 @@ public final class NotificationPanelViewController extends PanelViewController { } private void setQsExpandImmediate(boolean expandImmediate) { - mQsExpandImmediate = expandImmediate; - mPanelEventsEmitter.notifyExpandImmediateChange(expandImmediate); + if (expandImmediate != mQsExpandImmediate) { + mQsExpandImmediate = expandImmediate; + mPanelEventsEmitter.notifyExpandImmediateChange(expandImmediate); + } } private void setShowShelfOnly(boolean shelfOnly) { @@ -2479,17 +2482,23 @@ public final class NotificationPanelViewController extends PanelViewController { mDepthController.setQsPanelExpansion(qsExpansionFraction); mStatusBarKeyguardViewManager.setQsExpansion(qsExpansionFraction); - // updateQsExpansion will get called whenever mTransitionToFullShadeProgress or - // mLockscreenShadeTransitionController.getDragProgress change. - // When in lockscreen, getDragProgress indicates the true expanded fraction of QS - float shadeExpandedFraction = mTransitioningToFullShadeProgress > 0 - ? mLockscreenShadeTransitionController.getQSDragProgress() + float shadeExpandedFraction = isOnKeyguard() + ? getLockscreenShadeDragProgress() : getExpandedFraction(); mLargeScreenShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction); mLargeScreenShadeHeaderController.setQsExpandedFraction(qsExpansionFraction); mLargeScreenShadeHeaderController.setQsVisible(mQsVisible); } + private float getLockscreenShadeDragProgress() { + // mTransitioningToFullShadeProgress > 0 means we're doing regular lockscreen to shade + // transition. If that's not the case we should follow QS expansion fraction for when + // user is pulling from the same top to go directly to expanded QS + return mTransitioningToFullShadeProgress > 0 + ? mLockscreenShadeTransitionController.getQSDragProgress() + : computeQsExpansionFraction(); + } + private void onStackYChanged(boolean shouldAnimate) { if (mQs != null) { if (shouldAnimate) { @@ -3124,26 +3133,24 @@ public final class NotificationPanelViewController extends PanelViewController { } if (mQsExpandImmediate || (mQsExpanded && !mQsTracking && mQsExpansionAnimator == null && !mQsExpansionFromOverscroll)) { - float t; - if (mKeyguardShowing) { - + float qsExpansionFraction; + if (mSplitShadeEnabled) { + qsExpansionFraction = 1; + } else if (mKeyguardShowing) { // On Keyguard, interpolate the QS expansion linearly to the panel expansion - t = expandedHeight / (getMaxPanelHeight()); + qsExpansionFraction = expandedHeight / (getMaxPanelHeight()); } else { // In Shade, interpolate linearly such that QS is closed whenever panel height is // minimum QS expansion + minStackHeight - float - panelHeightQsCollapsed = + float panelHeightQsCollapsed = mNotificationStackScrollLayoutController.getIntrinsicPadding() + mNotificationStackScrollLayoutController.getLayoutMinHeight(); float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); - t = - (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded - - panelHeightQsCollapsed); + qsExpansionFraction = (expandedHeight - panelHeightQsCollapsed) + / (panelHeightQsExpanded - panelHeightQsCollapsed); } - float - targetHeight = - mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight); + float targetHeight = mQsMinExpansionHeight + + qsExpansionFraction * (mQsMaxExpansionHeight - mQsMinExpansionHeight); setQsExpansion(targetHeight); } updateExpandedHeight(expandedHeight); @@ -3329,7 +3336,11 @@ public final class NotificationPanelViewController extends PanelViewController { } else { setListening(true); } - setQsExpandImmediate(false); + if (mBarState != SHADE) { + // updating qsExpandImmediate is done in onPanelStateChanged for unlocked shade but + // on keyguard panel state is always OPEN so we need to have that extra update + setQsExpandImmediate(false); + } setShowShelfOnly(false); mTwoFingerQsExpandPossible = false; updateTrackingHeadsUp(null); @@ -4678,6 +4689,11 @@ public final class NotificationPanelViewController extends PanelViewController { } } } else { + // this else branch means we are doing one of: + // - from KEYGUARD and SHADE (but not expanded shade) + // - from SHADE to KEYGUARD + // - from SHADE_LOCKED to SHADE + // - getting notified again about the current SHADE or KEYGUARD state final boolean animatingUnlockedShadeToKeyguard = oldState == SHADE && statusBarState == KEYGUARD && mScreenOffAnimationController.isKeyguardShowDelayed(); @@ -4749,9 +4765,7 @@ public final class NotificationPanelViewController extends PanelViewController { @Override public float getLockscreenShadeDragProgress() { - return mTransitioningToFullShadeProgress > 0 - ? mLockscreenShadeTransitionController.getQSDragProgress() - : computeQsExpansionFraction(); + return NotificationPanelViewController.this.getLockscreenShadeDragProgress(); } }; @@ -4988,6 +5002,7 @@ public final class NotificationPanelViewController extends PanelViewController { updateQSExpansionEnabledAmbient(); if (state == STATE_OPEN && mCurrentPanelState != state) { + setQsExpandImmediate(false); mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); } if (state == STATE_OPENING) { @@ -5000,6 +5015,7 @@ public final class NotificationPanelViewController extends PanelViewController { mCentralSurfaces.makeExpandedVisible(false); } if (state == STATE_CLOSED) { + setQsExpandImmediate(false); // Close the status bar in the next frame so we can show the end of the // animation. mView.post(mMaybeHideExpandedRunnable); 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 8278b549a7a0..ccf6feca6992 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 @@ -393,7 +393,7 @@ class HeadsUpCoordinator @Inject constructor( val posted = mPostedEntries.compute(entry.key) { _, value -> value?.also { update -> update.wasUpdated = true - update.shouldHeadsUpEver = update.shouldHeadsUpEver || shouldHeadsUpEver + update.shouldHeadsUpEver = shouldHeadsUpEver update.shouldHeadsUpAgain = update.shouldHeadsUpAgain || shouldHeadsUpAgain update.isAlerting = isAlerting update.isBinding = isBinding diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java index bc172ce537f3..0b435fe9dcc6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java @@ -35,12 +35,12 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; * bounds change. */ public class NotificationSection { - private @PriorityBucket int mBucket; - private View mOwningView; - private Rect mBounds = new Rect(); - private Rect mCurrentBounds = new Rect(-1, -1, -1, -1); - private Rect mStartAnimationRect = new Rect(); - private Rect mEndAnimationRect = new Rect(); + private @PriorityBucket final int mBucket; + private final View mOwningView; + private final Rect mBounds = new Rect(); + private final Rect mCurrentBounds = new Rect(-1, -1, -1, -1); + private final Rect mStartAnimationRect = new Rect(); + private final Rect mEndAnimationRect = new Rect(); private ObjectAnimator mTopAnimator = null; private ObjectAnimator mBottomAnimator = null; private ExpandableView mFirstVisibleChild; @@ -277,7 +277,6 @@ public class NotificationSection { } } } - top = Math.max(minTopPosition, top); ExpandableView lastView = getLastVisibleChild(); if (lastView != null) { float finalTranslationY = ViewState.getFinalTranslationY(lastView); 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 5fbaa515d5d7..e37750194368 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 @@ -135,7 +135,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private static final boolean SPEW = Log.isLoggable(TAG, Log.VERBOSE); // Delay in milli-seconds before shade closes for clear all. - private final int DELAY_BEFORE_SHADE_CLOSE = 200; + private static final int DELAY_BEFORE_SHADE_CLOSE = 200; private boolean mShadeNeedsToClose = false; private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f; @@ -152,7 +152,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1; private boolean mKeyguardBypassEnabled; - private ExpandHelper mExpandHelper; + private final ExpandHelper mExpandHelper; private NotificationSwipeHelper mSwipeHelper; private int mCurrentStackHeight = Integer.MAX_VALUE; private final Paint mBackgroundPaint = new Paint(); @@ -165,12 +165,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private VelocityTracker mVelocityTracker; private OverScroller mScroller; - /** Last Y position reported by {@link #mScroller}, used to calculate scroll delta. */ - private int mLastScrollerY; - /** - * True if the max position was set to a known position on the last call to {@link #mScroller}. - */ - private boolean mIsScrollerBoundSet; + private Runnable mFinishScrollingCallback; private int mTouchSlop; private float mSlopMultiplier; @@ -194,7 +189,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private int mContentHeight; private float mIntrinsicContentHeight; - private int mCollapsedSize; private int mPaddingBetweenElements; private int mMaxTopPadding; private int mTopPadding; @@ -210,15 +204,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private final StackScrollAlgorithm mStackScrollAlgorithm; private final AmbientState mAmbientState; - private GroupMembershipManager mGroupMembershipManager; - private GroupExpansionManager mGroupExpansionManager; - private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>(); - private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>(); - private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>(); - private ArrayList<ExpandableView> mChildrenChangingPositions = new ArrayList<>(); - private HashSet<View> mFromMoreCardAdditions = new HashSet<>(); - private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>(); - private ArrayList<View> mSwipedOutViews = new ArrayList<>(); + private final GroupMembershipManager mGroupMembershipManager; + private final GroupExpansionManager mGroupExpansionManager; + private final HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>(); + private final ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>(); + private final ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>(); + private final ArrayList<ExpandableView> mChildrenChangingPositions = new ArrayList<>(); + private final HashSet<View> mFromMoreCardAdditions = new HashSet<>(); + private final ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>(); + private final ArrayList<View> mSwipedOutViews = new ArrayList<>(); private NotificationStackSizeCalculator mNotificationStackSizeCalculator; private final StackStateAnimator mStateAnimator = new StackStateAnimator(this); private boolean mAnimationsEnabled; @@ -296,7 +290,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private boolean mDisallowDismissInThisMotion; private boolean mDisallowScrollingInThisMotion; private long mGoToFullShadeDelay; - private ViewTreeObserver.OnPreDrawListener mChildrenUpdater + private final ViewTreeObserver.OnPreDrawListener mChildrenUpdater = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { @@ -309,17 +303,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable }; private NotificationStackScrollLogger mLogger; private CentralSurfaces mCentralSurfaces; - private int[] mTempInt2 = new int[2]; + private final int[] mTempInt2 = new int[2]; private boolean mGenerateChildOrderChangedEvent; - private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>(); - private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>(); - private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations + private final HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>(); + private final HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>(); + private final HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations = new HashSet<>(); - private boolean mTrackingHeadsUp; private boolean mForceNoOverlappingRendering; private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>(); private boolean mAnimationRunning; - private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater + private final ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { @@ -327,21 +320,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return true; } }; - private NotificationSection[] mSections; + private final NotificationSection[] mSections; private boolean mAnimateNextBackgroundTop; private boolean mAnimateNextBackgroundBottom; private boolean mAnimateNextSectionBoundsChange; private int mBgColor; private float mDimAmount; private ValueAnimator mDimAnimator; - private ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>(); + private final ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>(); private final Animator.AnimatorListener mDimEndListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mDimAnimator = null; } }; - private ValueAnimator.AnimatorUpdateListener mDimUpdateListener + private final ValueAnimator.AnimatorUpdateListener mDimUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @Override @@ -351,29 +344,23 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable }; protected ViewGroup mQsHeader; // Rect of QsHeader. Kept as a field just to avoid creating a new one each time. - private Rect mQsHeaderBound = new Rect(); + private final Rect mQsHeaderBound = new Rect(); private boolean mContinuousShadowUpdate; private boolean mContinuousBackgroundUpdate; - private ViewTreeObserver.OnPreDrawListener mShadowUpdater + private final ViewTreeObserver.OnPreDrawListener mShadowUpdater = () -> { updateViewShadows(); return true; }; - private ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> { + private final ViewTreeObserver.OnPreDrawListener mBackgroundUpdater = () -> { updateBackground(); return true; }; - private Comparator<ExpandableView> mViewPositionComparator = (view, otherView) -> { + private final Comparator<ExpandableView> mViewPositionComparator = (view, otherView) -> { float endY = view.getTranslationY() + view.getActualHeight(); float otherEndY = otherView.getTranslationY() + otherView.getActualHeight(); - if (endY < otherEndY) { - return -1; - } else if (endY > otherEndY) { - return 1; - } else { - // The two notifications end at the same location - return 0; - } + // Return zero when the two notifications end at the same location + return Float.compare(endY, otherEndY); }; private final ViewOutlineProvider mOutlineProvider = new ViewOutlineProvider() { @Override @@ -435,16 +422,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private int mUpcomingStatusBarState; private int mCachedBackgroundColor; private boolean mHeadsUpGoingAwayAnimationsAllowed = true; - private Runnable mReflingAndAnimateScroll = () -> { - animateScroll(); - }; + private final Runnable mReflingAndAnimateScroll = this::animateScroll; private int mCornerRadius; private int mMinimumPaddings; private int mQsTilePadding; private boolean mSkinnyNotifsInLandscape; private int mSidePaddings; private final Rect mBackgroundAnimationRect = new Rect(); - private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>(); + private final ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>(); private int mHeadsUpInset; /** @@ -479,8 +464,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private int mWaterfallTopInset; private NotificationStackScrollLayoutController mController; - private boolean mKeyguardMediaControllorVisible; - /** * The clip path used to clip the view in a rounded way. */ @@ -501,7 +484,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private int mRoundedRectClippingTop; private int mRoundedRectClippingBottom; private int mRoundedRectClippingRight; - private float[] mBgCornerRadii = new float[8]; + private final float[] mBgCornerRadii = new float[8]; /** * Whether stackY should be animated in case the view is getting shorter than the scroll @@ -527,7 +510,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable /** * Corner radii of the launched notification if it's clipped */ - private float[] mLaunchedNotificationRadii = new float[8]; + private final float[] mLaunchedNotificationRadii = new float[8]; /** * The notification that is being launched currently. @@ -779,7 +762,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable y = getLayoutHeight(); drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "getLayoutHeight() = " + y); - y = (int) mMaxLayoutHeight; + y = mMaxLayoutHeight; drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = " + y); // The space between mTopPadding and mKeyguardBottomPadding determines the available space @@ -997,7 +980,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mOverflingDistance = configuration.getScaledOverflingDistance(); Resources res = context.getResources(); - mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height); mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height); mStackScrollAlgorithm.initView(context); mAmbientState.reload(context); @@ -1256,12 +1238,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private void clampScrollPosition() { int scrollRange = getScrollRange(); if (scrollRange < mOwnScrollY && !mAmbientState.isClearAllInProgress()) { - boolean animateStackY = false; - if (scrollRange < getScrollAmountToScrollBoundary() - && mAnimateStackYForContentHeightChange) { - // if the scroll boundary updates the position of the stack, - animateStackY = true; - } + // if the scroll boundary updates the position of the stack, + boolean animateStackY = scrollRange < getScrollAmountToScrollBoundary() + && mAnimateStackYForContentHeightChange; setOwnScrollY(scrollRange, animateStackY); } } @@ -1504,7 +1483,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } if (mAmbientState.isHiddenAtAll()) { - clipToOutline = false; invalidateOutline(); if (isFullyHidden()) { setClipBounds(null); @@ -1782,7 +1760,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private Runnable mReclamp = new Runnable() { + private final Runnable mReclamp = new Runnable() { @Override public void run() { int range = getScrollRange(); @@ -3084,11 +3062,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable int currentIndex = indexOfChild(child); if (currentIndex == -1) { - boolean isTransient = false; - if (child instanceof ExpandableNotificationRow - && child.getTransientContainer() != null) { - isTransient = true; - } + boolean isTransient = child instanceof ExpandableNotificationRow + && child.getTransientContainer() != null; Log.e(TAG, "Attempting to re-position " + (isTransient ? "transient" : "") + " view {" @@ -3149,7 +3124,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private void generateHeadsUpAnimationEvents() { for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) { ExpandableNotificationRow row = eventPair.first; - String key = row.getEntry().getKey(); boolean isHeadsUp = eventPair.second; if (isHeadsUp != row.isHeadsUp()) { // For cases where we have a heads up showing and appearing again we shouldn't @@ -3212,10 +3186,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.COORDINATOR) private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) { - if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) { - return false; - } - return true; + return viewState.yTranslation + viewState.height + >= mAmbientState.getMaxHeadsUpTranslation(); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -4790,7 +4762,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setTrackingHeadsUp(ExpandableNotificationRow row) { mAmbientState.setTrackedHeadsUpRow(row); - mTrackingHeadsUp = row != null; } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -6176,7 +6147,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() { + private final ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() { @Override public ExpandableView getChildAtPosition(float touchX, float touchY) { return NotificationStackScrollLayout.this.getChildAtPosition(touchX, touchY); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index 5d5918de3d9e..d2c2d58820bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -14,6 +14,9 @@ package com.android.systemui.qs; +import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import static com.android.systemui.statusbar.StatusBarState.SHADE; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertTrue; @@ -49,13 +52,13 @@ import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.media.MediaHost; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.customize.QSCustomizerController; import com.android.systemui.qs.dagger.QSFragmentComponent; import com.android.systemui.qs.external.TileServiceRequestController; import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; @@ -93,7 +96,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { @Mock private QSPanel.QSTileLayout mQsTileLayout; @Mock private QSPanel.QSTileLayout mQQsTileLayout; @Mock private QSAnimator mQSAnimator; - @Mock private StatusBarStateController mStatusBarStateController; + @Mock private SysuiStatusBarStateController mStatusBarStateController; @Mock private QSSquishinessController mSquishinessController; private View mQsFragmentView; @@ -158,7 +161,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { public void transitionToFullShade_onKeyguard_noBouncer_setsAlphaUsingLinearInterpolator() { QSFragment fragment = resumeAndGetFragment(); - setStatusBarState(StatusBarState.KEYGUARD); + setStatusBarState(KEYGUARD); when(mQSPanelController.isBouncerInTransit()).thenReturn(false); boolean isTransitioningToFullShade = true; float transitionProgress = 0.5f; @@ -174,7 +177,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { public void transitionToFullShade_onKeyguard_bouncerActive_setsAlphaUsingBouncerInterpolator() { QSFragment fragment = resumeAndGetFragment(); - setStatusBarState(StatusBarState.KEYGUARD); + setStatusBarState(KEYGUARD); when(mQSPanelController.isBouncerInTransit()).thenReturn(true); boolean isTransitioningToFullShade = true; float transitionProgress = 0.5f; @@ -262,6 +265,27 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { } @Test + public void setQsExpansion_inSplitShade_whenTransitioningToKeyguard_setsAlphaBasedOnShadeTransitionProgress() { + QSFragment fragment = resumeAndGetFragment(); + enableSplitShade(); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD); + boolean isTransitioningToFullShade = false; + float transitionProgress = 0; + float squishinessFraction = 0f; + + fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress, + squishinessFraction); + + // trigger alpha refresh with non-zero expansion and fraction values + fragment.setQsExpansion(/* expansion= */ 1, /* panelExpansionFraction= */1, + /* proposedTranslation= */ 0, /* squishinessFraction= */ 1); + + // alpha should follow lockscreen to shade progress, not panel expansion fraction + assertThat(mQsFragmentView.getAlpha()).isEqualTo(transitionProgress); + } + + @Test public void getQsMinExpansionHeight_notInSplitShade_returnsHeaderHeight() { QSFragment fragment = resumeAndGetFragment(); disableSplitShade(); @@ -402,6 +426,19 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { verify(mQSPanelController).setListening(eq(true), anyBoolean()); } + @Test + public void passCorrectExpansionState_inSplitShade() { + QSFragment fragment = resumeAndGetFragment(); + enableSplitShade(); + clearInvocations(mQSPanelController); + + fragment.setExpanded(true); + verify(mQSPanelController).setExpanded(true); + + fragment.setExpanded(false); + verify(mQSPanelController).setExpanded(false); + } + @Override protected Fragment instantiate(Context context, String className, Bundle arguments) { MockitoAnnotations.initMocks(this); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index b40d5ac69d7b..0c60d3c72c2a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -23,6 +23,9 @@ import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; +import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_CLOSED; +import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPEN; +import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPENING; import static com.google.common.truth.Truth.assertThat; @@ -1249,14 +1252,10 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { @Test public void testQsToBeImmediatelyExpandedWhenOpeningPanelInSplitShade() { enableSplitShade(/* enabled= */ true); - // set panel state to CLOSED - mPanelExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 0, - /* expanded= */ false, /* tracking= */ false, /* dragDownPxAmount= */ 0); + mPanelExpansionStateManager.updateState(STATE_CLOSED); assertThat(mNotificationPanelViewController.mQsExpandImmediate).isFalse(); - // change panel state to OPENING - mPanelExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 0.5f, - /* expanded= */ true, /* tracking= */ true, /* dragDownPxAmount= */ 100); + mPanelExpansionStateManager.updateState(STATE_OPENING); assertThat(mNotificationPanelViewController.mQsExpandImmediate).isTrue(); } @@ -1264,15 +1263,23 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { @Test public void testQsNotToBeImmediatelyExpandedWhenGoingFromUnlockedToLocked() { enableSplitShade(/* enabled= */ true); - // set panel state to CLOSED - mPanelExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 0, - /* expanded= */ false, /* tracking= */ false, /* dragDownPxAmount= */ 0); + mPanelExpansionStateManager.updateState(STATE_CLOSED); - // go to lockscreen, which also sets fraction to 1.0f and makes shade "expanded" mStatusBarStateController.setState(KEYGUARD); - mPanelExpansionStateManager.onPanelExpansionChanged(/* fraction= */ 1, - /* expanded= */ true, /* tracking= */ true, /* dragDownPxAmount= */ 0); + // going to lockscreen would trigger STATE_OPENING + mPanelExpansionStateManager.updateState(STATE_OPENING); + + assertThat(mNotificationPanelViewController.mQsExpandImmediate).isFalse(); + } + + @Test + public void testQsImmediateResetsWhenPanelOpensOrCloses() { + mNotificationPanelViewController.mQsExpandImmediate = true; + mPanelExpansionStateManager.updateState(STATE_OPEN); + assertThat(mNotificationPanelViewController.mQsExpandImmediate).isFalse(); + mNotificationPanelViewController.mQsExpandImmediate = true; + mPanelExpansionStateManager.updateState(STATE_CLOSED); assertThat(mNotificationPanelViewController.mQsExpandImmediate).isFalse(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index 2ee31265ff7b..2970807afb36 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -337,6 +337,40 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { } @Test + fun testOnEntryUpdated_toAlert() { + // GIVEN that an entry is posted that should not heads up + setShouldHeadsUp(mEntry, false) + mCollectionListener.onEntryAdded(mEntry) + + // WHEN it's updated to heads up + setShouldHeadsUp(mEntry) + mCollectionListener.onEntryUpdated(mEntry) + mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry)) + mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry)) + + // THEN the notification alerts + finishBind(mEntry) + verify(mHeadsUpManager).showNotification(mEntry) + } + + @Test + fun testOnEntryUpdated_toNotAlert() { + // GIVEN that an entry is posted that should heads up + setShouldHeadsUp(mEntry) + mCollectionListener.onEntryAdded(mEntry) + + // WHEN it's updated to not heads up + setShouldHeadsUp(mEntry, false) + mCollectionListener.onEntryUpdated(mEntry) + mBeforeTransformGroupsListener.onBeforeTransformGroups(listOf(mEntry)) + mBeforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(mEntry)) + + // THEN the notification is never bound or shown + verify(mHeadsUpViewBinder, never()).bindHeadsUpView(any(), any()) + verify(mHeadsUpManager, never()).showNotification(any()) + } + + @Test fun testOnEntryRemovedRemovesHeadsUpNotification() { // GIVEN the current HUN is mEntry addHUN(mEntry) diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java index dc9144a68e4c..0ab5a8afe907 100644 --- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java @@ -116,6 +116,7 @@ class AssociationRequestsProcessor { private static final String EXTRA_APPLICATION_CALLBACK = "application_callback"; private static final String EXTRA_ASSOCIATION_REQUEST = "association_request"; private static final String EXTRA_RESULT_RECEIVER = "result_receiver"; + private static final String EXTRA_FORCE_CANCEL_CONFIRMATION = "cancel_confirmation"; // AssociationRequestsProcessor -> UI private static final int RESULT_CODE_ASSOCIATION_CREATED = 0; @@ -195,21 +196,7 @@ class AssociationRequestsProcessor { intent.putExtras(extras); // 2b.3. Create a PendingIntent. - final PendingIntent pendingIntent; - final long token = Binder.clearCallingIdentity(); - try { - // Using uid of the application that will own the association (usually the same - // application that sent the request) allows us to have multiple "pending" association - // requests at the same time. - // If the application already has a pending association request, that PendingIntent - // will be cancelled. - pendingIntent = PendingIntent.getActivityAsUser( - mContext, /*requestCode */ packageUid, intent, - FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE, - /* options= */ null, UserHandle.CURRENT); - } finally { - Binder.restoreCallingIdentity(token); - } + final PendingIntent pendingIntent = createPendingIntent(packageUid, intent); // 2b.4. Send the PendingIntent back to the app. try { @@ -217,6 +204,27 @@ class AssociationRequestsProcessor { } catch (RemoteException ignore) { } } + /** + * Process another AssociationRequest in CompanionDeviceActivity to cancel current dialog. + */ + PendingIntent buildAssociationCancellationIntent(@NonNull String packageName, + @UserIdInt int userId) { + requireNonNull(packageName, "Package name MUST NOT be null"); + + enforceUsesCompanionDeviceFeature(mContext, userId, packageName); + + final int packageUid = mPackageManager.getPackageUid(packageName, 0, userId); + + final Bundle extras = new Bundle(); + extras.putBoolean(EXTRA_FORCE_CANCEL_CONFIRMATION, true); + + final Intent intent = new Intent(); + intent.setComponent(ASSOCIATION_REQUEST_APPROVAL_ACTIVITY); + intent.putExtras(extras); + + return createPendingIntent(packageUid, intent); + } + private void processAssociationRequestApproval(@NonNull AssociationRequest request, @NonNull IAssociationRequestCallback callback, @NonNull ResultReceiver resultReceiver, @Nullable MacAddress macAddress) { @@ -286,6 +294,27 @@ class AssociationRequestsProcessor { return !isRoleHolder; } + private PendingIntent createPendingIntent(int packageUid, Intent intent) { + final PendingIntent pendingIntent; + final long token = Binder.clearCallingIdentity(); + + // Using uid of the application that will own the association (usually the same + // application that sent the request) allows us to have multiple "pending" association + // requests at the same time. + // If the application already has a pending association request, that PendingIntent + // will be cancelled except application wants to cancel the request by the system. + try { + pendingIntent = PendingIntent.getActivityAsUser( + mContext, /*requestCode */ packageUid, intent, + FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE, + /* options= */ null, UserHandle.CURRENT); + } finally { + Binder.restoreCallingIdentity(token); + } + + return pendingIntent; + } + private final ResultReceiver mOnRequestConfirmationReceiver = new ResultReceiver(Handler.getMain()) { @Override diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 85d314042496..56a5a8f829b7 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -549,6 +549,18 @@ public class CompanionDeviceManagerService extends SystemService { } @Override + public PendingIntent buildAssociationCancellationIntent(String packageName, + int userId) throws RemoteException { + Slog.i(TAG, "buildAssociationCancellationIntent() " + + "package=u" + userId + "/" + packageName); + enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName, + "build association cancellation intent"); + + return mAssociationRequestsProcessor.buildAssociationCancellationIntent( + packageName, userId); + } + + @Override public List<AssociationInfo> getAssociations(String packageName, int userId) { enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName, "get associations"); diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java index b2abdbd144b8..67aa2b9a89bb 100644 --- a/services/core/java/com/android/server/AlarmManagerInternal.java +++ b/services/core/java/com/android/server/AlarmManagerInternal.java @@ -16,8 +16,10 @@ package com.android.server; +import android.annotation.CurrentTimeMillisLong; import android.app.PendingIntent; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.SystemTimeZone.TimeZoneConfidence; public interface AlarmManagerInternal { @@ -59,4 +61,15 @@ public interface AlarmManagerInternal { * for details */ void setTimeZone(String tzId, @TimeZoneConfidence int confidence); + + /** + * Sets the device's current time and time confidence. + * + * @param unixEpochTimeMillis the time + * @param confidence the confidence that {@code unixEpochTimeMillis} is correct, see {@link + * TimeConfidence} for details + * @param logMsg the reason the time is being changed, for bug report logging + */ + void setTime(@CurrentTimeMillisLong long unixEpochTimeMillis, @TimeConfidence int confidence, + String logMsg); } diff --git a/services/core/java/com/android/server/SystemClockTime.java b/services/core/java/com/android/server/SystemClockTime.java new file mode 100644 index 000000000000..46fbbb290ed2 --- /dev/null +++ b/services/core/java/com/android/server/SystemClockTime.java @@ -0,0 +1,174 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server; + +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.CurrentTimeMillisLong; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Build; +import android.os.Environment; +import android.os.SystemProperties; +import android.util.LocalLog; +import android.util.Slog; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * A set of static methods that encapsulate knowledge of how the system clock time and associated + * metadata are stored on Android. + */ +public final class SystemClockTime { + + private static final String TAG = "SystemClockTime"; + + /** + * A log that records the decisions / decision metadata that affected the device's system clock + * time. This is logged in bug reports to assist with debugging issues with time. + */ + @NonNull + private static final LocalLog sTimeDebugLog = + new LocalLog(30, false /* useLocalTimestamps */); + + + /** + * An annotation that indicates a "time confidence" value is expected. + * + * <p>The confidence indicates whether the time is expected to be correct. The confidence can be + * upgraded or downgraded over time. It can be used to decide whether a user could / should be + * asked to confirm the time. For example, during device set up low confidence would describe a + * time that has been initialized by default. The user may then be asked to confirm the time, + * moving it to a high confidence. + */ + @Retention(SOURCE) + @Target(TYPE_USE) + @IntDef(prefix = "TIME_CONFIDENCE_", + value = { TIME_CONFIDENCE_LOW, TIME_CONFIDENCE_HIGH }) + public @interface TimeConfidence { + } + + /** Used when confidence is low and would (ideally) be confirmed by a user. */ + public static final @TimeConfidence int TIME_CONFIDENCE_LOW = 0; + + /** + * Used when confidence in the time is high and does not need to be confirmed by a user. + */ + public static final @TimeConfidence int TIME_CONFIDENCE_HIGH = 100; + + /** + * The confidence in the current time. Android's time confidence is held in memory because RTC + * hardware can forget / corrupt the time while the device is powered off. Therefore, on boot + * we can't assume the time is good, and so default it to "low" confidence until it is confirmed + * or explicitly set. + */ + private static @TimeConfidence int sTimeConfidence = TIME_CONFIDENCE_LOW; + + private static final long sNativeData = init(); + + private SystemClockTime() { + } + + /** + * Sets the system clock time to a reasonable lower bound. Used during boot-up to ensure the + * device has a time that is better than a default like 1970-01-01. + */ + public static void initializeIfRequired() { + // Use the most recent of Build.TIME, the root file system's timestamp, and the + // value of the ro.build.date.utc system property (which is in seconds). + final long systemBuildTime = Long.max( + 1000L * SystemProperties.getLong("ro.build.date.utc", -1L), + Long.max(Environment.getRootDirectory().lastModified(), Build.TIME)); + long currentTimeMillis = getCurrentTimeMillis(); + if (currentTimeMillis < systemBuildTime) { + String logMsg = "Current time only " + currentTimeMillis + + ", advancing to build time " + systemBuildTime; + Slog.i(TAG, logMsg); + setTimeAndConfidence(systemBuildTime, TIME_CONFIDENCE_LOW, logMsg); + } + } + + /** + * Sets the system clock time and confidence. See also {@link #setConfidence(int, String)} for + * an alternative that only sets the confidence. + * + * @param unixEpochMillis the time to set + * @param confidence the confidence in {@code unixEpochMillis}. See {@link TimeConfidence} for + * details. + * @param logMsg a log message that can be included in bug reports that explains the update + */ + public static void setTimeAndConfidence( + @CurrentTimeMillisLong long unixEpochMillis, int confidence, @NonNull String logMsg) { + synchronized (SystemClockTime.class) { + setTime(sNativeData, unixEpochMillis); + sTimeConfidence = confidence; + sTimeDebugLog.log(logMsg); + } + } + + /** + * Sets the system clock confidence. See also {@link #setTimeAndConfidence(long, int, String)} + * for an alternative that sets the time and confidence. + * + * @param confidence the confidence in the system clock time. See {@link TimeConfidence} for + * details. + * @param logMsg a log message that can be included in bug reports that explains the update + */ + public static void setConfidence(@TimeConfidence int confidence, @NonNull String logMsg) { + synchronized (SystemClockTime.class) { + sTimeConfidence = confidence; + sTimeDebugLog.log(logMsg); + } + } + + /** + * Returns the system clock time. The same as {@link System#currentTimeMillis()}. + */ + private static @CurrentTimeMillisLong long getCurrentTimeMillis() { + return System.currentTimeMillis(); + } + + /** + * Returns the system clock confidence. See {@link TimeConfidence} for details. + */ + public static @TimeConfidence int getTimeConfidence() { + synchronized (SystemClockTime.class) { + return sTimeConfidence; + } + } + + /** + * Adds an entry to the system time debug log that is included in bug reports. This method is + * intended to be used to record event that may lead to a time change, e.g. config or mode + * changes. + */ + public static void addDebugLogEntry(@NonNull String logMsg) { + sTimeDebugLog.log(logMsg); + } + + /** + * Dumps information about recent time / confidence changes to the supplied writer. + */ + public static void dump(PrintWriter writer) { + sTimeDebugLog.dump(writer); + } + + private static native long init(); + private static native int setTime(long nativeData, @CurrentTimeMillisLong long millis); +} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 481672e64d06..7cedf9c543f0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -247,6 +247,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionInfo; import android.content.pm.PermissionMethod; +import android.content.pm.PermissionName; import android.content.pm.ProcessInfo; import android.content.pm.ProviderInfo; import android.content.pm.ProviderInfoList; @@ -5987,8 +5988,9 @@ public class ActivityManagerService extends IActivityManager.Stub * provided non-{@code null} {@code permission} before. Otherwise calls into * {@link ActivityManager#checkComponentPermission(String, int, int, boolean)}. */ + @PackageManager.PermissionResult @PermissionMethod - public static int checkComponentPermission(String permission, int pid, int uid, + public static int checkComponentPermission(@PermissionName String permission, int pid, int uid, int owningUid, boolean exported) { if (pid == MY_PID) { return PackageManager.PERMISSION_GRANTED; @@ -6034,8 +6036,9 @@ public class ActivityManagerService extends IActivityManager.Stub * This can be called with or without the global lock held. */ @Override + @PackageManager.PermissionResult @PermissionMethod - public int checkPermission(String permission, int pid, int uid) { + public int checkPermission(@PermissionName String permission, int pid, int uid) { if (permission == null) { return PackageManager.PERMISSION_DENIED; } @@ -6046,8 +6049,9 @@ public class ActivityManagerService extends IActivityManager.Stub * Binder IPC calls go through the public entry point. * This can be called with or without the global lock held. */ + @PackageManager.PermissionResult @PermissionMethod - int checkCallingPermission(String permission) { + int checkCallingPermission(@PermissionName String permission) { return checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid()); @@ -6057,7 +6061,7 @@ public class ActivityManagerService extends IActivityManager.Stub * This can be called with or without the global lock held. */ @PermissionMethod - void enforceCallingPermission(String permission, String func) { + void enforceCallingPermission(@PermissionName String permission, String func) { if (checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) { return; @@ -6074,7 +6078,6 @@ public class ActivityManagerService extends IActivityManager.Stub /** * This can be called with or without the global lock held. */ - @PermissionMethod private void enforceCallingHasAtLeastOnePermission(String func, String... permissions) { for (String permission : permissions) { if (checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) { @@ -6093,7 +6096,8 @@ public class ActivityManagerService extends IActivityManager.Stub /** * This can be called with or without the global lock held. */ - void enforcePermission(String permission, int pid, int uid, String func) { + @PermissionMethod + void enforcePermission(@PermissionName String permission, int pid, int uid, String func) { if (checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) { return; } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index a41a311bd643..d9d29d650f03 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -831,6 +831,26 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override + @EnforcePermission(BATTERY_STATS) + public long computeBatteryScreenOffRealtimeMs() { + synchronized (mStats) { + final long curTimeUs = SystemClock.elapsedRealtimeNanos() / 1000; + long timeUs = mStats.computeBatteryScreenOffRealtime(curTimeUs, + BatteryStats.STATS_SINCE_CHARGED); + return timeUs / 1000; + } + } + + @Override + @EnforcePermission(BATTERY_STATS) + public long getScreenOffDischargeMah() { + synchronized (mStats) { + long dischargeUah = mStats.getUahDischargeScreenOff(BatteryStats.STATS_SINCE_CHARGED); + return dischargeUah / 1000; + } + } + + @Override @EnforcePermission(UPDATE_DEVICE_STATS) public void noteEvent(final int code, final String name, final int uid) { if (name == null) { diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthResult.java b/services/core/java/com/android/server/biometrics/sensors/AuthResult.java new file mode 100644 index 000000000000..c0ebf6b8b634 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/AuthResult.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.hardware.biometrics.BiometricManager; + +class AuthResult { + static final int FAILED = 0; + static final int LOCKED_OUT = 1; + static final int AUTHENTICATED = 2; + private final int mStatus; + private final int mBiometricStrength; + + AuthResult(int status, @BiometricManager.Authenticators.Types int strength) { + mStatus = status; + mBiometricStrength = strength; + } + + int getStatus() { + return mStatus; + } + + int getBiometricStrength() { + return mBiometricStrength; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java new file mode 100644 index 000000000000..6d00c3fc15ea --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.hardware.biometrics.BiometricManager.Authenticators; + +import java.util.ArrayList; +import java.util.List; + +/** + * A class that takes in a series of authentication attempts (successes, failures, lockouts) + * across different biometric strengths (convenience, weak, strong) and returns a single AuthResult. + * + * The AuthResult will be the strongest biometric operation that occurred amongst all reported + * operations, and if multiple such operations exist, it will favor a successful authentication. + */ +class AuthResultCoordinator { + + private static final String TAG = "AuthResultCoordinator"; + private final List<AuthResult> mOperations; + + AuthResultCoordinator() { + mOperations = new ArrayList<>(); + } + + /** + * Adds auth success for a given strength to the current operation list. + */ + void authenticatedFor(@Authenticators.Types int strength) { + mOperations.add(new AuthResult(AuthResult.AUTHENTICATED, strength)); + } + + /** + * Adds auth ended for a given strength to the current operation list. + */ + void authEndedFor(@Authenticators.Types int strength) { + mOperations.add(new AuthResult(AuthResult.FAILED, strength)); + } + + /** + * Adds a lock out of a given strength to the current operation list. + */ + void lockedOutFor(@Authenticators.Types int strength) { + mOperations.add(new AuthResult(AuthResult.LOCKED_OUT, strength)); + } + + /** + * Obtains an auth result & strength from a current set of biometric operations. + */ + AuthResult getResult() { + AuthResult result = new AuthResult(AuthResult.FAILED, Authenticators.BIOMETRIC_CONVENIENCE); + return mOperations.stream().filter( + (element) -> element.getStatus() != AuthResult.FAILED).reduce(result, + ((curr, next) -> { + int strengthCompare = curr.getBiometricStrength() - next.getBiometricStrength(); + if (strengthCompare < 0) { + return curr; + } else if (strengthCompare == 0) { + // Equal level of strength, favor authentication. + if (curr.getStatus() == AuthResult.AUTHENTICATED) { + return curr; + } else { + // Either next is Authenticated, or it is not, either way return this + // one. + return next; + } + } else { + // curr is a weaker biometric + return next; + } + })); + } + + void resetState() { + mOperations.clear(); + } +} + + diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java new file mode 100644 index 000000000000..13840ff389d9 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.hardware.biometrics.BiometricManager.Authenticators; +import android.util.Slog; + +import java.util.HashSet; +import java.util.Set; + +/** + * Coordinates lockout counter enforcement for all types of biometric strengths across all users. + * + * This class is not thread-safe. In general, all calls to this class should be made on the same + * handler to ensure no collisions. + */ +class AuthSessionCoordinator implements AuthSessionListener { + private static final String TAG = "AuthSessionCoordinator"; + + private final Set<Integer> mAuthOperations; + + private int mUserId; + private boolean mIsAuthenticating; + private AuthResultCoordinator mAuthResultCoordinator; + private MultiBiometricLockoutState mMultiBiometricLockoutState; + + AuthSessionCoordinator() { + mAuthOperations = new HashSet<>(); + mAuthResultCoordinator = new AuthResultCoordinator(); + mMultiBiometricLockoutState = new MultiBiometricLockoutState(); + } + + /** + * A Call indicating that an auth session has started + */ + void onAuthSessionStarted(int userId) { + mAuthOperations.clear(); + mUserId = userId; + mIsAuthenticating = true; + mAuthResultCoordinator.resetState(); + } + + /** + * Ends the current auth session and updates the lockout state. + * + * This can happen two ways. + * 1. Manually calling this API + * 2. If authStartedFor() was called, and all authentication attempts finish. + */ + void endAuthSession() { + if (mIsAuthenticating) { + mAuthOperations.clear(); + AuthResult res = + mAuthResultCoordinator.getResult(); + if (res.getStatus() == AuthResult.AUTHENTICATED) { + mMultiBiometricLockoutState.onUserUnlocked(mUserId, res.getBiometricStrength()); + } else if (res.getStatus() == AuthResult.LOCKED_OUT) { + mMultiBiometricLockoutState.onUserLocked(mUserId, res.getBiometricStrength()); + } + mAuthResultCoordinator.resetState(); + mIsAuthenticating = false; + } + } + + /** + * @return true if a user can authenticate with a given strength. + */ + boolean getCanAuthFor(int userId, @Authenticators.Types int strength) { + return mMultiBiometricLockoutState.canUserAuthenticate(userId, strength); + } + + @Override + public void authStartedFor(int userId, int sensorId) { + if (!mIsAuthenticating) { + onAuthSessionStarted(userId); + } + + if (mAuthOperations.contains(sensorId)) { + Slog.e(TAG, "Error, authStartedFor(" + sensorId + ") without being finished"); + return; + } + + if (mUserId != userId) { + Slog.e(TAG, "Error authStartedFor(" + userId + ") Incorrect userId, expected" + mUserId + + ", ignoring..."); + return; + } + + mAuthOperations.add(sensorId); + } + + @Override + public void authenticatedFor(int userId, @Authenticators.Types int biometricStrength, + int sensorId) { + mAuthResultCoordinator.authenticatedFor(biometricStrength); + attemptToFinish(userId, sensorId, + "authenticatedFor(userId=" + userId + ", biometricStrength=" + biometricStrength + + ", sensorId=" + sensorId + ""); + } + + @Override + public void lockedOutFor(int userId, @Authenticators.Types int biometricStrength, + int sensorId) { + mAuthResultCoordinator.lockedOutFor(biometricStrength); + attemptToFinish(userId, sensorId, + "lockOutFor(userId=" + userId + ", biometricStrength=" + biometricStrength + + ", sensorId=" + sensorId + ""); + } + + @Override + public void authEndedFor(int userId, @Authenticators.Types int biometricStrength, + int sensorId) { + mAuthResultCoordinator.authEndedFor(biometricStrength); + attemptToFinish(userId, sensorId, + "authEndedFor(userId=" + userId + " ,biometricStrength=" + biometricStrength + + ", sensorId=" + sensorId); + } + + @Override + public void resetLockoutFor(int userId, @Authenticators.Types int biometricStrength) { + mMultiBiometricLockoutState.onUserUnlocked(userId, biometricStrength); + } + + private void attemptToFinish(int userId, int sensorId, String description) { + boolean didFail = false; + if (!mAuthOperations.contains(sensorId)) { + Slog.e(TAG, "Error unable to find auth operation : " + description); + didFail = true; + } + if (userId != mUserId) { + Slog.e(TAG, "Error mismatched userId, expected=" + mUserId + " for " + description); + didFail = true; + } + if (didFail) { + return; + } + mAuthOperations.remove(sensorId); + if (mIsAuthenticating && mAuthOperations.isEmpty()) { + endAuthSession(); + } + } + +} diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthSessionListener.java b/services/core/java/com/android/server/biometrics/sensors/AuthSessionListener.java new file mode 100644 index 000000000000..8b1f90af0234 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/AuthSessionListener.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import android.hardware.biometrics.BiometricManager.Authenticators; + +/** + * An interface that listens to authentication events. + */ +interface AuthSessionListener { + /** + * Indicates an auth operation has started for a given user and sensor. + */ + void authStartedFor(int userId, int sensorId); + + /** + * Indicates a successful authentication occurred for a sensor of a given strength. + */ + void authenticatedFor(int userId, @Authenticators.Types int biometricStrength, int sensorId); + + /** + * Indicates authentication ended for a sensor of a given strength. + */ + void authEndedFor(int userId, @Authenticators.Types int biometricStrength, int sensorId); + + /** + * Indicates a lockout occurred for a sensor of a given strength. + */ + void lockedOutFor(int userId, @Authenticators.Types int biometricStrength, int sensorId); + + /** + * Indicates that a reset lockout has happened for a given strength. + */ + void resetLockoutFor(int uerId, @Authenticators.Types int biometricStrength); +} diff --git a/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java b/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java new file mode 100644 index 000000000000..49dc817c7fd5 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import static android.hardware.biometrics.BiometricManager.Authenticators; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK; + +import android.util.ArrayMap; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class is used as a system to store the state of each + * {@link Authenticators.Types} status for every user. + */ +class MultiBiometricLockoutState { + + private static final String TAG = "MultiBiometricLockoutState"; + private static final Map<Integer, List<Integer>> PRECEDENCE; + + static { + Map<Integer, List<Integer>> precedence = new ArrayMap<>(); + precedence.put(Authenticators.BIOMETRIC_STRONG, + Arrays.asList(BIOMETRIC_STRONG, BIOMETRIC_WEAK, BIOMETRIC_CONVENIENCE)); + precedence.put(BIOMETRIC_WEAK, Arrays.asList(BIOMETRIC_WEAK, BIOMETRIC_CONVENIENCE)); + precedence.put(BIOMETRIC_CONVENIENCE, Arrays.asList(BIOMETRIC_CONVENIENCE)); + PRECEDENCE = Collections.unmodifiableMap(precedence); + } + + private final Map<Integer, Map<Integer, Boolean>> mCanUserAuthenticate; + + @VisibleForTesting + MultiBiometricLockoutState() { + mCanUserAuthenticate = new HashMap<>(); + } + + private static Map<Integer, Boolean> createLockedOutMap() { + Map<Integer, Boolean> lockOutMap = new HashMap<>(); + lockOutMap.put(BIOMETRIC_STRONG, false); + lockOutMap.put(BIOMETRIC_WEAK, false); + lockOutMap.put(BIOMETRIC_CONVENIENCE, false); + return lockOutMap; + } + + private Map<Integer, Boolean> getAuthMapForUser(int userId) { + if (!mCanUserAuthenticate.containsKey(userId)) { + mCanUserAuthenticate.put(userId, createLockedOutMap()); + } + return mCanUserAuthenticate.get(userId); + } + + /** + * Indicates a {@link Authenticators} has been locked for userId. + * + * @param userId The user. + * @param strength The strength of biometric that is requested to be locked. + */ + void onUserLocked(int userId, @Authenticators.Types int strength) { + Slog.d(TAG, "onUserLocked(userId=" + userId + ", strength=" + strength + ")"); + Map<Integer, Boolean> canUserAuthState = getAuthMapForUser(userId); + for (int strengthToLockout : PRECEDENCE.get(strength)) { + canUserAuthState.put(strengthToLockout, false); + } + } + + /** + * Indicates that a user has unlocked a {@link Authenticators} + * + * @param userId The user. + * @param strength The strength of biometric that is unlocked. + */ + void onUserUnlocked(int userId, @Authenticators.Types int strength) { + Slog.d(TAG, "onUserUnlocked(userId=" + userId + ", strength=" + strength + ")"); + Map<Integer, Boolean> canUserAuthState = getAuthMapForUser(userId); + for (int strengthToLockout : PRECEDENCE.get(strength)) { + canUserAuthState.put(strengthToLockout, true); + } + } + + /** + * Indicates if a user can perform an authentication operation with a given + * {@link Authenticators.Types} + * + * @param userId The user. + * @param strength The strength of biometric that is requested to authenticate. + * @return If a user can authenticate with a given biometric of this strength. + */ + boolean canUserAuthenticate(int userId, @Authenticators.Types int strength) { + final boolean canAuthenticate = getAuthMapForUser(userId).get(strength); + Slog.d(TAG, "canUserAuthenticate(userId=" + userId + ", strength=" + strength + ") =" + + canAuthenticate); + return canAuthenticate; + } +} diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java index 3e397468aa87..82436cc0890a 100644 --- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java +++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java @@ -221,14 +221,12 @@ final class IInputMethodInvoker { } @AnyThread - boolean updateEditorToolType(int toolType) { + void updateEditorToolType(@MotionEvent.ToolType int toolType) { try { mTarget.updateEditorToolType(toolType); } catch (RemoteException e) { logRemoteException(e); - return false; } - return true; } @AnyThread diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 31e0693ed47c..2b74eeb4bfe8 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -69,7 +69,6 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppGlobals; -import android.app.AppOpsManager; import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationManager; @@ -298,8 +297,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final IWindowManager mIWindowManager; private final SparseBooleanArray mLoggedDeniedGetInputMethodWindowVisibleHeightForUid = new SparseBooleanArray(0); - private final SparseBooleanArray mLoggedDeniedIsInputMethodPickerShownForTestForUid = - new SparseBooleanArray(0); final WindowManagerInternal mWindowManagerInternal; final PackageManagerInternal mPackageManagerInternal; final InputManagerInternal mInputManagerInternal; @@ -309,7 +306,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final boolean mHasFeature; private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap = new ArrayMap<>(); - private final AppOpsManager mAppOpsManager; private final UserManager mUserManager; private final UserManagerInternal mUserManagerInternal; private final InputMethodMenuController mMenuController; @@ -1479,7 +1475,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub public void onUidRemoved(int uid) { synchronized (ImfLock.class) { mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.delete(uid); - mLoggedDeniedIsInputMethodPickerShownForTestForUid.delete(uid); } } @@ -1737,7 +1732,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mInputMethodDeviceConfigs = new InputMethodDeviceConfigs(); mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy; mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); - mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mUserManager = mContext.getSystemService(UserManager.class); mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mAccessibilityManager = AccessibilityManager.getInstance(context); @@ -2523,7 +2517,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub null, null, null, selectedMethodId, getSequenceNumberLocked(), null, false); } - if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.mUid, + if (!InputMethodUtils.checkIfPackageBelongsToUid(mPackageManagerInternal, cs.mUid, editorInfo.packageName)) { Slog.e(TAG, "Rejecting this client as it reported an invalid package name." + " uid=" + cs.mUid + " package=" + editorInfo.packageName); @@ -2960,19 +2954,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub hideStatusBarIconLocked(); } else if (packageName != null) { if (DEBUG) Slog.d(TAG, "show a small icon for the input method"); - CharSequence contentDescription = null; + final PackageManager userAwarePackageManager = + getPackageManagerForUser(mContext, mSettings.getCurrentUserId()); + ApplicationInfo applicationInfo = null; try { - // Use PackageManager to load label - final PackageManager packageManager = mContext.getPackageManager(); - final ApplicationInfo applicationInfo = mIPackageManager - .getApplicationInfo(packageName, 0, mSettings.getCurrentUserId()); - if (applicationInfo != null) { - contentDescription = packageManager - .getApplicationLabel(applicationInfo); - } - } catch (RemoteException e) { - /* ignore */ + applicationInfo = userAwarePackageManager.getApplicationInfo(packageName, + PackageManager.ApplicationInfoFlags.of(0)); + } catch (PackageManager.NameNotFoundException e) { } + final CharSequence contentDescription = applicationInfo != null + ? userAwarePackageManager.getApplicationLabel(applicationInfo) + : null; if (mStatusBar != null) { mStatusBar.setIcon(mSlotIme, packageName, iconId, 0, contentDescription != null @@ -3962,7 +3954,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return false; } if (getCurIntentLocked() != null && InputMethodUtils.checkIfPackageBelongsToUid( - mAppOpsManager, + mPackageManagerInternal, uid, getCurIntentLocked().getComponent().getPackageName())) { return true; @@ -4002,19 +3994,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub /** * A test API for CTS to make sure that the input method menu is showing. */ + @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShownForTest() { - if (mContext.checkCallingPermission(android.Manifest.permission.TEST_INPUT_METHOD) - != PackageManager.PERMISSION_GRANTED) { - final int callingUid = Binder.getCallingUid(); - synchronized (ImfLock.class) { - if (!mLoggedDeniedIsInputMethodPickerShownForTestForUid.get(callingUid)) { - EventLog.writeEvent(0x534e4554, "237317525", callingUid, ""); - mLoggedDeniedIsInputMethodPickerShownForTestForUid.put(callingUid, true); - } - } - throw new SecurityException( - "isInputMethodPickerShownForTest requires TEST_INPUT_METHOD permission"); - } synchronized (ImfLock.class) { return mMenuController.isisInputMethodPickerShownForTestLocked(); } @@ -4224,8 +4205,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final int callingUid = Binder.getCallingUid(); final ComponentName imeComponentName = imeId != null ? ComponentName.unflattenFromString(imeId) : null; - if (imeComponentName == null || !InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, - callingUid, imeComponentName.getPackageName())) { + if (imeComponentName == null || !InputMethodUtils.checkIfPackageBelongsToUid( + mPackageManagerInternal, callingUid, imeComponentName.getPackageName())) { throw new SecurityException("Calling UID=" + callingUid + " does not belong to imeId=" + imeId); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index beeaa90a3832..054c994dddc5 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -20,12 +20,12 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserHandleAware; import android.annotation.UserIdInt; -import android.app.AppOpsManager; import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.res.Resources; import android.os.Binder; import android.os.Build; @@ -80,29 +80,6 @@ final class InputMethodUtils { } // ---------------------------------------------------------------------- - // Utilities for debug - static String getApiCallStack() { - String apiCallStack = ""; - try { - throw new RuntimeException(); - } catch (RuntimeException e) { - final StackTraceElement[] frames = e.getStackTrace(); - for (int j = 1; j < frames.length; ++j) { - final String tempCallStack = frames[j].toString(); - if (TextUtils.isEmpty(apiCallStack)) { - // Overwrite apiCallStack if it's empty - apiCallStack = tempCallStack; - } else if (tempCallStack.indexOf("Transact(") < 0) { - // Overwrite apiCallStack if it's not a binder call - apiCallStack = tempCallStack; - } else { - break; - } - } - } - return apiCallStack; - } - // ---------------------------------------------------------------------- static boolean canAddToLastInputMethod(InputMethodSubtype subtype) { if (subtype == null) return true; @@ -218,20 +195,20 @@ final class InputMethodUtils { /** * Returns true if a package name belongs to a UID. * - * <p>This is a simple wrapper of {@link AppOpsManager#checkPackage(int, String)}.</p> - * @param appOpsManager the {@link AppOpsManager} object to be used for the validation. + * <p>This is a simple wrapper of + * {@link PackageManagerInternal#getPackageUid(String, long, int)}.</p> + * @param packageManagerInternal the {@link PackageManagerInternal} object to be used for the + * validation. * @param uid the UID to be validated. * @param packageName the package name. * @return {@code true} if the package name belongs to the UID. */ - static boolean checkIfPackageBelongsToUid(AppOpsManager appOpsManager, - @UserIdInt int uid, String packageName) { - try { - appOpsManager.checkPackage(uid, packageName); - return true; - } catch (SecurityException e) { - return false; - } + static boolean checkIfPackageBelongsToUid(PackageManagerInternal packageManagerInternal, + int uid, String packageName) { + // PackageManagerInternal#getPackageUid() doesn't check MATCH_INSTANT/MATCH_APEX as of + // writing. So setting 0 should be fine. + return packageManagerInternal.getPackageUid(packageName, 0 /* flags */, + UserHandle.getUserId(uid)) == uid; } /** diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 62e145a66d0c..1dd796504ea3 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -58,6 +58,8 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.function.pooled.PooledLambda; +import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -87,6 +89,7 @@ class MediaRouter2ServiceImpl { private static final int PACKAGE_IMPORTANCE_FOR_DISCOVERY = IMPORTANCE_FOREGROUND_SERVICE; private final Context mContext; + private final UserManagerInternal mUserManagerInternal; private final Object mLock = new Object(); final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1); final ActivityManager mActivityManager; @@ -99,7 +102,7 @@ class MediaRouter2ServiceImpl { @GuardedBy("mLock") private final ArrayMap<IBinder, ManagerRecord> mAllManagerRecords = new ArrayMap<>(); @GuardedBy("mLock") - private int mCurrentUserId = -1; + private int mCurrentActiveUserId = -1; private final ActivityManager.OnUidImportanceListener mOnUidImportanceListener = (uid, importance) -> { @@ -125,12 +128,13 @@ class MediaRouter2ServiceImpl { } }; - MediaRouter2ServiceImpl(Context context) { + /* package */ MediaRouter2ServiceImpl(Context context) { mContext = context; mActivityManager = mContext.getSystemService(ActivityManager.class); mActivityManager.addOnUidImportanceListener(mOnUidImportanceListener, PACKAGE_IMPORTANCE_FOR_DISCOVERY); mPowerManager = mContext.getSystemService(PowerManager.class); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); IntentFilter screenOnOffIntentFilter = new IntentFilter(); screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON); @@ -608,7 +612,7 @@ class MediaRouter2ServiceImpl { synchronized (mLock) { pw.println(indent + "mNextRouterOrManagerId=" + mNextRouterOrManagerId.get()); - pw.println(indent + "mCurrentUserId=" + mCurrentUserId); + pw.println(indent + "mCurrentActiveUserId=" + mCurrentActiveUserId); pw.println(indent + "UserRecords:"); if (mUserRecords.size() > 0) { @@ -621,24 +625,23 @@ class MediaRouter2ServiceImpl { } } - // TODO(b/136703681): Review this is handling multi-user properly. - void switchUser(int userId) { + /* package */ void updateRunningUserAndProfiles(int newActiveUserId) { synchronized (mLock) { - if (mCurrentUserId != userId) { - final int oldUserId = mCurrentUserId; - mCurrentUserId = userId; // do this first - - UserRecord oldUser = mUserRecords.get(oldUserId); - if (oldUser != null) { - oldUser.mHandler.sendMessage( - obtainMessage(UserHandler::stop, oldUser.mHandler)); - disposeUserIfNeededLocked(oldUser); // since no longer current user - } - - UserRecord newUser = mUserRecords.get(userId); - if (newUser != null) { - newUser.mHandler.sendMessage( - obtainMessage(UserHandler::start, newUser.mHandler)); + if (mCurrentActiveUserId != newActiveUserId) { + mCurrentActiveUserId = newActiveUserId; + for (int i = 0; i < mUserRecords.size(); i++) { + int userId = mUserRecords.keyAt(i); + UserRecord userRecord = mUserRecords.valueAt(i); + if (isUserActiveLocked(userId)) { + // userId corresponds to the active user, or one of its profiles. We + // ensure the associated structures are initialized. + userRecord.mHandler.sendMessage( + obtainMessage(UserHandler::start, userRecord.mHandler)); + } else { + userRecord.mHandler.sendMessage( + obtainMessage(UserHandler::stop, userRecord.mHandler)); + disposeUserIfNeededLocked(userRecord); + } } } } @@ -656,11 +659,21 @@ class MediaRouter2ServiceImpl { } } + /** + * Returns {@code true} if the given {@code userId} corresponds to the active user or a profile + * of the active user, returns {@code false} otherwise. + */ + @GuardedBy("mLock") + private boolean isUserActiveLocked(int userId) { + return mUserManagerInternal.getProfileParentId(userId) == mCurrentActiveUserId; + } + //////////////////////////////////////////////////////////////// //// ***Locked methods related to MediaRouter2 //// - Should have @NonNull/@Nullable on all arguments //////////////////////////////////////////////////////////////// + @GuardedBy("mLock") private void registerRouter2Locked(@NonNull IMediaRouter2 router, int uid, int pid, @NonNull String packageName, int userId, boolean hasConfigureWifiDisplayPermission, boolean hasModifyAudioRoutingPermission) { @@ -688,6 +701,7 @@ class MediaRouter2ServiceImpl { userRecord.mHandler, routerRecord)); } + @GuardedBy("mLock") private void unregisterRouter2Locked(@NonNull IMediaRouter2 router, boolean died) { RouterRecord routerRecord = mAllRouterRecords.remove(router.asBinder()); if (routerRecord == null) { @@ -910,6 +924,7 @@ class MediaRouter2ServiceImpl { return sessionInfos; } + @GuardedBy("mLock") private void registerManagerLocked(@NonNull IMediaRouter2Manager manager, int uid, int pid, @NonNull String packageName, int userId) { final IBinder binder = manager.asBinder(); @@ -1145,13 +1160,14 @@ class MediaRouter2ServiceImpl { //// - Should have @NonNull/@Nullable on all arguments //////////////////////////////////////////////////////////// + @GuardedBy("mLock") private UserRecord getOrCreateUserRecordLocked(int userId) { UserRecord userRecord = mUserRecords.get(userId); if (userRecord == null) { userRecord = new UserRecord(userId); mUserRecords.put(userId, userRecord); userRecord.init(); - if (userId == mCurrentUserId) { + if (isUserActiveLocked(userId)) { userRecord.mHandler.sendMessage( obtainMessage(UserHandler::start, userRecord.mHandler)); } @@ -1159,12 +1175,13 @@ class MediaRouter2ServiceImpl { return userRecord; } + @GuardedBy("mLock") private void disposeUserIfNeededLocked(@NonNull UserRecord userRecord) { // If there are no records left and the user is no longer current then go ahead // and purge the user record and all of its associated state. If the user is current // then leave it alone since we might be connected to a route or want to query // the same route information again soon. - if (userRecord.mUserId != mCurrentUserId + if (!isUserActiveLocked(userRecord.mUserId) && userRecord.mRouterRecords.isEmpty() && userRecord.mManagerRecords.isEmpty()) { if (DEBUG) { @@ -2438,6 +2455,7 @@ class MediaRouter2ServiceImpl { pw.println(indent + "mUniqueRequestId=" + mUniqueRequestId); pw.println(indent + "mManagerRequestId=" + mManagerRequestId); mOldSession.dump(pw, indent); + mRoute.dump(pw, prefix); } } } diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index d7d0b42aa0a7..511026e71d23 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -63,7 +63,9 @@ import android.util.SparseArray; import android.util.TimeUtils; import com.android.internal.util.DumpUtils; +import com.android.server.LocalServices; import com.android.server.Watchdog; +import com.android.server.pm.UserManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -104,9 +106,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub // State guarded by mLock. private final Object mLock = new Object(); + private final UserManagerInternal mUserManagerInternal; private final SparseArray<UserRecord> mUserRecords = new SparseArray<>(); private final ArrayMap<IBinder, ClientRecord> mAllClientRecords = new ArrayMap<>(); - private int mCurrentUserId = -1; + private int mCurrentActiveUserId = -1; private final IAudioService mAudioService; private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; private final Handler mHandler = new Handler(); @@ -132,6 +135,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub mBluetoothA2dpRouteId = res.getString(com.android.internal.R.string.bluetooth_a2dp_audio_route_id); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(context); @@ -164,11 +168,11 @@ public final class MediaRouterService extends IMediaRouterService.Stub new UserSwitchObserver() { @Override public void onUserSwitchComplete(int newUserId) { - switchUser(newUserId); + updateRunningUserAndProfiles(newUserId); } }, TAG); - switchUser(ActivityManager.getCurrentUser()); + updateRunningUserAndProfiles(ActivityManager.getCurrentUser()); } @Override @@ -388,7 +392,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub pw.println("MEDIA ROUTER SERVICE (dumpsys media_router)"); pw.println(); pw.println("Global state"); - pw.println(" mCurrentUserId=" + mCurrentUserId); + pw.println(" mCurrentUserId=" + mCurrentActiveUserId); synchronized (mLock) { final int count = mUserRecords.size(); @@ -645,25 +649,31 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } - void switchUser(int userId) { + /** + * Starts all {@link UserRecord user records} associated with the active user (whose ID is + * {@code newActiveUserId}) or the active user's profiles. + * + * <p>All other records are stopped, and those without associated client records are removed. + */ + private void updateRunningUserAndProfiles(int newActiveUserId) { synchronized (mLock) { - if (mCurrentUserId != userId) { - final int oldUserId = mCurrentUserId; - mCurrentUserId = userId; // do this first - - UserRecord oldUser = mUserRecords.get(oldUserId); - if (oldUser != null) { - oldUser.mHandler.sendEmptyMessage(UserHandler.MSG_STOP); - disposeUserIfNeededLocked(oldUser); // since no longer current user - } - - UserRecord newUser = mUserRecords.get(userId); - if (newUser != null) { - newUser.mHandler.sendEmptyMessage(UserHandler.MSG_START); + if (mCurrentActiveUserId != newActiveUserId) { + mCurrentActiveUserId = newActiveUserId; + for (int i = 0; i < mUserRecords.size(); i++) { + int userId = mUserRecords.keyAt(i); + UserRecord userRecord = mUserRecords.valueAt(i); + if (isUserActiveLocked(userId)) { + // userId corresponds to the active user, or one of its profiles. We + // ensure the associated structures are initialized. + userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START); + } else { + userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_STOP); + disposeUserIfNeededLocked(userRecord); + } } } } - mService2.switchUser(userId); + mService2.updateRunningUserAndProfiles(newActiveUserId); } void clientDied(ClientRecord clientRecord) { @@ -718,8 +728,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub clientRecord.mGroupId = groupId; if (groupId != null) { userRecord.addToGroup(groupId, clientRecord); - userRecord.mHandler.obtainMessage(UserHandler.MSG_NOTIFY_GROUP_ROUTE_SELECTED, groupId) - .sendToTarget(); + userRecord + .mHandler + .obtainMessage(UserHandler.MSG_NOTIFY_GROUP_ROUTE_SELECTED, groupId) + .sendToTarget(); } } @@ -805,9 +817,13 @@ public final class MediaRouterService extends IMediaRouterService.Stub clientRecord.mUserRecord.mClientGroupMap.get(clientRecord.mGroupId); if (group != null) { group.mSelectedRouteId = routeId; - clientRecord.mUserRecord.mHandler.obtainMessage( - UserHandler.MSG_NOTIFY_GROUP_ROUTE_SELECTED, clientRecord.mGroupId) - .sendToTarget(); + clientRecord + .mUserRecord + .mHandler + .obtainMessage( + UserHandler.MSG_NOTIFY_GROUP_ROUTE_SELECTED, + clientRecord.mGroupId) + .sendToTarget(); } } } @@ -839,7 +855,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub if (DEBUG) { Slog.d(TAG, userRecord + ": Initialized"); } - if (userRecord.mUserId == mCurrentUserId) { + if (isUserActiveLocked(userRecord.mUserId)) { userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START); } } @@ -849,8 +865,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub // and purge the user record and all of its associated state. If the user is current // then leave it alone since we might be connected to a route or want to query // the same route information again soon. - if (userRecord.mUserId != mCurrentUserId - && userRecord.mClientRecords.isEmpty()) { + if (!isUserActiveLocked(userRecord.mUserId) && userRecord.mClientRecords.isEmpty()) { if (DEBUG) { Slog.d(TAG, userRecord + ": Disposed"); } @@ -859,6 +874,14 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } + /** + * Returns {@code true} if the given {@code userId} corresponds to the active user or a profile + * of the active user, returns {@code false} otherwise. + */ + private boolean isUserActiveLocked(int userId) { + return mUserManagerInternal.getProfileParentId(userId) == mCurrentActiveUserId; + } + private void initializeClientLocked(ClientRecord clientRecord) { if (DEBUG) { Slog.d(TAG, clientRecord + ": Registered"); @@ -898,7 +921,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) { - BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, android.bluetooth.BluetoothDevice.class); + BluetoothDevice btDevice = + intent.getParcelableExtra( + BluetoothDevice.EXTRA_DEVICE, + android.bluetooth.BluetoothDevice.class); synchronized (mLock) { mActiveBluetoothDevice = btDevice; mGlobalBluetoothA2dpOn = btDevice != null; diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java index bea616869014..56390a9f6b0e 100644 --- a/services/core/java/com/android/server/om/IdmapDaemon.java +++ b/services/core/java/com/android/server/om/IdmapDaemon.java @@ -217,6 +217,7 @@ class IdmapDaemon { synchronized List<FabricatedOverlayInfo> getFabricatedOverlayInfos() { final ArrayList<FabricatedOverlayInfo> allInfos = new ArrayList<>(); Connection c = null; + int iteratorId = -1; try { c = connect(); final IIdmap2 service = c.getIdmap2(); @@ -225,9 +226,9 @@ class IdmapDaemon { return Collections.emptyList(); } - service.acquireFabricatedOverlayIterator(); + iteratorId = service.acquireFabricatedOverlayIterator(); List<FabricatedOverlayInfo> infos; - while (!(infos = service.nextFabricatedOverlayInfos()).isEmpty()) { + while (!(infos = service.nextFabricatedOverlayInfos(iteratorId)).isEmpty()) { allInfos.addAll(infos); } return allInfos; @@ -235,8 +236,8 @@ class IdmapDaemon { Slog.wtf(TAG, "failed to get all fabricated overlays", e); } finally { try { - if (c.getIdmap2() != null) { - c.getIdmap2().releaseFabricatedOverlayIterator(); + if (c.getIdmap2() != null && iteratorId != -1) { + c.getIdmap2().releaseFabricatedOverlayIterator(iteratorId); } } catch (RemoteException e) { // ignore diff --git a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java index 372bcc6b07ca..46f335ef9fa2 100644 --- a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java +++ b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java @@ -46,6 +46,7 @@ public final class ConfigurationInternal { private final boolean mAutoDetectionSupported; private final int mSystemClockUpdateThresholdMillis; + private final int mSystemClockConfidenceUpgradeThresholdMillis; private final Instant mAutoSuggestionLowerBound; private final Instant mManualSuggestionLowerBound; private final Instant mSuggestionUpperBound; @@ -57,6 +58,8 @@ public final class ConfigurationInternal { private ConfigurationInternal(Builder builder) { mAutoDetectionSupported = builder.mAutoDetectionSupported; mSystemClockUpdateThresholdMillis = builder.mSystemClockUpdateThresholdMillis; + mSystemClockConfidenceUpgradeThresholdMillis = + builder.mSystemClockConfidenceUpgradeThresholdMillis; mAutoSuggestionLowerBound = Objects.requireNonNull(builder.mAutoSuggestionLowerBound); mManualSuggestionLowerBound = Objects.requireNonNull(builder.mManualSuggestionLowerBound); mSuggestionUpperBound = Objects.requireNonNull(builder.mSuggestionUpperBound); @@ -82,6 +85,17 @@ public final class ConfigurationInternal { } /** + * Return the absolute threshold at/below which the system clock confidence can be upgraded. + * i.e. if the detector receives a high-confidence time and the current system clock is +/- this + * value from that time and the confidence in the time is low, then the device's confidence in + * the current system clock time can be upgraded. This needs to be an amount users would + * consider "close enough". + */ + public int getSystemClockConfidenceUpgradeThresholdMillis() { + return mSystemClockConfidenceUpgradeThresholdMillis; + } + + /** * Returns the lower bound for valid automatic time suggestions. It is guaranteed to be in the * past, i.e. it is unrelated to the current system clock time. * It holds no other meaning; it could be related to when the device system image was built, @@ -242,6 +256,8 @@ public final class ConfigurationInternal { return "ConfigurationInternal{" + "mAutoDetectionSupported=" + mAutoDetectionSupported + ", mSystemClockUpdateThresholdMillis=" + mSystemClockUpdateThresholdMillis + + ", mSystemClockConfidenceUpgradeThresholdMillis=" + + mSystemClockConfidenceUpgradeThresholdMillis + ", mAutoSuggestionLowerBound=" + mAutoSuggestionLowerBound + "(" + mAutoSuggestionLowerBound.toEpochMilli() + ")" + ", mManualSuggestionLowerBound=" + mManualSuggestionLowerBound @@ -258,6 +274,7 @@ public final class ConfigurationInternal { static final class Builder { private boolean mAutoDetectionSupported; private int mSystemClockUpdateThresholdMillis; + private int mSystemClockConfidenceUpgradeThresholdMillis; @NonNull private Instant mAutoSuggestionLowerBound; @NonNull private Instant mManualSuggestionLowerBound; @NonNull private Instant mSuggestionUpperBound; @@ -286,68 +303,55 @@ public final class ConfigurationInternal { this.mAutoDetectionEnabledSetting = toCopy.mAutoDetectionEnabledSetting; } - /** - * Sets whether the user is allowed to configure time settings on this device. - */ + /** See {@link ConfigurationInternal#isUserConfigAllowed()}. */ Builder setUserConfigAllowed(boolean userConfigAllowed) { mUserConfigAllowed = userConfigAllowed; return this; } - /** - * Sets whether automatic time detection is supported on this device. - */ + /** See {@link ConfigurationInternal#isAutoDetectionSupported()}. */ public Builder setAutoDetectionSupported(boolean supported) { mAutoDetectionSupported = supported; return this; } - /** - * Sets the absolute threshold below which the system clock need not be updated. i.e. if - * setting the system clock would adjust it by less than this (either backwards or forwards) - * then it need not be set. - */ + /** See {@link ConfigurationInternal#getSystemClockUpdateThresholdMillis()}. */ public Builder setSystemClockUpdateThresholdMillis(int systemClockUpdateThresholdMillis) { mSystemClockUpdateThresholdMillis = systemClockUpdateThresholdMillis; return this; } - /** - * Sets the lower bound for valid automatic time suggestions. - */ + /** See {@link ConfigurationInternal#getSystemClockConfidenceUpgradeThresholdMillis()}. */ + public Builder setSystemClockConfidenceUpgradeThresholdMillis(int thresholdMillis) { + mSystemClockConfidenceUpgradeThresholdMillis = thresholdMillis; + return this; + } + + /** See {@link ConfigurationInternal#getAutoSuggestionLowerBound()}. */ public Builder setAutoSuggestionLowerBound(@NonNull Instant autoSuggestionLowerBound) { mAutoSuggestionLowerBound = Objects.requireNonNull(autoSuggestionLowerBound); return this; } - /** - * Sets the lower bound for valid manual time suggestions. - */ + /** See {@link ConfigurationInternal#getManualSuggestionLowerBound()}. */ public Builder setManualSuggestionLowerBound(@NonNull Instant manualSuggestionLowerBound) { mManualSuggestionLowerBound = Objects.requireNonNull(manualSuggestionLowerBound); return this; } - /** - * Sets the upper bound for valid time suggestions (manual and automatic). - */ + /** See {@link ConfigurationInternal#getSuggestionUpperBound()}. */ public Builder setSuggestionUpperBound(@NonNull Instant suggestionUpperBound) { mSuggestionUpperBound = Objects.requireNonNull(suggestionUpperBound); return this; } - /** - * Sets the order to look at time suggestions when automatically detecting time. - * See {@code #ORIGIN_} constants - */ + /** See {@link ConfigurationInternal#getAutoOriginPriorities()}. */ public Builder setOriginPriorities(@NonNull @Origin int... originPriorities) { mOriginPriorities = Objects.requireNonNull(originPriorities); return this; } - /** - * Sets the value of the automatic time detection enabled setting for this device. - */ + /** See {@link ConfigurationInternal#getAutoDetectionEnabledSetting()}. */ Builder setAutoDetectionEnabledSetting(boolean autoDetectionEnabledSetting) { mAutoDetectionEnabledSetting = autoDetectionEnabledSetting; return this; diff --git a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java index 3e02b463284d..4972412472f1 100644 --- a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java +++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java @@ -16,16 +16,21 @@ package com.android.server.timedetector; +import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; -import android.app.AlarmManager; import android.content.Context; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; import android.util.Slog; +import com.android.server.AlarmManagerInternal; +import com.android.server.LocalServices; +import com.android.server.SystemClockTime; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.timezonedetector.ConfigurationChangeListener; +import java.io.PrintWriter; import java.util.Objects; /** @@ -38,7 +43,7 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { @NonNull private final Handler mHandler; @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final PowerManager.WakeLock mWakeLock; - @NonNull private final AlarmManager mAlarmManager; + @NonNull private final AlarmManagerInternal mAlarmManagerInternal; EnvironmentImpl(@NonNull Context context, @NonNull Handler handler, @NonNull ServiceConfigAccessor serviceConfigAccessor) { @@ -49,7 +54,8 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { mWakeLock = Objects.requireNonNull( powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG)); - mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class)); + mAlarmManagerInternal = Objects.requireNonNull( + LocalServices.getService(AlarmManagerInternal.class)); } @Override @@ -84,9 +90,22 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { } @Override - public void setSystemClock(long newTimeMillis) { + public @TimeConfidence int systemClockConfidence() { + return SystemClockTime.getTimeConfidence(); + } + + @Override + public void setSystemClock( + @CurrentTimeMillisLong long newTimeMillis, @TimeConfidence int confidence, + @NonNull String logMsg) { + checkWakeLockHeld(); + mAlarmManagerInternal.setTime(newTimeMillis, confidence, logMsg); + } + + @Override + public void setSystemClockConfidence(@TimeConfidence int confidence, @NonNull String logMsg) { checkWakeLockHeld(); - mAlarmManager.setTime(newTimeMillis); + SystemClockTime.setConfidence(confidence, logMsg); } @Override @@ -100,4 +119,13 @@ final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " not held"); } } -} + + @Override + public void addDebugLogEntry(@NonNull String logMsg) { + SystemClockTime.addDebugLogEntry(logMsg); + } + + @Override + public void dumpDebugLog(@NonNull PrintWriter printWriter) { + SystemClockTime.dump(printWriter); + }} diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java index 888304aa199b..0ea5f7a105d1 100644 --- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java +++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java @@ -68,6 +68,15 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000; /** + * An absolute threshold at/below which the system clock confidence can be upgraded. i.e. if the + * detector receives a high-confidence time and the current system clock is +/- this value from + * that time and the confidence in the time is low, then the device's confidence in the current + * system clock time can be upgraded. This needs to be an amount users would consider + * "close enough". + */ + private static final int SYSTEM_CLOCK_CONFIRMATION_THRESHOLD_MILLIS = 1000; + + /** * By default telephony and network only suggestions are accepted and telephony takes * precedence over network. */ @@ -236,6 +245,8 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { .setAutoDetectionSupported(isAutoDetectionSupported()) .setAutoDetectionEnabledSetting(getAutoDetectionEnabledSetting()) .setSystemClockUpdateThresholdMillis(getSystemClockUpdateThresholdMillis()) + .setSystemClockConfidenceUpgradeThresholdMillis( + getSystemClockConfidenceUpgradeThresholdMillis()) .setAutoSuggestionLowerBound(getAutoSuggestionLowerBound()) .setManualSuggestionLowerBound(timeDetectorHelper.getManualSuggestionLowerBound()) .setSuggestionUpperBound(timeDetectorHelper.getSuggestionUpperBound()) @@ -285,6 +296,10 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { return mSystemClockUpdateThresholdMillis; } + private int getSystemClockConfidenceUpgradeThresholdMillis() { + return SYSTEM_CLOCK_CONFIRMATION_THRESHOLD_MILLIS; + } + @NonNull private Instant getAutoSuggestionLowerBound() { return mServerFlags.getOptionalInstant(KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE) diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index 547cf9d32aa1..fe2760e9ce10 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -16,6 +16,7 @@ package com.android.server.timedetector; +import static com.android.server.SystemClockTime.TIME_CONFIDENCE_HIGH; import static com.android.server.timedetector.TimeDetectorStrategy.originToString; import android.annotation.CurrentTimeMillisLong; @@ -23,7 +24,6 @@ import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.app.AlarmManager; import android.app.time.ExternalTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; @@ -31,24 +31,24 @@ import android.content.Context; import android.os.Handler; import android.os.TimestampedValue; import android.util.IndentingPrintWriter; -import android.util.LocalLog; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.SystemClockTime; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.timezonedetector.ArrayMapWithHistory; import com.android.server.timezonedetector.ConfigurationChangeListener; import com.android.server.timezonedetector.ReferenceWithHistory; +import java.io.PrintWriter; import java.time.Duration; import java.time.Instant; import java.util.Arrays; import java.util.Objects; /** - * An implementation of {@link TimeDetectorStrategy} that passes telephony and manual suggestions to - * {@link AlarmManager}. When there are multiple telephony sources, the one with the lowest ID is - * used unless the data becomes too stale. + * The real implementation of {@link TimeDetectorStrategy}. * * <p>Most public methods are marked synchronized to ensure thread safety around internal state. */ @@ -84,13 +84,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { */ private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10; - /** - * A log that records the decisions / decision metadata that affected the device's system clock - * time. This is logged in bug reports to assist with debugging issues with detection. - */ - @NonNull - private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */); - @NonNull private final Environment mEnvironment; @@ -157,11 +150,30 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @CurrentTimeMillisLong long systemClockMillis(); - /** Sets the device system clock. The WakeLock must be held. */ - void setSystemClock(@CurrentTimeMillisLong long newTimeMillis); + /** Returns the system clock confidence value. */ + @TimeConfidence int systemClockConfidence(); + + /** Sets the device system clock and confidence. The WakeLock must be held. */ + void setSystemClock( + @CurrentTimeMillisLong long newTimeMillis, @TimeConfidence int confidence, + @NonNull String logMsg); + + /** Sets the device system clock confidence. The WakeLock must be held. */ + void setSystemClockConfidence(@TimeConfidence int confidence, @NonNull String logMsg); /** Release the wake lock acquired by a call to {@link #acquireWakeLock()}. */ void releaseWakeLock(); + + + /** + * Adds a standalone entry to the time debug log. + */ + void addDebugLogEntry(@NonNull String logMsg); + + /** + * Dumps the time debug log to the supplied {@link PrintWriter}. + */ + void dumpDebugLog(PrintWriter printWriter); } static TimeDetectorStrategy create( @@ -250,7 +262,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } String cause = "Manual time suggestion received: suggestion=" + suggestion; - return setSystemClockIfRequired(ORIGIN_MANUAL, newUnixEpochTime, cause); + return setSystemClockAndConfidenceIfRequired(ORIGIN_MANUAL, newUnixEpochTime, cause); } @Override @@ -318,7 +330,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { String logMsg = "handleConfigurationInternalChanged:" + " oldConfiguration=" + mCurrentConfigurationInternal + ", newConfiguration=" + currentUserConfig; - logTimeDetectorChange(logMsg); + addDebugLogEntry(logMsg); mCurrentConfigurationInternal = currentUserConfig; boolean autoDetectionEnabled = @@ -335,11 +347,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } } - private void logTimeDetectorChange(@NonNull String logMsg) { + private void addDebugLogEntry(@NonNull String logMsg) { if (DBG) { Slog.d(LOG_TAG, logMsg); } - mTimeChangesLog.log(logMsg); + mEnvironment.addDebugLogEntry(logMsg); } @Override @@ -356,10 +368,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { long systemClockMillis = mEnvironment.systemClockMillis(); ipw.printf("mEnvironment.systemClockMillis()=%s (%s)\n", Instant.ofEpochMilli(systemClockMillis), systemClockMillis); + ipw.println("mEnvironment.systemClockConfidence()=" + mEnvironment.systemClockConfidence()); ipw.println("Time change log:"); ipw.increaseIndent(); // level 2 - mTimeChangesLog.dump(ipw); + SystemClockTime.dump(ipw); ipw.decreaseIndent(); // level 2 ipw.println("Telephony suggestion history:"); @@ -494,11 +507,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @GuardedBy("this") private void doAutoTimeDetection(@NonNull String detectionReason) { - if (!mCurrentConfigurationInternal.getAutoDetectionEnabledBehavior()) { - // Avoid doing unnecessary work with this (race-prone) check. - return; - } - // Try the different origins one at a time. int[] originPriorities = mCurrentConfigurationInternal.getAutoOriginPriorities(); for (int origin : originPriorities) { @@ -544,7 +552,14 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { // Update the system clock if a good suggestion has been found. if (newUnixEpochTime != null) { - setSystemClockIfRequired(origin, newUnixEpochTime, cause); + if (mCurrentConfigurationInternal.getAutoDetectionEnabledBehavior()) { + setSystemClockAndConfidenceIfRequired(origin, newUnixEpochTime, cause); + } else { + // An automatically detected time can be used to raise the confidence in the + // current time even if the device is set to only allow user input for the time + // itself. + upgradeSystemClockConfidenceIfRequired(newUnixEpochTime, cause); + } return; } } @@ -718,14 +733,18 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @GuardedBy("this") - private boolean setSystemClockIfRequired( + private boolean setSystemClockAndConfidenceIfRequired( @Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) { + // Any time set through this class is inherently high confidence. Either it came directly + // from a user, or it was detected automatically. + @TimeConfidence final int newTimeConfidence = TIME_CONFIDENCE_HIGH; boolean isOriginAutomatic = isOriginAutomatic(origin); if (isOriginAutomatic) { if (!mCurrentConfigurationInternal.getAutoDetectionEnabledBehavior()) { if (DBG) { - Slog.d(LOG_TAG, "Auto time detection is not enabled." + Slog.d(LOG_TAG, + "Auto time detection is not enabled / no confidence update is needed." + " origin=" + originToString(origin) + ", time=" + time + ", cause=" + cause); @@ -746,7 +765,57 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { mEnvironment.acquireWakeLock(); try { - return setSystemClockUnderWakeLock(origin, time, cause); + return setSystemClockAndConfidenceUnderWakeLock(origin, time, newTimeConfidence, cause); + } finally { + mEnvironment.releaseWakeLock(); + } + } + + /** + * Upgrades the system clock confidence if the current time matches the supplied auto-detected + * time. The method never changes the system clock and it never lowers the confidence. It only + * raises the confidence if the supplied time is within the configured threshold of the current + * system clock time. + */ + @GuardedBy("this") + private void upgradeSystemClockConfidenceIfRequired( + @NonNull TimestampedValue<Long> autoDetectedUnixEpochTime, @NonNull String cause) { + @TimeConfidence int newTimeConfidence = TIME_CONFIDENCE_HIGH; + @TimeConfidence int currentTimeConfidence = mEnvironment.systemClockConfidence(); + boolean timeNeedsConfirmation = currentTimeConfidence < newTimeConfidence; + if (!timeNeedsConfirmation) { + return; + } + + // All system clock calculation take place under a wake lock. + mEnvironment.acquireWakeLock(); + try { + // Check if the specified time matches the current system clock time (closely + // enough) to raise the confidence. + long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis(); + long actualSystemClockMillis = mEnvironment.systemClockMillis(); + long adjustedAutoDetectedUnixEpochMillis = TimeDetectorStrategy.getTimeAt( + autoDetectedUnixEpochTime, elapsedRealtimeMillis); + long absTimeDifferenceMillis = + Math.abs(adjustedAutoDetectedUnixEpochMillis - actualSystemClockMillis); + int confidenceUpgradeThresholdMillis = + mCurrentConfigurationInternal.getSystemClockConfidenceUpgradeThresholdMillis(); + boolean updateConfidenceRequired = + absTimeDifferenceMillis <= confidenceUpgradeThresholdMillis; + if (updateConfidenceRequired) { + String logMsg = "Upgrade system clock confidence." + + " autoDetectedUnixEpochTime=" + autoDetectedUnixEpochTime + + " newTimeConfidence=" + newTimeConfidence + + " cause=" + cause + + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + + " (old) actualSystemClockMillis=" + actualSystemClockMillis + + " currentTimeConfidence=" + currentTimeConfidence; + if (DBG) { + Slog.d(LOG_TAG, logMsg); + } + + mEnvironment.setSystemClockConfidence(newTimeConfidence, logMsg); + } } finally { mEnvironment.releaseWakeLock(); } @@ -757,8 +826,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @GuardedBy("this") - private boolean setSystemClockUnderWakeLock( - @Origin int origin, @NonNull TimestampedValue<Long> newTime, @NonNull String cause) { + private boolean setSystemClockAndConfidenceUnderWakeLock( + @Origin int origin, @NonNull TimestampedValue<Long> newTime, + @TimeConfidence int newTimeConfidence, @NonNull String cause) { long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis(); boolean isOriginAutomatic = isOriginAutomatic(origin); @@ -776,6 +846,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { "System clock has not tracked elapsed real time clock. A clock may" + " be inaccurate or something unexpectedly set the system" + " clock." + + " origin=" + originToString(origin) + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " expectedTimeMillis=" + expectedTimeMillis + " actualTimeMillis=" + actualSystemClockMillis @@ -784,44 +855,72 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } } + // If the new signal would make sufficient difference to the system clock or mean a change + // in confidence then system state must be updated. + // Adjust for the time that has elapsed since the signal was received. long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis); - - // Check if the new signal would make sufficient difference to the system clock. If it's - // below the threshold then ignore it. long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis); long systemClockUpdateThreshold = mCurrentConfigurationInternal.getSystemClockUpdateThresholdMillis(); - if (absTimeDifference < systemClockUpdateThreshold) { + boolean updateSystemClockRequired = absTimeDifference >= systemClockUpdateThreshold; + + @TimeConfidence int currentTimeConfidence = mEnvironment.systemClockConfidence(); + boolean updateConfidenceRequired = newTimeConfidence > currentTimeConfidence; + + if (updateSystemClockRequired) { + String logMsg = "Set system clock & confidence." + + " origin=" + originToString(origin) + + " newTime=" + newTime + + " newTimeConfidence=" + newTimeConfidence + + " cause=" + cause + + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + + " (old) actualSystemClockMillis=" + actualSystemClockMillis + + " newSystemClockMillis=" + newSystemClockMillis + + " currentTimeConfidence=" + currentTimeConfidence; + mEnvironment.setSystemClock(newSystemClockMillis, newTimeConfidence, logMsg); if (DBG) { - Slog.d(LOG_TAG, "Not setting system clock. New time and" - + " system clock are close enough." - + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + Slog.d(LOG_TAG, logMsg); + } + + // CLOCK_PARANOIA : Record the last time this class set the system clock due to an + // auto-time signal, or clear the record it is being done manually. + if (isOriginAutomatic(origin)) { + mLastAutoSystemClockTimeSet = newTime; + } else { + mLastAutoSystemClockTimeSet = null; + } + } else if (updateConfidenceRequired) { + // Only the confidence needs updating. This path is separate from a system clock update + // to deliberately avoid touching the system clock's value when it's not needed. Doing + // so could introduce inaccuracies or cause unnecessary wear in RTC hardware or + // associated storage. + String logMsg = "Set system clock confidence." + + " origin=" + originToString(origin) + + " newTime=" + newTime + + " newTimeConfidence=" + newTimeConfidence + + " cause=" + cause + + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + + " (old) actualSystemClockMillis=" + actualSystemClockMillis + + " newSystemClockMillis=" + newSystemClockMillis + + " currentTimeConfidence=" + currentTimeConfidence; + if (DBG) { + Slog.d(LOG_TAG, logMsg); + } + mEnvironment.setSystemClockConfidence(newTimeConfidence, logMsg); + } else { + // Neither clock nor confidence need updating. + if (DBG) { + Slog.d(LOG_TAG, "Not setting system clock or confidence." + + " origin=" + originToString(origin) + " newTime=" + newTime + + " newTimeConfidence=" + newTimeConfidence + " cause=" + cause + + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " systemClockUpdateThreshold=" + systemClockUpdateThreshold - + " absTimeDifference=" + absTimeDifference); + + " absTimeDifference=" + absTimeDifference + + " currentTimeConfidence=" + currentTimeConfidence); } - return true; - } - - mEnvironment.setSystemClock(newSystemClockMillis); - String logMsg = "Set system clock using time=" + newTime - + " cause=" + cause - + " elapsedRealtimeMillis=" + elapsedRealtimeMillis - + " (old) actualSystemClockMillis=" + actualSystemClockMillis - + " newSystemClockMillis=" + newSystemClockMillis; - if (DBG) { - Slog.d(LOG_TAG, logMsg); - } - mTimeChangesLog.log(logMsg); - - // CLOCK_PARANOIA : Record the last time this class set the system clock due to an auto-time - // signal, or clear the record it is being done manually. - if (isOriginAutomatic(origin)) { - mLastAutoSystemClockTimeSet = newTime; - } else { - mLastAutoSystemClockTimeSet = null; } return true; } diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index ffe24c07f1d3..7bc62ec698a3 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -332,8 +332,8 @@ class ActivityClientController extends IActivityClientController.Stub { } @Override - public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode, - Intent resultData) { + public boolean navigateUpTo(IBinder token, Intent destIntent, String resolvedType, + int resultCode, Intent resultData) { final ActivityRecord r; synchronized (mGlobalLock) { r = ActivityRecord.isInRootTaskLocked(token); @@ -348,7 +348,7 @@ class ActivityClientController extends IActivityClientController.Stub { synchronized (mGlobalLock) { return r.getRootTask().navigateUpTo( - r, destIntent, destGrants, resultCode, resultData, resultGrants); + r, destIntent, resolvedType, destGrants, resultCode, resultData, resultGrants); } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 63c6c35a8e1e..62d93ad7d030 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -5278,8 +5278,9 @@ class Task extends TaskFragment { return false; } - boolean navigateUpTo(ActivityRecord srec, Intent destIntent, NeededUriGrants destGrants, - int resultCode, Intent resultData, NeededUriGrants resultGrants) { + boolean navigateUpTo(ActivityRecord srec, Intent destIntent, String resolvedType, + NeededUriGrants destGrants, int resultCode, Intent resultData, + NeededUriGrants resultGrants) { if (!srec.attachedToProcess()) { // Nothing to do if the caller is not attached, because this method should be called // from an alive activity. @@ -5348,32 +5349,26 @@ class Task extends TaskFragment { if (parent != null && foundParentInTask) { final int callingUid = srec.info.applicationInfo.uid; - try { - ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo( - destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS, - srec.mUserId); - // TODO(b/64750076): Check if calling pid should really be -1. - final int res = mAtmService.getActivityStartController() - .obtainStarter(destIntent, "navigateUpTo") - .setCaller(srec.app.getThread()) - .setActivityInfo(aInfo) - .setResultTo(parent.token) - .setIntentGrants(destGrants) - .setCallingPid(-1) - .setCallingUid(callingUid) - .setCallingPackage(srec.packageName) - .setCallingFeatureId(parent.launchedFromFeatureId) - .setRealCallingPid(-1) - .setRealCallingUid(callingUid) - .setComponentSpecified(true) - .execute(); - foundParentInTask = isStartResultSuccessful(res); - if (res == ActivityManager.START_SUCCESS) { - parent.finishIfPossible(resultCode, resultData, resultGrants, - "navigate-top", true /* oomAdj */); - } - } catch (RemoteException e) { - foundParentInTask = false; + // TODO(b/64750076): Check if calling pid should really be -1. + final int res = mAtmService.getActivityStartController() + .obtainStarter(destIntent, "navigateUpTo") + .setResolvedType(resolvedType) + .setUserId(srec.mUserId) + .setCaller(srec.app.getThread()) + .setResultTo(parent.token) + .setIntentGrants(destGrants) + .setCallingPid(-1) + .setCallingUid(callingUid) + .setCallingPackage(srec.packageName) + .setCallingFeatureId(parent.launchedFromFeatureId) + .setRealCallingPid(-1) + .setRealCallingUid(callingUid) + .setComponentSpecified(true) + .execute(); + foundParentInTask = isStartResultSuccessful(res); + if (res == ActivityManager.START_SUCCESS) { + parent.finishIfPossible(resultCode, resultData, resultGrants, + "navigate-top", true /* oomAdj */); } } Binder.restoreCallingIdentity(origId); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 5d6ffd8b8ead..7e93d623f3e0 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -53,6 +53,7 @@ cc_library_static { "com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp", "com_android_server_stats_pull_StatsPullAtomService.cpp", "com_android_server_storage_AppFuseBridge.cpp", + "com_android_server_SystemClockTime.cpp", "com_android_server_SystemServer.cpp", "com_android_server_tv_TvUinputBridge.cpp", "com_android_server_tv_TvInputHal.cpp", diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS index 9abf107c780a..2584b86f53db 100644 --- a/services/core/jni/OWNERS +++ b/services/core/jni/OWNERS @@ -12,6 +12,7 @@ per-file com_android_server_power_PowerManagerService.* = michaelwr@google.com, per-file com_android_server_am_BatteryStatsService.cpp = file:/BATTERY_STATS_OWNERS per-file Android.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION} +per-file com_android_server_SystemClock* = file:/services/core/java/com/android/server/timedetector/OWNERS per-file com_android_server_Usb* = file:/services/usb/OWNERS per-file com_android_server_Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS per-file com_android_server_hdmi_* = file:/core/java/android/hardware/hdmi/OWNERS diff --git a/services/core/jni/com_android_server_SystemClockTime.cpp b/services/core/jni/com_android_server_SystemClockTime.cpp new file mode 100644 index 000000000000..9db4c429f0c7 --- /dev/null +++ b/services/core/jni/com_android_server_SystemClockTime.cpp @@ -0,0 +1,126 @@ +/* + * 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. + */ + +#define LOG_TAG "SystemClockTime" + +#include <android-base/file.h> +#include <android-base/unique_fd.h> +#include <linux/rtc.h> +#include <nativehelper/JNIHelp.h> +#include <utils/Log.h> +#include <utils/String8.h> + +#include "jni.h" + +namespace android { + +class SystemClockImpl { +public: + SystemClockImpl(const std::string &rtc_dev) : rtc_dev{rtc_dev} {} + + int setTime(struct timeval *tv); + +private: + std::string rtc_dev; +}; + +int SystemClockImpl::setTime(struct timeval *tv) { + if (settimeofday(tv, NULL) == -1) { + ALOGV("settimeofday() failed: %s", strerror(errno)); + return -1; + } + + android::base::unique_fd fd{open(rtc_dev.c_str(), O_RDWR)}; + if (!fd.ok()) { + ALOGE("Unable to open %s: %s", rtc_dev.c_str(), strerror(errno)); + return -1; + } + + struct tm tm; + if (!gmtime_r(&tv->tv_sec, &tm)) { + ALOGV("gmtime_r() failed: %s", strerror(errno)); + return -1; + } + + struct rtc_time rtc = {}; + rtc.tm_sec = tm.tm_sec; + rtc.tm_min = tm.tm_min; + rtc.tm_hour = tm.tm_hour; + rtc.tm_mday = tm.tm_mday; + rtc.tm_mon = tm.tm_mon; + rtc.tm_year = tm.tm_year; + rtc.tm_wday = tm.tm_wday; + rtc.tm_yday = tm.tm_yday; + rtc.tm_isdst = tm.tm_isdst; + if (ioctl(fd, RTC_SET_TIME, &rtc) == -1) { + ALOGV("RTC_SET_TIME ioctl failed: %s", strerror(errno)); + return -1; + } + + return 0; +} + +static jlong com_android_server_SystemClockTime_init(JNIEnv *, jobject) { + // Find the wall clock RTC. We expect this always to be /dev/rtc0, but + // check the /dev/rtc symlink first so that legacy devices that don't use + // rtc0 can add a symlink rather than need to carry a local patch to this + // code. + // + // TODO: if you're reading this in a world where all devices are using the + // GKI, you can remove the readlink and just assume /dev/rtc0. + std::string dev_rtc; + if (!android::base::Readlink("/dev/rtc", &dev_rtc)) { + dev_rtc = "/dev/rtc0"; + } + + std::unique_ptr<SystemClockImpl> system_clock{new SystemClockImpl(dev_rtc)}; + return reinterpret_cast<jlong>(system_clock.release()); +} + +static jint com_android_server_SystemClockTime_setTime(JNIEnv *, jobject, jlong nativeData, + jlong millis) { + SystemClockImpl *impl = reinterpret_cast<SystemClockImpl *>(nativeData); + + if (millis <= 0 || millis / 1000LL >= std::numeric_limits<time_t>::max()) { + return -1; + } + + struct timeval tv; + tv.tv_sec = (millis / 1000LL); + tv.tv_usec = ((millis % 1000LL) * 1000LL); + + ALOGD("Setting time of day to sec=%ld", tv.tv_sec); + + int ret = impl->setTime(&tv); + if (ret < 0) { + ALOGW("Unable to set rtc to %ld: %s", tv.tv_sec, strerror(errno)); + ret = -1; + } + return ret; +} + +static const JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"init", "()J", (void *)com_android_server_SystemClockTime_init}, + {"setTime", "(JJ)I", (void *)com_android_server_SystemClockTime_setTime}, +}; + +int register_com_android_server_SystemClockTime(JNIEnv *env) { + return jniRegisterNativeMethods(env, "com/android/server/SystemClockTime", sMethods, + NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 00bef0935308..00f851f9f4ff 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -65,6 +65,7 @@ int register_android_server_companion_virtual_InputController(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); }; using namespace android; @@ -122,5 +123,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) 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); return JNI_VERSION_1_4; } diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index 446317e8829f..830e2ac029c2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -168,10 +168,12 @@ import com.android.server.AppStateTracker; import com.android.server.AppStateTrackerImpl; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.SystemService; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.tare.AlarmManagerEconomicPolicy; import com.android.server.tare.EconomyManagerInternal; import com.android.server.usage.AppStandbyInternal; @@ -345,10 +347,6 @@ public class AlarmManagerServiceTest { } @Override - void setKernelTime(long millis) { - } - - @Override int getSystemUiUid(PackageManagerInternal unused) { return SYSTEM_UI_UID; } @@ -360,7 +358,18 @@ public class AlarmManagerServiceTest { } @Override - long getElapsedRealtime() { + void initializeTimeIfRequired() { + // No-op + } + + @Override + void setCurrentTimeMillis(long unixEpochMillis, + @TimeConfidence int confidence, String logMsg) { + mNowRtcTest = unixEpochMillis; + } + + @Override + long getElapsedRealtimeMillis() { return mNowElapsedTest; } @@ -650,7 +659,8 @@ public class AlarmManagerServiceTest { } private void setTareEnabled(boolean enabled) { - when(mEconomyManagerInternal.isEnabled()).thenReturn(enabled); + when(mEconomyManagerInternal.isEnabled(eq(AlarmManagerEconomicPolicy.POLICY_ALARM))) + .thenReturn(enabled); mService.mConstants.onTareEnabledStateChanged(enabled); } diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java index 47155a1eadd3..6da4ab714d7a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java @@ -25,6 +25,10 @@ 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.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -78,10 +82,13 @@ public class CompleteEconomicPolicyTest { } @Override - boolean isPolicyEnabled(int policy) { + boolean isPolicyEnabled(int policy, @Nullable DeviceConfig.Properties properties) { // Use a limited set of policies so that the test doesn't need to be updated whenever // a policy is added or removed. - return policy == EconomicPolicy.POLICY_AM || policy == EconomicPolicy.POLICY_JS; + if (policy == EconomicPolicy.POLICY_ALARM || policy == EconomicPolicy.POLICY_JOB) { + return super.isPolicyEnabled(policy, properties); + } + return false; } } @@ -116,10 +123,14 @@ public class CompleteEconomicPolicyTest { -> mDeviceConfigPropertiesBuilder.build()) .when(() -> DeviceConfig.getProperties( eq(DeviceConfig.NAMESPACE_TARE), ArgumentMatchers.<String>any())); + mDeviceConfigPropertiesBuilder + .setBoolean(EconomyManager.KEY_ENABLE_POLICY_ALARM, true) + .setBoolean(EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER, true); // Initialize real objects. // Capture the listeners. mEconomicPolicy = new CompleteEconomicPolicy(mIrs, mInjector); + mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build()); } @After @@ -129,6 +140,11 @@ public class CompleteEconomicPolicyTest { } } + private void setDeviceConfigBoolean(String key, boolean val) { + mDeviceConfigPropertiesBuilder.setBoolean(key, val); + mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build()); + } + private void setDeviceConfigCakes(String key, long valCakes) { mDeviceConfigPropertiesBuilder.setString(key, valCakes + "c"); mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build()); @@ -182,4 +198,52 @@ public class CompleteEconomicPolicyTest { assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); } + + + @Test + public void testPolicyToggling() { + setDeviceConfigBoolean(EconomyManager.KEY_ENABLE_POLICY_ALARM, true); + setDeviceConfigBoolean(EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER, false); + assertEquals(EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES, + mEconomicPolicy.getInitialSatiatedConsumptionLimit()); + assertEquals(EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES, + mEconomicPolicy.getHardSatiatedConsumptionLimit()); + final String pkgRestricted = "com.pkg.restricted"; + when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); + assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); + assertEquals(EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES, + mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); + final String pkgExempted = "com.pkg.exempted"; + when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); + assertEquals(EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES, + mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); + assertEquals(EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES, + mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); + assertNotNull(mEconomicPolicy.getAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK)); + assertNull(mEconomicPolicy.getAction(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START)); + assertEquals(EconomicPolicy.POLICY_ALARM, mEconomicPolicy.getEnabledPolicyIds()); + assertTrue(mEconomicPolicy.isPolicyEnabled(EconomicPolicy.POLICY_ALARM)); + assertFalse(mEconomicPolicy.isPolicyEnabled(EconomicPolicy.POLICY_JOB)); + + setDeviceConfigBoolean(EconomyManager.KEY_ENABLE_POLICY_ALARM, false); + setDeviceConfigBoolean(EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER, true); + assertEquals(EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES, + mEconomicPolicy.getInitialSatiatedConsumptionLimit()); + assertEquals(EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES, + mEconomicPolicy.getHardSatiatedConsumptionLimit()); + when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); + assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); + assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, + mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); + when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); + assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES, + mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); + assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES, + mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); + assertNull(mEconomicPolicy.getAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK)); + assertNotNull(mEconomicPolicy.getAction(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START)); + assertEquals(EconomicPolicy.POLICY_JOB, mEconomicPolicy.getEnabledPolicyIds()); + assertFalse(mEconomicPolicy.isPolicyEnabled(EconomicPolicy.POLICY_ALARM)); + assertTrue(mEconomicPolicy.isPolicyEnabled(EconomicPolicy.POLICY_JOB)); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java index 4ce268f0dc39..ddfa05cf5a2e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java @@ -140,6 +140,8 @@ public class ScribeTest { report1.numPositiveRegulations = 10; report1.cumulativeNegativeRegulations = 11; report1.numNegativeRegulations = 12; + report1.screenOffDurationMs = 13; + report1.screenOffDischargeMah = 14; mReports.add(report1); mScribeUnderTest.writeImmediatelyForTesting(); mScribeUnderTest.loadFromDiskLocked(); @@ -160,6 +162,8 @@ public class ScribeTest { report2.numPositiveRegulations = 100; report2.cumulativeNegativeRegulations = 110; report2.numNegativeRegulations = 120; + report2.screenOffDurationMs = 130; + report2.screenOffDischargeMah = 140; mReports.add(report2); mScribeUnderTest.writeImmediatelyForTesting(); mScribeUnderTest.loadFromDiskLocked(); @@ -385,6 +389,10 @@ public class ScribeTest { eReport.cumulativeNegativeRegulations, aReport.cumulativeNegativeRegulations); assertEquals("Reports #" + i + " numNegativeRegulations are not equal", eReport.numNegativeRegulations, aReport.numNegativeRegulations); + assertEquals("Reports #" + i + " screenOffDurationMs are not equal", + eReport.screenOffDurationMs, aReport.screenOffDurationMs); + assertEquals("Reports #" + i + " screenOffDischargeMah are not equal", + eReport.screenOffDischargeMah, aReport.screenOffDischargeMah); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java new file mode 100644 index 000000000000..47b4bf547ff8 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthResultCoordinatorTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import static com.google.common.truth.Truth.assertThat; + +import android.hardware.biometrics.BiometricManager; + +import org.junit.Before; +import org.junit.Test; + +public class AuthResultCoordinatorTest { + private AuthResultCoordinator mAuthResultCoordinator; + + @Before + public void setUp() throws Exception { + mAuthResultCoordinator = new AuthResultCoordinator(); + } + + @Test + public void testDefaultMessage() { + checkResult(mAuthResultCoordinator.getResult(), + AuthResult.FAILED, + BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE); + } + + @Test + public void testSingleMessageCoordinator() { + mAuthResultCoordinator.authenticatedFor( + BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE); + checkResult(mAuthResultCoordinator.getResult(), + AuthResult.AUTHENTICATED, + BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE); + } + + @Test + public void testLockout() { + mAuthResultCoordinator.lockedOutFor( + BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE); + checkResult(mAuthResultCoordinator.getResult(), + AuthResult.LOCKED_OUT, + BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE); + } + + @Test + public void testHigherStrengthPrecedence() { + mAuthResultCoordinator.authenticatedFor( + BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE); + mAuthResultCoordinator.authenticatedFor( + BiometricManager.Authenticators.BIOMETRIC_WEAK); + checkResult(mAuthResultCoordinator.getResult(), + AuthResult.AUTHENTICATED, + BiometricManager.Authenticators.BIOMETRIC_WEAK); + + mAuthResultCoordinator.authenticatedFor( + BiometricManager.Authenticators.BIOMETRIC_STRONG); + checkResult(mAuthResultCoordinator.getResult(), + AuthResult.AUTHENTICATED, + BiometricManager.Authenticators.BIOMETRIC_STRONG); + } + + @Test + public void testAuthPrecedence() { + mAuthResultCoordinator.authenticatedFor( + BiometricManager.Authenticators.BIOMETRIC_WEAK); + mAuthResultCoordinator.lockedOutFor( + BiometricManager.Authenticators.BIOMETRIC_WEAK); + checkResult(mAuthResultCoordinator.getResult(), + AuthResult.AUTHENTICATED, + BiometricManager.Authenticators.BIOMETRIC_WEAK); + + } + + void checkResult(AuthResult res, int status, + @BiometricManager.Authenticators.Types int strength) { + assertThat(res.getStatus()).isEqualTo(status); + assertThat(res.getBiometricStrength()).isEqualTo(strength); + } + +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java new file mode 100644 index 000000000000..9bb0f58db520 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +@Presubmit +@SmallTest +public class AuthSessionCoordinatorTest { + private static final int PRIMARY_USER = 0; + private static final int SECONDARY_USER = 10; + + private AuthSessionCoordinator mCoordinator; + + @Before + public void setUp() throws Exception { + mCoordinator = new AuthSessionCoordinator(); + } + + @Test + public void testUserUnlocked() { + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse(); + + mCoordinator.authStartedFor(PRIMARY_USER, 1); + mCoordinator.authenticatedFor(PRIMARY_USER, BIOMETRIC_WEAK, 1); + + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse(); + } + + @Test + public void testUserCanAuthDuringLockoutOfSameSession() { + mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_STRONG); + + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue(); + + mCoordinator.authStartedFor(PRIMARY_USER, 1); + mCoordinator.authStartedFor(PRIMARY_USER, 2); + mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_WEAK, 2); + + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue(); + } + + @Test + public void testMultiUserAuth() { + mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_STRONG); + + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue(); + + assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_CONVENIENCE)).isFalse(); + assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_WEAK)).isFalse(); + assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_STRONG)).isFalse(); + + mCoordinator.authStartedFor(PRIMARY_USER, 1); + mCoordinator.authStartedFor(PRIMARY_USER, 2); + mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_WEAK, 2); + + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue(); + assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue(); + + assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_CONVENIENCE)).isFalse(); + assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_WEAK)).isFalse(); + assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_STRONG)).isFalse(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java new file mode 100644 index 000000000000..8baa1ce3f6c6 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/MultiBiometricLockoutStateTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; +import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@Presubmit +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class MultiBiometricLockoutStateTest { + private static final int PRIMARY_USER = 0; + private MultiBiometricLockoutState mCoordinator; + + private void unlockAllBiometrics() { + unlockAllBiometrics(mCoordinator, PRIMARY_USER); + } + + private void lockoutAllBiometrics() { + lockoutAllBiometrics(mCoordinator, PRIMARY_USER); + } + + private static void unlockAllBiometrics(MultiBiometricLockoutState coordinator, int userId) { + coordinator.onUserUnlocked(userId, BIOMETRIC_STRONG); + assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_STRONG)).isTrue(); + assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_WEAK)).isTrue(); + assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_CONVENIENCE)).isTrue(); + } + + private static void lockoutAllBiometrics(MultiBiometricLockoutState coordinator, int userId) { + coordinator.onUserLocked(userId, BIOMETRIC_STRONG); + assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_STRONG)).isFalse(); + assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_WEAK)).isFalse(); + assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_CONVENIENCE)).isFalse(); + } + + @Before + public void setUp() throws Exception { + mCoordinator = new MultiBiometricLockoutState(); + } + + @Test + public void testInitialStateLockedOut() { + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse(); + } + + @Test + public void testConvenienceLockout() { + unlockAllBiometrics(); + mCoordinator.onUserLocked(PRIMARY_USER, BIOMETRIC_CONVENIENCE); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse(); + } + + @Test + public void testWeakLockout() { + unlockAllBiometrics(); + mCoordinator.onUserLocked(PRIMARY_USER, BIOMETRIC_WEAK); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse(); + } + + @Test + public void testStrongLockout() { + unlockAllBiometrics(); + mCoordinator.onUserLocked(PRIMARY_USER, BIOMETRIC_STRONG); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse(); + } + + @Test + public void testConvenienceUnlock() { + lockoutAllBiometrics(); + mCoordinator.onUserUnlocked(PRIMARY_USER, BIOMETRIC_CONVENIENCE); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue(); + } + + @Test + public void testWeakUnlock() { + lockoutAllBiometrics(); + mCoordinator.onUserUnlocked(PRIMARY_USER, BIOMETRIC_WEAK); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue(); + } + + @Test + public void testStrongUnlock() { + lockoutAllBiometrics(); + mCoordinator.onUserUnlocked(PRIMARY_USER, BIOMETRIC_STRONG); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue(); + assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue(); + } + + @Test + public void multiUser_userOneDoesNotAffectUserTwo() { + final int userOne = 1; + final int userTwo = 2; + MultiBiometricLockoutState coordinator = new MultiBiometricLockoutState(); + lockoutAllBiometrics(coordinator, userOne); + lockoutAllBiometrics(coordinator, userTwo); + + coordinator.onUserUnlocked(userOne, BIOMETRIC_WEAK); + assertThat(coordinator.canUserAuthenticate(userOne, BIOMETRIC_STRONG)).isFalse(); + assertThat(coordinator.canUserAuthenticate(userOne, BIOMETRIC_WEAK)).isTrue(); + assertThat(coordinator.canUserAuthenticate(userOne, BIOMETRIC_CONVENIENCE)).isTrue(); + + assertThat(coordinator.canUserAuthenticate(userTwo, BIOMETRIC_STRONG)).isFalse(); + assertThat(coordinator.canUserAuthenticate(userTwo, BIOMETRIC_WEAK)).isFalse(); + assertThat(coordinator.canUserAuthenticate(userTwo, BIOMETRIC_CONVENIENCE)).isFalse(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/tare/AnalystTest.java b/services/tests/servicestests/src/com/android/server/tare/AnalystTest.java index 2b527a261ae1..a603b93ab307 100644 --- a/services/tests/servicestests/src/com/android/server/tare/AnalystTest.java +++ b/services/tests/servicestests/src/com/android/server/tare/AnalystTest.java @@ -19,10 +19,14 @@ package com.android.server.tare; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.app.IBatteryStats; + import org.junit.Test; import org.junit.runner.RunWith; @@ -45,20 +49,20 @@ public class AnalystTest { final Analyst analyst = new Analyst(); Analyst.Report expected = new Analyst.Report(); - expected.currentBatteryLevel = 55; - analyst.noteBatteryLevelChange(55); + expected.currentBatteryLevel = 75; + analyst.noteBatteryLevelChange(75); assertEquals(1, analyst.getReports().size()); assertReportsEqual(expected, analyst.getReports().get(0)); // Discharging analyst.noteBatteryLevelChange(54); expected.currentBatteryLevel = 54; - expected.cumulativeBatteryDischarge = 1; + expected.cumulativeBatteryDischarge = 21; assertEquals(1, analyst.getReports().size()); assertReportsEqual(expected, analyst.getReports().get(0)); analyst.noteBatteryLevelChange(50); expected.currentBatteryLevel = 50; - expected.cumulativeBatteryDischarge = 5; + expected.cumulativeBatteryDischarge = 25; assertEquals(1, analyst.getReports().size()); assertReportsEqual(expected, analyst.getReports().get(0)); @@ -87,27 +91,53 @@ public class AnalystTest { } @Test - public void testTransaction_PeriodChange() { - final Analyst analyst = new Analyst(); + public void testTransaction_PeriodChange() throws Exception { + IBatteryStats iBatteryStats = mock(IBatteryStats.class); + final Analyst analyst = new Analyst(iBatteryStats); + // Reset from enough discharge. Analyst.Report expected = new Analyst.Report(); - expected.currentBatteryLevel = 55; - analyst.noteBatteryLevelChange(55); + expected.currentBatteryLevel = 75; + analyst.noteBatteryLevelChange(75); runTestTransactions(analyst, expected, 1); expected.currentBatteryLevel = 49; - expected.cumulativeBatteryDischarge = 6; + expected.cumulativeBatteryDischarge = 26; analyst.noteBatteryLevelChange(49); runTestTransactions(analyst, expected, 1); expected = new Analyst.Report(); - expected.currentBatteryLevel = 100; - analyst.noteBatteryLevelChange(100); + expected.currentBatteryLevel = 90; + analyst.noteBatteryLevelChange(90); expected.cumulativeBatteryDischarge = 0; runTestTransactions(analyst, expected, 2); + + // Reset from report being long enough. + doReturn(Analyst.MIN_REPORT_DURATION_FOR_RESET) + .when(iBatteryStats).computeBatteryScreenOffRealtimeMs(); + expected.currentBatteryLevel = 85; + analyst.noteBatteryLevelChange(85); + expected.cumulativeBatteryDischarge = 5; + expected.screenOffDurationMs = Analyst.MIN_REPORT_DURATION_FOR_RESET; + + runTestTransactions(analyst, expected, 2); + + expected.currentBatteryLevel = 79; + analyst.noteBatteryLevelChange(79); + expected.cumulativeBatteryDischarge = 11; + + runTestTransactions(analyst, expected, 2); + + expected = new Analyst.Report(); + expected.currentBatteryLevel = 80; + analyst.noteBatteryLevelChange(80); + expected.cumulativeBatteryDischarge = 0; + expected.screenOffDurationMs = 0; + + runTestTransactions(analyst, expected, 3); } private void runTestTransactions(Analyst analyst, Analyst.Report lastExpectedReport, @@ -223,6 +253,8 @@ public class AnalystTest { assertEquals(expected.numPositiveRegulations, actual.numPositiveRegulations); assertEquals(expected.cumulativeNegativeRegulations, actual.cumulativeNegativeRegulations); assertEquals(expected.numNegativeRegulations, actual.numNegativeRegulations); + assertEquals(expected.screenOffDurationMs, actual.screenOffDurationMs); + assertEquals(expected.screenOffDischargeMah, actual.screenOffDischargeMah); } private void assertReportListsEqual(List<Analyst.Report> expected, diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index 1aea6727d3a1..060c31f2985e 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -16,6 +16,8 @@ package com.android.server.timedetector; +import static com.android.server.SystemClockTime.TIME_CONFIDENCE_HIGH; +import static com.android.server.SystemClockTime.TIME_CONFIDENCE_LOW; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_EXTERNAL; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_GNSS; import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; @@ -35,6 +37,7 @@ import android.os.TimestampedValue; import androidx.test.runner.AndroidJUnit4; +import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.timedetector.TimeDetectorStrategy.Origin; import com.android.server.timezonedetector.ConfigurationChangeListener; @@ -42,6 +45,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.PrintWriter; import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; @@ -86,6 +90,7 @@ public class TimeDetectorStrategyImplTest { .setAutoDetectionSupported(true) .setSystemClockUpdateThresholdMillis( ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS) + .setSystemClockUpdateThresholdMillis(TIME_CONFIDENCE_HIGH) .setAutoSuggestionLowerBound(DEFAULT_SUGGESTION_LOWER_BOUND) .setManualSuggestionLowerBound(DEFAULT_SUGGESTION_LOWER_BOUND) .setSuggestionUpperBound(DEFAULT_SUGGESTION_UPPER_BOUND) @@ -99,6 +104,7 @@ public class TimeDetectorStrategyImplTest { .setAutoDetectionSupported(true) .setSystemClockUpdateThresholdMillis( ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS) + .setSystemClockUpdateThresholdMillis(TIME_CONFIDENCE_HIGH) .setAutoSuggestionLowerBound(DEFAULT_SUGGESTION_LOWER_BOUND) .setManualSuggestionLowerBound(DEFAULT_SUGGESTION_LOWER_BOUND) .setSuggestionUpperBound(DEFAULT_SUGGESTION_UPPER_BOUND) @@ -112,12 +118,14 @@ public class TimeDetectorStrategyImplTest { public void setUp() { mFakeEnvironment = new FakeEnvironment(); mFakeEnvironment.initializeConfig(CONFIG_AUTO_DISABLED); - mFakeEnvironment.initializeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO); + mFakeEnvironment.initializeFakeClocks( + ARBITRARY_CLOCK_INITIALIZATION_INFO, TIME_CONFIDENCE_LOW); } @Test public void testSuggestTelephonyTime_autoTimeEnabled() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); int slotIndex = ARBITRARY_SLOT_INDEX; Instant testTime = ARBITRARY_TEST_TIME; @@ -129,18 +137,21 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); - script.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) + script.verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion); } @Test public void testSuggestTelephonyTime_emptySuggestionIgnored() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); int slotIndex = ARBITRARY_SLOT_INDEX; TelephonyTimeSuggestion timeSuggestion = script.generateTelephonyTimeSuggestion(slotIndex, null); script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, null); } @@ -278,17 +289,115 @@ public class TimeDetectorStrategyImplTest { } } + /** + * If an auto suggested time matches the current system clock, the confidence in the current + * system clock is raised even when auto time is disabled. The system clock itself must not be + * changed. + */ @Test - public void testSuggestTelephonyTime_autoTimeDisabled() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED); + public void testSuggestTelephonyTime_autoTimeDisabled_suggestionMatchesSystemClock() { + TimestampedValue<Instant> initialClockTime = ARBITRARY_CLOCK_INITIALIZATION_INFO; + final int confidenceUpgradeThresholdMillis = 1000; + ConfigurationInternal configInternal = + new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) + .setSystemClockConfidenceUpgradeThresholdMillis( + confidenceUpgradeThresholdMillis) + .build(); + Script script = new Script() + .pokeFakeClocks(initialClockTime, TIME_CONFIDENCE_LOW) + .simulateConfigurationInternalChange(configInternal); int slotIndex = ARBITRARY_SLOT_INDEX; - TelephonyTimeSuggestion timeSuggestion = - script.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME); - script.simulateTimePassing() - .simulateTelephonyTimeSuggestion(timeSuggestion) - .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestTelephonySuggestion(slotIndex, timeSuggestion); + + script.simulateTimePassing(); + long timeElapsedMillis = + script.peekElapsedRealtimeMillis() - initialClockTime.getReferenceTimeMillis(); + + // Create a suggestion time that approximately matches the current system clock. + Instant suggestionInstant = initialClockTime.getValue() + .plusMillis(timeElapsedMillis) + .plusMillis(confidenceUpgradeThresholdMillis); + TimestampedValue<Long> matchingClockTime = new TimestampedValue<>( + script.peekElapsedRealtimeMillis(), + suggestionInstant.toEpochMilli()); + TelephonyTimeSuggestion timeSuggestion = new TelephonyTimeSuggestion.Builder(slotIndex) + .setUnixEpochTime(matchingClockTime) + .build(); + script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) + .verifySystemClockWasNotSetAndResetCallTracking(); + } + + /** + * If an auto suggested time doesn't match the current system clock, the confidence in the + * current system clock will stay where it is. The system clock itself must not be changed. + */ + @Test + public void testSuggestTelephonyTime_autoTimeDisabled_suggestionMismatchesSystemClock() { + TimestampedValue<Instant> initialClockTime = ARBITRARY_CLOCK_INITIALIZATION_INFO; + final int confidenceUpgradeThresholdMillis = 1000; + ConfigurationInternal configInternal = + new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) + .setSystemClockConfidenceUpgradeThresholdMillis( + confidenceUpgradeThresholdMillis) + .build(); + Script script = new Script().pokeFakeClocks(initialClockTime, TIME_CONFIDENCE_LOW) + .simulateConfigurationInternalChange(configInternal); + + int slotIndex = ARBITRARY_SLOT_INDEX; + + script.simulateTimePassing(); + long timeElapsedMillis = + script.peekElapsedRealtimeMillis() - initialClockTime.getReferenceTimeMillis(); + + // Create a suggestion time that doesn't match the current system clock closely enough. + Instant suggestionInstant = initialClockTime.getValue() + .plusMillis(timeElapsedMillis) + .plusMillis(confidenceUpgradeThresholdMillis + 1); + TimestampedValue<Long> mismatchingClockTime = new TimestampedValue<>( + script.peekElapsedRealtimeMillis(), + suggestionInstant.toEpochMilli()); + TelephonyTimeSuggestion timeSuggestion = new TelephonyTimeSuggestion.Builder(slotIndex) + .setUnixEpochTime(mismatchingClockTime) + .build(); + script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) + .verifySystemClockWasNotSetAndResetCallTracking(); + } + + /** + * If a suggested time doesn't match the current system clock, the confidence in the current + * system clock will not drop. + */ + @Test + public void testSuggestTelephonyTime_autoTimeDisabled_suggestionMismatchesSystemClock2() { + TimestampedValue<Instant> initialClockTime = ARBITRARY_CLOCK_INITIALIZATION_INFO; + final int confidenceUpgradeThresholdMillis = 1000; + ConfigurationInternal configInternal = + new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) + .setSystemClockConfidenceUpgradeThresholdMillis( + confidenceUpgradeThresholdMillis) + .build(); + Script script = new Script().pokeFakeClocks(initialClockTime, TIME_CONFIDENCE_HIGH) + .simulateConfigurationInternalChange(configInternal); + + int slotIndex = ARBITRARY_SLOT_INDEX; + + script.simulateTimePassing(); + long timeElapsedMillis = + script.peekElapsedRealtimeMillis() - initialClockTime.getReferenceTimeMillis(); + + // Create a suggestion time that doesn't closely match the current system clock. + Instant initialClockInstant = initialClockTime.getValue(); + TimestampedValue<Long> mismatchingClockTime = new TimestampedValue<>( + script.peekElapsedRealtimeMillis(), + initialClockInstant.plusMillis(timeElapsedMillis + 1_000_000).toEpochMilli()); + TelephonyTimeSuggestion timeSuggestion = new TelephonyTimeSuggestion.Builder(slotIndex) + .setUnixEpochTime(mismatchingClockTime) + .build(); + script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) + .verifySystemClockWasNotSetAndResetCallTracking(); } @Test @@ -298,7 +407,8 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) .setSystemClockUpdateThresholdMillis(systemClockUpdateThresholdMillis) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant testTime = ARBITRARY_TEST_TIME; int slotIndex = ARBITRARY_SLOT_INDEX; @@ -311,6 +421,7 @@ public class TimeDetectorStrategyImplTest { script.simulateTimePassing(); long expectedSystemClockMillis1 = script.calculateTimeInMillisForNow(unixEpochTime1); script.simulateTelephonyTimeSuggestion(timeSuggestion1) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); @@ -327,6 +438,7 @@ public class TimeDetectorStrategyImplTest { TelephonyTimeSuggestion timeSuggestion2 = createTelephonyTimeSuggestion(slotIndex, unixEpochTime2); script.simulateTelephonyTimeSuggestion(timeSuggestion2) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); @@ -339,6 +451,7 @@ public class TimeDetectorStrategyImplTest { TelephonyTimeSuggestion timeSuggestion3 = createTelephonyTimeSuggestion(slotIndex, unixEpochTime3); script.simulateTelephonyTimeSuggestion(timeSuggestion3) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); @@ -350,6 +463,7 @@ public class TimeDetectorStrategyImplTest { TelephonyTimeSuggestion timeSuggestion4 = createTelephonyTimeSuggestion(slotIndex, unixEpochTime4); script.simulateTelephonyTimeSuggestion(timeSuggestion4) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion4); } @@ -362,7 +476,8 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) .setSystemClockUpdateThresholdMillis(systemClockUpdateThresholdMillis) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); int slotIndex = ARBITRARY_SLOT_INDEX; Instant testTime = ARBITRARY_TEST_TIME; @@ -376,6 +491,7 @@ public class TimeDetectorStrategyImplTest { // Simulate the time signal being received. It should not be used because auto time // detection is off but it should be recorded. script.simulateTelephonyTimeSuggestion(timeSuggestion1) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); @@ -386,11 +502,13 @@ public class TimeDetectorStrategyImplTest { // Turn on auto time detection. script.simulateAutoTimeDetectionToggle() + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); // Turn off auto time detection. script.simulateAutoTimeDetectionToggle() + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); @@ -408,18 +526,21 @@ public class TimeDetectorStrategyImplTest { // The new time, though valid, should not be set in the system clock because auto time is // disabled. script.simulateTelephonyTimeSuggestion(timeSuggestion2) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, timeSuggestion2); // Turn on auto time detection. script.simulateAutoTimeDetectionToggle() + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2) .assertLatestTelephonySuggestion(slotIndex, timeSuggestion2); } @Test public void testSuggestTelephonyTime_maxSuggestionAge() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); int slotIndex = ARBITRARY_SLOT_INDEX; Instant testTime = ARBITRARY_TEST_TIME; @@ -431,6 +552,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(telephonySuggestion.getUnixEpochTime()); script.simulateTelephonyTimeSuggestion(telephonySuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking( expectedSystemClockMillis /* expectedNetworkBroadcast */) .assertLatestTelephonySuggestion(slotIndex, telephonySuggestion); @@ -442,7 +564,7 @@ public class TimeDetectorStrategyImplTest { script.simulateTimePassing(TimeDetectorStrategyImpl.MAX_SUGGESTION_TIME_AGE_MILLIS); // Look inside and check what the strategy considers the current best telephony suggestion. - // It should still be the, it's just no longer used. + // It should still be there, it's just no longer used. assertNull(script.peekBestTelephonySuggestion()); script.assertLatestTelephonySuggestion(slotIndex, telephonySuggestion); } @@ -454,12 +576,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_TELEPHONY) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1); TelephonyTimeSuggestion timeSuggestion = script.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, belowLowerBound); script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -470,12 +594,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_TELEPHONY) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1); TelephonyTimeSuggestion timeSuggestion = script.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, aboveLowerBound); script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -486,12 +612,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_TELEPHONY) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1); TelephonyTimeSuggestion timeSuggestion = script.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, aboveUpperBound); script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -502,18 +630,21 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_TELEPHONY) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1); TelephonyTimeSuggestion timeSuggestion = script.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, belowUpperBound); script.simulateTelephonyTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @Test public void testSuggestManualTime_autoTimeDisabled() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(ARBITRARY_TEST_TIME); @@ -524,12 +655,14 @@ public class TimeDetectorStrategyImplTest { script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @Test public void testSuggestManualTime_retainsAutoSignal() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); int slotIndex = ARBITRARY_SLOT_INDEX; @@ -544,6 +677,7 @@ public class TimeDetectorStrategyImplTest { long expectedAutoClockMillis = script.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUnixEpochTime()); script.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis) .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); @@ -552,6 +686,7 @@ public class TimeDetectorStrategyImplTest { // Switch to manual. script.simulateAutoTimeDetectionToggle() + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); @@ -568,6 +703,7 @@ public class TimeDetectorStrategyImplTest { script.calculateTimeInMillisForNow(manualTimeSuggestion.getUnixEpochTime()); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, manualTimeSuggestion, true /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedManualClockMillis) .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); @@ -580,17 +716,20 @@ public class TimeDetectorStrategyImplTest { expectedAutoClockMillis = script.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUnixEpochTime()); script.verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); // Switch back to manual - nothing should happen to the clock. script.simulateAutoTimeDetectionToggle() + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasNotSetAndResetCallTracking() .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); } @Test public void testSuggestManualTime_isIgnored_whenAutoTimeEnabled() { - Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED); + Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(ARBITRARY_TEST_TIME); @@ -598,6 +737,7 @@ public class TimeDetectorStrategyImplTest { script.simulateTimePassing() .simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -607,12 +747,14 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(aboveUpperBound); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -622,12 +764,14 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(belowUpperBound); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @@ -637,12 +781,14 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) .setManualSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(belowLowerBound); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -652,12 +798,14 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) .setManualSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(aboveLowerBound); script.simulateManualTimeSuggestion( ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -667,7 +815,8 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) .setOriginPriorities(ORIGIN_NETWORK) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME); @@ -677,6 +826,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); script.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @@ -703,12 +853,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_NETWORK) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1); NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(belowLowerBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -719,12 +871,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_NETWORK) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1); NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(aboveLowerBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -735,12 +889,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_NETWORK) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1); NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(aboveUpperBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -751,12 +907,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_NETWORK) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1); NetworkTimeSuggestion timeSuggestion = script.generateNetworkTimeSuggestion(belowUpperBound); script.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @@ -766,7 +924,8 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) .setOriginPriorities(ORIGIN_GNSS) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); GnssTimeSuggestion timeSuggestion = script.generateGnssTimeSuggestion(ARBITRARY_TEST_TIME); @@ -776,6 +935,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); script.simulateGnssTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @@ -802,12 +962,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_GNSS) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1); GnssTimeSuggestion timeSuggestion = script.generateGnssTimeSuggestion(belowLowerBound); script.simulateGnssTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -818,12 +980,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_GNSS) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1); GnssTimeSuggestion timeSuggestion = script.generateGnssTimeSuggestion(aboveLowerBound); script.simulateGnssTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -834,12 +998,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_GNSS) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1); GnssTimeSuggestion timeSuggestion = script.generateGnssTimeSuggestion(aboveUpperBound); script.simulateGnssTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -850,12 +1016,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_GNSS) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1); GnssTimeSuggestion timeSuggestion = script.generateGnssTimeSuggestion(belowUpperBound); script.simulateGnssTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @@ -865,7 +1033,8 @@ public class TimeDetectorStrategyImplTest { new ConfigurationInternal.Builder(CONFIG_AUTO_ENABLED) .setOriginPriorities(ORIGIN_EXTERNAL) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); ExternalTimeSuggestion timeSuggestion = script.generateExternalTimeSuggestion(ARBITRARY_TEST_TIME); @@ -875,6 +1044,7 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); script.simulateExternalTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } @@ -901,12 +1071,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_EXTERNAL) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1); ExternalTimeSuggestion timeSuggestion = script.generateExternalTimeSuggestion(belowLowerBound); script.simulateExternalTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -917,12 +1089,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_EXTERNAL) .setAutoSuggestionLowerBound(TEST_SUGGESTION_LOWER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1); ExternalTimeSuggestion timeSuggestion = script.generateExternalTimeSuggestion(aboveLowerBound); script.simulateExternalTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -933,12 +1107,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_EXTERNAL) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1); ExternalTimeSuggestion timeSuggestion = script.generateExternalTimeSuggestion(aboveUpperBound); script.simulateExternalTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -949,12 +1125,14 @@ public class TimeDetectorStrategyImplTest { .setOriginPriorities(ORIGIN_EXTERNAL) .setSuggestionUpperBound(TEST_SUGGESTION_UPPER_BOUND) .build(); - Script script = new Script().simulateConfigurationInternalChange(configInternal); + Script script = new Script().simulateConfigurationInternalChange(configInternal) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1); ExternalTimeSuggestion timeSuggestion = script.generateExternalTimeSuggestion(belowUpperBound); script.simulateExternalTimeSuggestion(timeSuggestion) + .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @@ -1472,6 +1650,7 @@ public class TimeDetectorStrategyImplTest { private boolean mWakeLockAcquired; private long mElapsedRealtimeMillis; private long mSystemClockMillis; + private int mSystemClockConfidence = TIME_CONFIDENCE_LOW; private ConfigurationChangeListener mConfigurationInternalChangeListener; // Tracking operations. @@ -1481,9 +1660,10 @@ public class TimeDetectorStrategyImplTest { mConfigurationInternal = configurationInternal; } - public void initializeFakeClocks(TimestampedValue<Instant> timeInfo) { + public void initializeFakeClocks( + TimestampedValue<Instant> timeInfo, @TimeConfidence int timeConfidence) { pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis()); - pokeSystemClockMillis(timeInfo.getValue().toEpochMilli()); + pokeSystemClockMillis(timeInfo.getValue().toEpochMilli(), timeConfidence); } @Override @@ -1515,10 +1695,23 @@ public class TimeDetectorStrategyImplTest { } @Override - public void setSystemClock(long newTimeMillis) { + public @TimeConfidence int systemClockConfidence() { + return mSystemClockConfidence; + } + + @Override + public void setSystemClock( + long newTimeMillis, @TimeConfidence int confidence, String logMsg) { assertWakeLockAcquired(); mSystemClockWasSet = true; mSystemClockMillis = newTimeMillis; + mSystemClockConfidence = confidence; + } + + @Override + public void setSystemClockConfidence(@TimeConfidence int confidence, String logMsg) { + assertWakeLockAcquired(); + mSystemClockConfidence = confidence; } @Override @@ -1527,6 +1720,16 @@ public class TimeDetectorStrategyImplTest { mWakeLockAcquired = false; } + @Override + public void addDebugLogEntry(String logMsg) { + // No-op for tests + } + + @Override + public void dumpDebugLog(PrintWriter printWriter) { + // No-op for tests + } + // Methods below are for managing the fake's behavior. void simulateConfigurationInternalChange(ConfigurationInternal configurationInternal) { @@ -1538,8 +1741,9 @@ public class TimeDetectorStrategyImplTest { mElapsedRealtimeMillis = elapsedRealtimeMillis; } - void pokeSystemClockMillis(long systemClockMillis) { + void pokeSystemClockMillis(long systemClockMillis, @TimeConfidence int timeConfidence) { mSystemClockMillis = systemClockMillis; + mSystemClockConfidence = timeConfidence; } long peekElapsedRealtimeMillis() { @@ -1567,6 +1771,10 @@ public class TimeDetectorStrategyImplTest { assertEquals(expectedSystemClockMillis, mSystemClockMillis); } + public void verifySystemClockConfidence(@TimeConfidence int expectedConfidence) { + assertEquals(expectedConfidence, mSystemClockConfidence); + } + void resetCallTracking() { mSystemClockWasSet = false; } @@ -1589,6 +1797,14 @@ public class TimeDetectorStrategyImplTest { mTimeDetectorStrategy = new TimeDetectorStrategyImpl(mFakeEnvironment); } + Script pokeFakeClocks(TimestampedValue<Instant> initialClockTime, + @TimeConfidence int timeConfidence) { + mFakeEnvironment.pokeElapsedRealtimeMillis(initialClockTime.getReferenceTimeMillis()); + mFakeEnvironment.pokeSystemClockMillis( + initialClockTime.getValue().toEpochMilli(), timeConfidence); + return this; + } + long peekElapsedRealtimeMillis() { return mFakeEnvironment.peekElapsedRealtimeMillis(); } @@ -1675,6 +1891,11 @@ public class TimeDetectorStrategyImplTest { return this; } + Script verifySystemClockConfidence(@TimeConfidence int expectedConfidence) { + mFakeEnvironment.verifySystemClockConfidence(expectedConfidence); + return this; + } + /** * White box test info: Asserts the latest suggestion for the slotIndex is as expected. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index 6128428dcb7d..b46e90da3944 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -1312,13 +1312,15 @@ public class RootTaskTests extends WindowTestsBase { secondActivity.app.setThread(null); // This should do nothing from a non-attached caller. assertFalse(task.navigateUpTo(secondActivity /* source record */, - firstActivity.intent /* destIntent */, null /* destGrants */, - 0 /* resultCode */, null /* resultData */, null /* resultGrants */)); + firstActivity.intent /* destIntent */, null /* resolvedType */, + null /* destGrants */, 0 /* resultCode */, null /* resultData */, + null /* resultGrants */)); secondActivity.app.setThread(thread); assertTrue(task.navigateUpTo(secondActivity /* source record */, - firstActivity.intent /* destIntent */, null /* destGrants */, - 0 /* resultCode */, null /* resultData */, null /* resultGrants */)); + firstActivity.intent /* destIntent */, null /* resolvedType */, + null /* destGrants */, 0 /* resultCode */, null /* resultData */, + null /* resultGrants */)); // The firstActivity uses default launch mode, so the activities between it and itself will // be finished. assertTrue(secondActivity.finishing); diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 8010a4ae3d56..193c2c143f98 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1906,30 +1906,6 @@ public class SubscriptionManager { } /** - * @return the count of all subscriptions in the database, this includes - * all subscriptions that have been seen. - * @hide - */ - @UnsupportedAppUsage - public int getAllSubscriptionInfoCount() { - if (VDBG) logd("[getAllSubscriptionInfoCount]+"); - - int result = 0; - - try { - ISub iSub = TelephonyManager.getSubscriptionService(); - if (iSub != null) { - result = iSub.getAllSubInfoCount(mContext.getOpPackageName(), - mContext.getAttributionTag()); - } - } catch (RemoteException ex) { - // ignore it - } - - return result; - } - - /** * * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see @@ -2327,12 +2303,6 @@ public class SubscriptionManager { } } - /** @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public int getDefaultSmsPhoneId() { - return getPhoneId(getDefaultSmsSubscriptionId()); - } - /** * Returns the system's default data subscription id. * @@ -2381,12 +2351,6 @@ public class SubscriptionManager { } /** @hide */ - @UnsupportedAppUsage - public int getDefaultDataPhoneId() { - return getPhoneId(getDefaultDataSubscriptionId()); - } - - /** @hide */ public void clearSubscriptionInfo() { try { ISub iSub = TelephonyManager.getSubscriptionService(); diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index 1e38b9215d76..917f35bc1b82 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -30,14 +30,6 @@ interface ISub { List<SubscriptionInfo> getAllSubInfoList(String callingPackage, String callingFeatureId); /** - * @param callingPackage The package maing the call. - * @param callingFeatureId The feature in the package - * @return the count of all subscriptions in the database, this includes - * all subscriptions that have been seen. - */ - int getAllSubInfoCount(String callingPackage, String callingFeatureId); - - /** * Get the active SubscriptionInfo with the subId key * @param subId The unique SubscriptionInfo key in database * @param callingPackage The package maing the call. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt index f77ec8a4a853..8a1e1fabd131 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt @@ -31,46 +31,40 @@ import org.junit.Test * [ComponentNameMatcher.TASK_BAR], [ComponentNameMatcher.STATUS_BAR], and general assertions * (layers visible in consecutive states, entire screen covered, etc.) */ -abstract class BaseTest @JvmOverloads constructor( +abstract class BaseTest +@JvmOverloads +constructor( protected val testSpec: FlickerTestParameter, protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(), protected val tapl: LauncherInstrumentation = LauncherInstrumentation() ) { init { testSpec.setIsTablet( - WindowManagerStateHelper( - instrumentation, - clearCacheAfterParsing = false - ).currentState.wmState.isTablet + WindowManagerStateHelper(instrumentation, clearCacheAfterParsing = false) + .currentState + .wmState + .isTablet ) tapl.setExpectedRotationCheckEnabled(true) } - /** - * Specification of the test transition to execute - */ + /** Specification of the test transition to execute */ abstract val transition: FlickerBuilder.() -> Unit /** - * Entry point for the test runner. It will use this method to initialize and cache - * flicker executions + * Entry point for the test runner. It will use this method to initialize and cache flicker + * executions */ @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { - setup { - testSpec.setIsTablet(wmHelper.currentState.wmState.isTablet) - } + setup { testSpec.setIsTablet(wmHelper.currentState.wmState.isTablet) } transition() } } - /** - * Checks that all parts of the screen are covered during the transition - */ - @Presubmit - @Test - open fun entireScreenCovered() = testSpec.entireScreenCovered() + /** Checks that all parts of the screen are covered during the transition */ + @Presubmit @Test open fun entireScreenCovered() = testSpec.entireScreenCovered() /** * Checks that the [ComponentNameMatcher.NAV_BAR] layer is visible during the whole transition @@ -110,8 +104,8 @@ abstract class BaseTest @JvmOverloads constructor( } /** - * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible at the start and end of - * the transition + * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible at the start and end of the + * transition * * Note: Large screen only */ @@ -123,8 +117,7 @@ abstract class BaseTest @JvmOverloads constructor( } /** - * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole - * transition + * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition * * Note: Large screen only */ @@ -136,8 +129,8 @@ abstract class BaseTest @JvmOverloads constructor( } /** - * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible at the start and end - * of the transition + * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible at the start and end of + * the transition */ @Presubmit @Test @@ -161,26 +154,22 @@ abstract class BaseTest @JvmOverloads constructor( open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() /** - * Checks that all layers that are visible on the trace, are visible for at least 2 - * consecutive entries. + * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive + * entries. */ @Presubmit @Test open fun visibleLayersShownMoreThanOneConsecutiveEntry() { - testSpec.assertLayers { - this.visibleLayersShownMoreThanOneConsecutiveEntry() - } + testSpec.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry() } } /** - * Checks that all windows that are visible on the trace, are visible for at least 2 - * consecutive entries. + * Checks that all windows that are visible on the trace, are visible for at least 2 consecutive + * entries. */ @Presubmit @Test open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { - testSpec.assertWm { - this.visibleWindowsShownMoreThanOneConsecutiveEntry() - } + testSpec.assertWm { this.visibleWindowsShownMoreThanOneConsecutiveEntry() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt index c6a7c887c0c7..bbffd0881775 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -15,6 +15,7 @@ */ @file:JvmName("CommonAssertions") + package com.android.server.wm.flicker import com.android.server.wm.flicker.helpers.WindowUtils @@ -23,28 +24,24 @@ import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.common.IComponentNameMatcher /** - * Checks that [ComponentNameMatcher.STATUS_BAR] window is visible and above the app windows in - * all WM trace entries + * Checks that [ComponentNameMatcher.STATUS_BAR] window is visible and above the app windows in all + * WM trace entries */ fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() { - assertWm { - this.isAboveAppWindowVisible(ComponentNameMatcher.STATUS_BAR) - } + assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.STATUS_BAR) } } /** - * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows in - * all WM trace entries + * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows in all WM + * trace entries */ fun FlickerTestParameter.navBarWindowIsAlwaysVisible() { - assertWm { - this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) - } + assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) } } /** - * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the start - * and end of the WM trace + * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the + * start and end of the WM trace */ fun FlickerTestParameter.navBarWindowIsVisibleAtStartAndEnd() { this.navBarWindowIsVisibleAtStart() @@ -52,51 +49,43 @@ fun FlickerTestParameter.navBarWindowIsVisibleAtStartAndEnd() { } /** - * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the start - * of the WM trace + * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the + * start of the WM trace */ fun FlickerTestParameter.navBarWindowIsVisibleAtStart() { - assertWmStart { - this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) - } + assertWmStart { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) } } /** - * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the - * end of the WM trace + * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the end + * of the WM trace */ fun FlickerTestParameter.navBarWindowIsVisibleAtEnd() { - assertWmEnd { - this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) - } + assertWmEnd { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) } } /** - * Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in - * all WM trace entries + * Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in all WM + * trace entries */ fun FlickerTestParameter.taskBarWindowIsAlwaysVisible() { - assertWm { - this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) - } + assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) } } /** - * Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in - * all WM trace entries + * Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in all WM + * trace entries */ fun FlickerTestParameter.taskBarWindowIsVisibleAtEnd() { - assertWmEnd { - this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) - } + assertWmEnd { this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) } } /** - * If [allStates] is true, checks if the stack space of all displays is fully covered - * by any visible layer, during the whole transitions + * If [allStates] is true, checks if the stack space of all displays is fully covered by any visible + * layer, during the whole transitions * - * Otherwise, checks if the stack space of all displays is fully covered - * by any visible layer, at the start and end of the transition + * Otherwise, checks if the stack space of all displays is fully covered by any visible layer, at + * the start and end of the transition * * @param allStates if all states should be checked, othersie, just initial and final */ @@ -124,27 +113,18 @@ fun FlickerTestParameter.entireScreenCovered(allStates: Boolean = true) { } } -/** - * Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start of the SF trace - */ +/** Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start of the SF trace */ fun FlickerTestParameter.navBarLayerIsVisibleAtStart() { - assertLayersStart { - this.isVisible(ComponentNameMatcher.NAV_BAR) - } + assertLayersStart { this.isVisible(ComponentNameMatcher.NAV_BAR) } } -/** - * Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the end of the SF trace - */ +/** Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the end of the SF trace */ fun FlickerTestParameter.navBarLayerIsVisibleAtEnd() { - assertLayersEnd { - this.isVisible(ComponentNameMatcher.NAV_BAR) - } + assertLayersEnd { this.isVisible(ComponentNameMatcher.NAV_BAR) } } /** - * Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start and end of the SF - * trace + * Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start and end of the SF trace */ fun FlickerTestParameter.navBarLayerIsVisibleAtStartAndEnd() { this.navBarLayerIsVisibleAtStart() @@ -152,32 +132,21 @@ fun FlickerTestParameter.navBarLayerIsVisibleAtStartAndEnd() { } /** - * Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start and end of the SF - * trace + * Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start and end of the SF trace */ fun FlickerTestParameter.taskBarLayerIsVisibleAtStartAndEnd() { this.taskBarLayerIsVisibleAtStart() this.taskBarLayerIsVisibleAtEnd() } -/** - * Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start of the SF - * trace - */ +/** Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start of the SF trace */ fun FlickerTestParameter.taskBarLayerIsVisibleAtStart() { - assertLayersStart { - this.isVisible(ComponentNameMatcher.TASK_BAR) - } + assertLayersStart { this.isVisible(ComponentNameMatcher.TASK_BAR) } } -/** - * Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the end of the SF - * trace - */ +/** Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the end of the SF trace */ fun FlickerTestParameter.taskBarLayerIsVisibleAtEnd() { - assertLayersEnd { - this.isVisible(ComponentNameMatcher.TASK_BAR) - } + assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) } } /** @@ -185,43 +154,40 @@ fun FlickerTestParameter.taskBarLayerIsVisibleAtEnd() { * trace */ fun FlickerTestParameter.statusBarLayerIsVisibleAtStartAndEnd() { - assertLayersStart { - this.isVisible(ComponentNameMatcher.STATUS_BAR) - } - assertLayersEnd { - this.isVisible(ComponentNameMatcher.STATUS_BAR) - } + assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) } + assertLayersEnd { this.isVisible(ComponentNameMatcher.STATUS_BAR) } } /** - * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start - * of the SF trace + * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start of + * the SF trace */ fun FlickerTestParameter.navBarLayerPositionAtStart() { assertLayersStart { - val display = this.entry.displays.firstOrNull { !it.isVirtual } - ?: error("There is no display!") + val display = + this.entry.displays.firstOrNull { !it.isVirtual } ?: error("There is no display!") this.visibleRegion(ComponentNameMatcher.NAV_BAR) .coversExactly(WindowUtils.getNavigationBarPosition(display, isGesturalNavigation)) } } /** - * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the end - * of the SF trace + * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the end of + * the SF trace */ fun FlickerTestParameter.navBarLayerPositionAtEnd() { assertLayersEnd { - val display = this.entry.displays.minByOrNull { it.id } - ?: throw RuntimeException("There is no display!") + val display = + this.entry.displays.minByOrNull { it.id } + ?: throw RuntimeException("There is no display!") this.visibleRegion(ComponentNameMatcher.NAV_BAR) .coversExactly(WindowUtils.getNavigationBarPosition(display, isGesturalNavigation)) } } /** - * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start - * and end of the SF trace + * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start and + * end of the SF trace */ fun FlickerTestParameter.navBarLayerPositionAtStartAndEnd() { navBarLayerPositionAtStart() @@ -234,21 +200,23 @@ fun FlickerTestParameter.navBarLayerPositionAtStartAndEnd() { */ fun FlickerTestParameter.statusBarLayerPositionAtStart() { assertLayersStart { - val display = this.entry.displays.minByOrNull { it.id } - ?: throw RuntimeException("There is no display!") + val display = + this.entry.displays.minByOrNull { it.id } + ?: throw RuntimeException("There is no display!") this.visibleRegion(ComponentNameMatcher.STATUS_BAR) .coversExactly(WindowUtils.getStatusBarPosition(display)) } } /** - * Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the end - * of the SF trace + * Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the end of + * the SF trace */ fun FlickerTestParameter.statusBarLayerPositionAtEnd() { assertLayersEnd { - val display = this.entry.displays.minByOrNull { it.id } - ?: throw RuntimeException("There is no display!") + val display = + this.entry.displays.minByOrNull { it.id } + ?: throw RuntimeException("There is no display!") this.visibleRegion(ComponentNameMatcher.STATUS_BAR) .coversExactly(WindowUtils.getStatusBarPosition(display)) } @@ -264,23 +232,25 @@ fun FlickerTestParameter.statusBarLayerPositionAtStartAndEnd() { } /** - * Asserts that the visibleRegion of the [ComponentNameMatcher.SNAPSHOT] layer can cover - * the visibleRegion of the given app component exactly + * Asserts that the visibleRegion of the [ComponentNameMatcher.SNAPSHOT] layer can cover the + * visibleRegion of the given app component exactly */ fun FlickerTestParameter.snapshotStartingWindowLayerCoversExactlyOnApp( component: IComponentNameMatcher ) { assertLayers { invoke("snapshotStartingWindowLayerCoversExactlyOnApp") { - val snapshotLayers = it.subjects.filter { subject -> - subject.name.contains( - ComponentNameMatcher.SNAPSHOT.toLayerName()) && subject.isVisible - } + val snapshotLayers = + it.subjects.filter { subject -> + subject.name.contains(ComponentNameMatcher.SNAPSHOT.toLayerName()) && + subject.isVisible + } // Verify the size of snapshotRegion covers appVisibleRegion exactly in animation. if (snapshotLayers.isNotEmpty()) { - val visibleAreas = snapshotLayers.mapNotNull { snapshotLayer -> - snapshotLayer.layer?.visibleRegion - }.toTypedArray() + val visibleAreas = + snapshotLayers + .mapNotNull { snapshotLayer -> snapshotLayer.layer?.visibleRegion } + .toTypedArray() val snapshotRegion = RegionSubject.assertThat(visibleAreas, this, timestamp) val appVisibleRegion = it.visibleRegion(component) if (snapshotRegion.region.isNotEmpty) { @@ -293,22 +263,33 @@ fun FlickerTestParameter.snapshotStartingWindowLayerCoversExactlyOnApp( /** * Asserts that: + * ``` * [originalLayer] is visible at the start of the trace * [originalLayer] becomes invisible during the trace and (in the same entry) [newLayer] * becomes visible * [newLayer] remains visible until the end of the trace * - * @param originalLayer Layer that should be visible at the start + * @param originalLayer + * ``` + * Layer that should be visible at the start * @param newLayer Layer that should be visible at the end * @param ignoreEntriesWithRotationLayer If entries with a visible rotation layer should be ignored + * ``` * when checking the transition. If true we will not fail the assertion if a rotation layer is * visible to fill the gap between the [originalLayer] being visible and the [newLayer] being * visible. - * @param ignoreSnapshot If the snapshot layer should be ignored during the transition + * @param ignoreSnapshot + * ``` + * If the snapshot layer should be ignored during the transition + * ``` * (useful mostly for app launch) - * @param ignoreSplashscreen If the splashscreen layer should be ignored during the transition. + * @param ignoreSplashscreen + * ``` + * If the splashscreen layer should be ignored during the transition. + * ``` * If true then we will allow for a splashscreen to be shown before the layer is shown, * otherwise we won't and the layer must appear immediately. + * ``` */ fun FlickerTestParameter.replacesLayer( originalLayer: IComponentNameMatcher, @@ -333,13 +314,7 @@ fun FlickerTestParameter.replacesLayer( assertion.then().isVisible(newLayer) } - assertLayersStart { - this.isVisible(originalLayer) - .isInvisible(newLayer) - } + assertLayersStart { this.isVisible(originalLayer).isInvisible(newLayer) } - assertLayersEnd { - this.isInvisible(originalLayer) - .isVisible(newLayer) - } + assertLayersEnd { this.isInvisible(originalLayer).isVisible(newLayer) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt index 79654e36a888..b23fb5a284c1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplit.kt @@ -41,9 +41,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenActivityEmbeddingPlaceholderSplit( - testSpec: FlickerTestParameter -) : ActivityEmbeddingTestBase(testSpec) { +class OpenActivityEmbeddingPlaceholderSplit(testSpec: FlickerTestParameter) : + ActivityEmbeddingTestBase(testSpec) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit = { @@ -51,9 +50,7 @@ class OpenActivityEmbeddingPlaceholderSplit( tapl.setExpectedRotationCheckEnabled(false) testApp.launchViaIntent(wmHelper) } - transitions { - testApp.launchPlaceholderSplit(wmHelper) - } + transitions { testApp.launchPlaceholderSplit(wmHelper) } teardown { tapl.goHome() testApp.exit(wmHelper) @@ -65,8 +62,8 @@ class OpenActivityEmbeddingPlaceholderSplit( fun mainActivityBecomesInvisible() { testSpec.assertLayers { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - .then() - .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } } @@ -75,20 +72,18 @@ class OpenActivityEmbeddingPlaceholderSplit( fun placeholderSplitBecomesVisible() { testSpec.assertLayers { isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) - .then() - .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) } testSpec.assertLayers { isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) - .then() - .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) } } /** {@inheritDoc} */ - @Presubmit - @Test - override fun entireScreenCovered() = super.entireScreenCovered() + @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ @Presubmit @@ -124,14 +119,12 @@ class OpenActivityEmbeddingPlaceholderSplit( /** {@inheritDoc} */ @Presubmit @Test - override fun statusBarLayerPositionAtStartAndEnd() = - super.statusBarLayerPositionAtStartAndEnd() + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ @Presubmit @Test - override fun statusBarWindowIsAlwaysVisible() = - super.statusBarWindowIsAlwaysVisible() + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ @Presubmit @@ -149,20 +142,21 @@ class OpenActivityEmbeddingPlaceholderSplit( /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests( - supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90), - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) - ) + .getConfigNonRotationTests( + supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90), + supportedNavigationModes = + listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) + ) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt index 8fdd37d3777c..b16bfe070374 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt @@ -1,4 +1,3 @@ - /* * Copyright (C) 2020 The Android Open Source Project * @@ -36,28 +35,34 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:CloseAppBackButtonTest` * * Actions: + * ``` * Make sure no apps are running on the device * Launch an app [testApp] and wait animation to complete * Press back button - * + * ``` * To run only the presubmit assertions add: `-- + * ``` * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit` - * + * ``` * To run only the postsubmit assertions add: `-- + * ``` * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit` - * + * ``` * To run only the flaky assertions add: `-- + * ``` * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest` - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [CloseAppTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @FlickerServiceCompatible @@ -71,9 +76,7 @@ class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio super.transition(this) transitions { tapl.pressBack() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() } } @@ -86,14 +89,13 @@ class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt index 765be20ea1bd..78d0860f2636 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt @@ -35,28 +35,34 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:CloseAppHomeButtonTest` * * Actions: + * ``` * Make sure no apps are running on the device * Launch an app [testApp] and wait animation to complete * Press home button - * + * ``` * To run only the presubmit assertions add: `-- + * ``` * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit` - * + * ``` * To run only the postsubmit assertions add: `-- + * ``` * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit` - * + * ``` * To run only the flaky assertions add: `-- + * ``` * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest` - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [CloseAppTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @FlickerServiceCompatible @@ -68,18 +74,14 @@ class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio override val transition: FlickerBuilder.() -> Unit get() = { super.transition(this) - setup { - tapl.setExpectedRotationCheckEnabled(false) - } + setup { tapl.setExpectedRotationCheckEnabled(false) } transitions { // Can't use TAPL at the moment because of rotation test issues // When pressing home, TAPL expects the orientation to remain constant // However, when closing a landscape app back to a portrait-only launcher // this causes an error in verifyActiveContainer(); tapl.goHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() } } @@ -92,14 +94,13 @@ class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index f296d9746aef..5bb227f36333 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -27,9 +27,7 @@ import com.android.server.wm.flicker.replacesLayer import com.android.server.wm.traces.common.ComponentNameMatcher.Companion.LAUNCHER import org.junit.Test -/** - * Base test class for transitions that close an app back to the launcher screen - */ +/** Base test class for transitions that close an app back to the launcher screen */ abstract class CloseAppTransition(testSpec: FlickerTestParameter) : BaseTest(testSpec) { protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation) @@ -40,42 +38,30 @@ abstract class CloseAppTransition(testSpec: FlickerTestParameter) : BaseTest(tes testApp.launchViaIntent(wmHelper) this.setRotation(testSpec.startRotation) } - teardown { - testApp.exit(wmHelper) - } + teardown { testApp.exit(wmHelper) } } /** - * Checks that [testApp] is the top visible app window at the start of the transition and - * that it is replaced by [LAUNCHER] during the transition + * Checks that [testApp] is the top visible app window at the start of the transition and that + * it is replaced by [LAUNCHER] during the transition */ @Presubmit @Test open fun launcherReplacesAppWindowAsTopWindow() { - testSpec.assertWm { - this.isAppWindowOnTop(testApp) - .then() - .isAppWindowOnTop(LAUNCHER) - } + testSpec.assertWm { this.isAppWindowOnTop(testApp).then().isAppWindowOnTop(LAUNCHER) } } /** - * Checks that [LAUNCHER] is invisible at the start of the transition and that - * it becomes visible during the transition + * Checks that [LAUNCHER] is invisible at the start of the transition and that it becomes + * visible during the transition */ @Presubmit @Test open fun launcherWindowBecomesVisible() { - testSpec.assertWm { - this.isAppWindowNotOnTop(LAUNCHER) - .then() - .isAppWindowOnTop(LAUNCHER) - } + testSpec.assertWm { this.isAppWindowNotOnTop(LAUNCHER).then().isAppWindowOnTop(LAUNCHER) } } - /** - * Checks that [LAUNCHER] layer becomes visible when [testApp] becomes invisible - */ + /** Checks that [LAUNCHER] layer becomes visible when [testApp] becomes invisible */ @Presubmit @Test open fun launcherLayerReplacesApp() { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt index ef5cec29f898..48e1e64a05eb 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt @@ -32,13 +32,14 @@ import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.Assume.assumeNotNull -class ActivityEmbeddingAppHelper @JvmOverloads constructor( +class ActivityEmbeddingAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.ActivityEmbedding.MainActivity.LABEL, component: ComponentNameMatcher = MAIN_ACTIVITY_COMPONENT, - launcherStrategy: ILauncherStrategy = LauncherStrategyFactory - .getInstance(instr) - .launcherStrategy + launcherStrategy: ILauncherStrategy = + LauncherStrategyFactory.getInstance(instr).launcherStrategy ) : StandardAppHelper(instr, launcherName, component, launcherStrategy) { /** @@ -46,14 +47,15 @@ class ActivityEmbeddingAppHelper @JvmOverloads constructor( * placeholder secondary activity based on the placeholder rule. */ fun launchPlaceholderSplit(wmHelper: WindowManagerStateHelper) { - val launchButton = uiDevice.wait( - Until.findObject(By.res(getPackage(), "launch_placeholder_split_button")), - FIND_TIMEOUT) - require(launchButton != null) { - "Can't find launch placeholder split button on screen." - } + val launchButton = + uiDevice.wait( + Until.findObject(By.res(getPackage(), "launch_placeholder_split_button")), + FIND_TIMEOUT + ) + require(launchButton != null) { "Can't find launch placeholder split button on screen." } launchButton.click() - wmHelper.StateSyncBuilder() + wmHelper + .StateSyncBuilder() .withActivityState(PLACEHOLDER_PRIMARY_COMPONENT, STATE_RESUMED) .withActivityState(PLACEHOLDER_SECONDARY_COMPONENT, STATE_RESUMED) .waitForAndVerify() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt index 34f9ce43b4bc..a2d4d3a01a4a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt @@ -23,12 +23,18 @@ import android.content.pm.ResolveInfo import android.provider.MediaStore import com.android.server.wm.traces.common.ComponentNameMatcher -class CameraAppHelper @JvmOverloads constructor( +class CameraAppHelper +@JvmOverloads +constructor( instrumentation: Instrumentation, pkgManager: PackageManager = instrumentation.context.packageManager -) : StandardAppHelper(instrumentation, getCameraLauncherName(pkgManager), - getCameraComponent(pkgManager)){ - companion object{ +) : + StandardAppHelper( + instrumentation, + getCameraLauncherName(pkgManager), + getCameraComponent(pkgManager) + ) { + companion object { private fun getCameraIntent(): Intent { return Intent(MediaStore.ACTION_IMAGE_CAPTURE) } @@ -36,13 +42,15 @@ class CameraAppHelper @JvmOverloads constructor( private fun getResolveInfo(pkgManager: PackageManager): ResolveInfo { val intent = getCameraIntent() return pkgManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) - ?: error("unable to resolve camera activity") + ?: error("unable to resolve camera activity") } private fun getCameraComponent(pkgManager: PackageManager): ComponentNameMatcher { val resolveInfo = getResolveInfo(pkgManager) - return ComponentNameMatcher(resolveInfo.activityInfo.packageName, - className = resolveInfo.activityInfo.name) + return ComponentNameMatcher( + resolveInfo.activityInfo.packageName, + className = resolveInfo.activityInfo.name + ) } private fun getCameraLauncherName(pkgManager: PackageManager): String { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt index 132e7b6fef6d..4340bd788046 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt @@ -23,12 +23,13 @@ import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent -class FixedOrientationAppHelper @JvmOverloads constructor( +class FixedOrientationAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.PortraitOnlyActivity.LABEL, component: ComponentNameMatcher = ActivityOptions.PortraitOnlyActivity.COMPONENT.toFlickerComponent(), - launcherStrategy: ILauncherStrategy = LauncherStrategyFactory - .getInstance(instr) - .launcherStrategy + launcherStrategy: ILauncherStrategy = + LauncherStrategyFactory.getInstance(instr).launcherStrategy ) : StandardAppHelper(instr, launcherName, component, launcherStrategy) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt index d08cb5525d5e..73cb8627a068 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt @@ -15,6 +15,7 @@ */ @file:JvmName("FlickerExtensions") + package com.android.server.wm.flicker.helpers import com.android.server.wm.flicker.Flicker @@ -31,4 +32,4 @@ fun Flicker.setRotation(rotation: Int) = instrumentation, clearCacheAfterParsing = false, wmHelper = wmHelper -) + ) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt index 2d81e0d9e165..d45315e68e37 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt @@ -27,7 +27,9 @@ import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper -class GameAppHelper @JvmOverloads constructor( +class GameAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.Game.LABEL, component: ComponentNameMatcher = ActivityOptions.Game.COMPONENT.toFlickerComponent(), @@ -41,13 +43,18 @@ class GameAppHelper @JvmOverloads constructor( * @return true if the swipe operation is successful. */ fun swipeDown(): Boolean { - val gameView = uiDevice.wait( - Until.findObject(By.res(getPackage(), GAME_APP_VIEW_RES)), WAIT_TIME_MS) + val gameView = + uiDevice.wait(Until.findObject(By.res(getPackage(), GAME_APP_VIEW_RES)), WAIT_TIME_MS) require(gameView != null) { "Mock game app view not found." } val bound = gameView.getVisibleBounds() return uiDevice.swipe( - bound.centerX(), bound.top, bound.centerX(), bound.centerY(), SWIPE_STEPS) + bound.centerX(), + bound.top, + bound.centerX(), + bound.centerY(), + SWIPE_STEPS + ) } /** @@ -63,21 +70,27 @@ class GameAppHelper @JvmOverloads constructor( wmHelper: WindowManagerStateHelper, direction: Direction ): Boolean { - val ratioForScreenBottom = 0.97 + val ratioForScreenBottom = 0.99 val fullView = wmHelper.getWindowRegion(componentMatcher) require(!fullView.isEmpty) { "Target $componentMatcher view not found." } val bound = fullView.bounds val targetYPos = bound.bottom * ratioForScreenBottom - val endX = when (direction) { - Direction.LEFT -> bound.left - Direction.RIGHT -> bound.right - else -> { - throw IllegalStateException("Only left or right direction is allowed.") + val endX = + when (direction) { + Direction.LEFT -> bound.left + Direction.RIGHT -> bound.right + else -> { + throw IllegalStateException("Only left or right direction is allowed.") + } } - } return uiDevice.swipe( - bound.centerX(), targetYPos.toInt(), endX, targetYPos.toInt(), SWIPE_STEPS) + bound.centerX(), + targetYPos.toInt(), + endX, + targetYPos.toInt(), + SWIPE_STEPS + ) } /** diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt index b5b0da934ad9..16753e63eb01 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt @@ -28,7 +28,9 @@ import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import java.util.regex.Pattern -class ImeAppAutoFocusHelper @JvmOverloads constructor( +class ImeAppAutoFocusHelper +@JvmOverloads +constructor( instr: Instrumentation, private val rotation: Int, private val imePackageName: String = IME_PACKAGE, @@ -52,59 +54,61 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor( } override fun open() { - val expectedPackage = if (rotation.isRotated()) { - imePackageName - } else { - getPackage() - } + val expectedPackage = + if (rotation.isRotated()) { + imePackageName + } else { + getPackage() + } launcherStrategy.launch(appName, expectedPackage) } fun startDialogThemedActivity(wmHelper: WindowManagerStateHelper) { - val button = uiDevice.wait(Until.findObject(By.res(getPackage(), - "start_dialog_themed_activity_btn")), FIND_TIMEOUT) + val button = + uiDevice.wait( + Until.findObject(By.res(getPackage(), "start_dialog_themed_activity_btn")), + FIND_TIMEOUT + ) requireNotNull(button) { "Button not found, this usually happens when the device " + "was left in an unknown state (e.g. Screen turned off)" } button.click() - wmHelper.StateSyncBuilder() - .withFullScreenApp( - ActivityOptions.DialogThemedActivity.COMPONENT.toFlickerComponent()) + wmHelper + .StateSyncBuilder() + .withFullScreenApp(ActivityOptions.DialogThemedActivity.COMPONENT.toFlickerComponent()) .waitForAndVerify() } fun dismissDialog(wmHelper: WindowManagerStateHelper) { - val dialog = uiDevice.wait( - Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT) + val dialog = uiDevice.wait(Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT) // Pressing back key to dismiss the dialog if (dialog != null) { uiDevice.pressBack() - wmHelper.StateSyncBuilder() - .withAppTransitionIdle() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() } } fun getInsetsVisibleFromDialog(type: Int): Boolean { - val insetsVisibilityTextView = uiDevice.wait( - Until.findObject(By.res("android:id/text1")), FIND_TIMEOUT) + val insetsVisibilityTextView = + uiDevice.wait(Until.findObject(By.res("android:id/text1")), FIND_TIMEOUT) if (insetsVisibilityTextView != null) { val visibility = insetsVisibilityTextView.text.toString() - val matcher = when (type) { - ime() -> { - Pattern.compile("IME\\: (VISIBLE|INVISIBLE)").matcher(visibility) - } - statusBars() -> { - Pattern.compile("StatusBar\\: (VISIBLE|INVISIBLE)").matcher(visibility) + val matcher = + when (type) { + ime() -> { + Pattern.compile("IME\\: (VISIBLE|INVISIBLE)").matcher(visibility) + } + statusBars() -> { + Pattern.compile("StatusBar\\: (VISIBLE|INVISIBLE)").matcher(visibility) + } + navigationBars() -> { + Pattern.compile("NavBar\\: (VISIBLE|INVISIBLE)").matcher(visibility) + } + else -> null } - navigationBars() -> { - Pattern.compile("NavBar\\: (VISIBLE|INVISIBLE)").matcher(visibility) - } - else -> null - } if (matcher != null && matcher.find()) { return matcher.group(1).equals("VISIBLE") } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt index 56b6b92dd6ef..cefbf18d33c9 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt @@ -26,11 +26,12 @@ import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper -open class ImeAppHelper @JvmOverloads constructor( +open class ImeAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.Ime.Default.LABEL, - component: ComponentNameMatcher = - ActivityOptions.Ime.Default.COMPONENT.toFlickerComponent(), + component: ComponentNameMatcher = ActivityOptions.Ime.Default.COMPONENT.toFlickerComponent(), launcherStrategy: ILauncherStrategy = LauncherStrategyFactory.getInstance(instr).launcherStrategy ) : StandardAppHelper(instr, launcherName, component, launcherStrategy) { @@ -40,9 +41,8 @@ open class ImeAppHelper @JvmOverloads constructor( * @param wmHelper Helper used to wait for WindowManager states */ open fun openIME(wmHelper: WindowManagerStateHelper) { - val editText = uiDevice.wait( - Until.findObject(By.res(getPackage(), "plain_text_input")), - FIND_TIMEOUT) + val editText = + uiDevice.wait(Until.findObject(By.res(getPackage(), "plain_text_input")), FIND_TIMEOUT) requireNotNull(editText) { "Text field not found, this usually happens when the device " + @@ -53,9 +53,7 @@ open class ImeAppHelper @JvmOverloads constructor( } protected fun waitIMEShown(wmHelper: WindowManagerStateHelper) { - wmHelper.StateSyncBuilder() - .withImeShown() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify() } /** @@ -65,21 +63,19 @@ open class ImeAppHelper @JvmOverloads constructor( */ open fun closeIME(wmHelper: WindowManagerStateHelper) { uiDevice.pressBack() - wmHelper.StateSyncBuilder() - .withImeGone() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withImeGone().waitForAndVerify() } open fun finishActivity(wmHelper: WindowManagerStateHelper) { - val finishButton = uiDevice.wait( - Until.findObject(By.res(getPackage(), "finish_activity_btn")), - FIND_TIMEOUT) + val finishButton = + uiDevice.wait( + Until.findObject(By.res(getPackage(), "finish_activity_btn")), + FIND_TIMEOUT + ) requireNotNull(finishButton) { "Finish activity button not found, probably IME activity is not on the screen?" } finishButton.click() - wmHelper.StateSyncBuilder() - .withActivityRemoved(this) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withActivityRemoved(this).waitForAndVerify() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt index bfb68da20836..871b66ecbf0e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt @@ -24,7 +24,9 @@ import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper -class ImeEditorPopupDialogAppHelper @JvmOverloads constructor( +class ImeEditorPopupDialogAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.Ime.EditorPopupDialogActivity.LABEL, component: ComponentNameMatcher = @@ -42,15 +44,12 @@ class ImeEditorPopupDialogAppHelper @JvmOverloads constructor( } fun dismissDialog(wmHelper: WindowManagerStateHelper) { - val dismissButton = uiDevice.wait( - Until.findObject(By.text("Dismiss")), FIND_TIMEOUT) + val dismissButton = uiDevice.wait(Until.findObject(By.text("Dismiss")), FIND_TIMEOUT) // Pressing back key to dismiss the dialog if (dismissButton != null) { dismissButton.click() - wmHelper.StateSyncBuilder() - .withAppTransitionIdle() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt index 3b8d3c3440a8..1502ad5cde58 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt @@ -23,7 +23,9 @@ import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent -class ImeStateInitializeHelper @JvmOverloads constructor( +class ImeStateInitializeHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.Ime.StateInitializeActivity.LABEL, component: ComponentNameMatcher = diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt index cb54b5732c8d..ba8dabd4dfec 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt @@ -20,8 +20,9 @@ import android.app.Instrumentation import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.traces.parser.toFlickerComponent -class LaunchBubbleHelper(instrumentation: Instrumentation) : StandardAppHelper( - instrumentation, - ActivityOptions.Bubbles.LaunchBubble.LABEL, - ActivityOptions.Bubbles.LaunchBubble.COMPONENT.toFlickerComponent() -) +class LaunchBubbleHelper(instrumentation: Instrumentation) : + StandardAppHelper( + instrumentation, + ActivityOptions.Bubbles.LaunchBubble.LABEL, + ActivityOptions.Bubbles.LaunchBubble.COMPONENT.toFlickerComponent() + ) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt index dde0b3e6873d..f00904bd7570 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt @@ -27,19 +27,19 @@ import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent -class MailAppHelper @JvmOverloads constructor( +class MailAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.Mail.LABEL, - component: ComponentNameMatcher = - ActivityOptions.Mail.COMPONENT.toFlickerComponent(), - launcherStrategy: ILauncherStrategy = LauncherStrategyFactory - .getInstance(instr) - .launcherStrategy + component: ComponentNameMatcher = ActivityOptions.Mail.COMPONENT.toFlickerComponent(), + launcherStrategy: ILauncherStrategy = + LauncherStrategyFactory.getInstance(instr).launcherStrategy ) : StandardAppHelper(instr, launcherName, component, launcherStrategy) { fun openMail(rowIdx: Int) { - val rowSel = By.res(getPackage(), "mail_row_item_text") - .textEndsWith(String.format("%04d", rowIdx)) + val rowSel = + By.res(getPackage(), "mail_row_item_text").textEndsWith(String.format("%04d", rowIdx)) var row: UiObject2? = null for (i in 1..1000) { row = uiDevice.wait(Until.findObject(rowSel), SHORT_WAIT_TIME_MS) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt index 9bdfadb35ff3..e0f6fb0a5bbc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt @@ -40,23 +40,29 @@ class MultiWindowUtils( } fun getDevEnableNonResizableMultiWindow(context: Context): Int = - Settings.Global.getInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW) + Settings.Global.getInt( + context.contentResolver, + Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW + ) fun setDevEnableNonResizableMultiWindow(context: Context, configValue: Int) = - Settings.Global.putInt(context.contentResolver, - Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, configValue) + Settings.Global.putInt( + context.contentResolver, + Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, + configValue + ) fun setSupportsNonResizableMultiWindow(instrumentation: Instrumentation, configValue: Int) = executeShellCommand( - instrumentation, - createConfigSupportsNonResizableMultiWindowCommand(configValue)) + instrumentation, + createConfigSupportsNonResizableMultiWindowCommand(configValue) + ) fun resetMultiWindowConfig(instrumentation: Instrumentation) = executeShellCommand(instrumentation, resetMultiWindowConfigCommand) private fun createConfigSupportsNonResizableMultiWindowCommand(configValue: Int): String = - "wm set-multi-window-config --supportsNonResizable $configValue" + "wm set-multi-window-config --supportsNonResizable $configValue" private const val resetMultiWindowConfigCommand: String = "wm reset-multi-window-config" } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt index f3386afa610c..34294a6b7304 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt @@ -27,27 +27,24 @@ import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper -class NewTasksAppHelper @JvmOverloads constructor( +class NewTasksAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.LaunchNewTask.LABEL, - component: ComponentNameMatcher = - ActivityOptions.LaunchNewTask.COMPONENT.toFlickerComponent(), - launcherStrategy: ILauncherStrategy = LauncherStrategyFactory - .getInstance(instr) - .launcherStrategy + component: ComponentNameMatcher = ActivityOptions.LaunchNewTask.COMPONENT.toFlickerComponent(), + launcherStrategy: ILauncherStrategy = + LauncherStrategyFactory.getInstance(instr).launcherStrategy ) : StandardAppHelper(instr, launcherName, component, launcherStrategy) { fun openNewTask(device: UiDevice, wmHelper: WindowManagerStateHelper) { - val button = device.wait( - Until.findObject(By.res(getPackage(), "launch_new_task")), - FIND_TIMEOUT) + val button = + device.wait(Until.findObject(By.res(getPackage(), "launch_new_task")), FIND_TIMEOUT) requireNotNull(button) { "Button not found, this usually happens when the device " + "was left in an unknown state (e.g. in split screen)" } button.click() - wmHelper.StateSyncBuilder() - .withAppTransitionIdle() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt index 19ce3ba21812..bbb782d7ae50 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt @@ -23,7 +23,9 @@ import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent -class NonResizeableAppHelper @JvmOverloads constructor( +class NonResizeableAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.NonResizeableActivity.LABEL, component: ComponentNameMatcher = diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt index 97642f1617af..a3e32e501ce2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt @@ -26,19 +26,18 @@ import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper -class NotificationAppHelper @JvmOverloads constructor( +class NotificationAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.Notification.LABEL, - component: ComponentNameMatcher = - ActivityOptions.Notification.COMPONENT.toFlickerComponent(), - launcherStrategy: ILauncherStrategy = LauncherStrategyFactory - .getInstance(instr) - .launcherStrategy + component: ComponentNameMatcher = ActivityOptions.Notification.COMPONENT.toFlickerComponent(), + launcherStrategy: ILauncherStrategy = + LauncherStrategyFactory.getInstance(instr).launcherStrategy ) : StandardAppHelper(instr, launcherName, component, launcherStrategy) { fun postNotification(wmHelper: WindowManagerStateHelper) { - val button = uiDevice.wait( - Until.findObject(By.res(getPackage(), "post_notification")), - FIND_TIMEOUT) + val button = + uiDevice.wait(Until.findObject(By.res(getPackage(), "post_notification")), FIND_TIMEOUT) requireNotNull(button) { "Post notification button not found, this usually happens when the device " + @@ -48,8 +47,6 @@ class NotificationAppHelper @JvmOverloads constructor( uiDevice.wait(Until.findObject(By.text("Flicker Test Notification")), FIND_TIMEOUT) ?: error("Flicker Notification not found") - wmHelper.StateSyncBuilder() - .withAppTransitionIdle() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt index dc49fc8021d9..e730f315392b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt @@ -29,19 +29,20 @@ import com.android.server.wm.traces.common.region.Region import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper -open class PipAppHelper(instrumentation: Instrumentation) : StandardAppHelper( - instrumentation, - ActivityOptions.Pip.LABEL, - ActivityOptions.Pip.COMPONENT.toFlickerComponent() -) { +open class PipAppHelper(instrumentation: Instrumentation) : + StandardAppHelper( + instrumentation, + ActivityOptions.Pip.LABEL, + ActivityOptions.Pip.COMPONENT.toFlickerComponent() + ) { private val mediaSessionManager: MediaSessionManager - get() = context.getSystemService(MediaSessionManager::class.java) - ?: error("Could not get MediaSessionManager") + get() = + context.getSystemService(MediaSessionManager::class.java) + ?: error("Could not get MediaSessionManager") private val mediaController: MediaController? - get() = mediaSessionManager.getActiveSessions(null).firstOrNull { - it.packageName == `package` - } + get() = + mediaSessionManager.getActiveSessions(null).firstOrNull { it.packageName == `package` } open fun clickObject(resId: String) { val selector = By.res(`package`, resId) @@ -51,8 +52,8 @@ open class PipAppHelper(instrumentation: Instrumentation) : StandardAppHelper( } /** - * Launches the app through an intent instead of interacting with the launcher and waits - * until the app window is in PIP mode + * Launches the app through an intent instead of interacting with the launcher and waits until + * the app window is in PIP mode */ @JvmOverloads fun launchViaIntentAndWaitForPip( @@ -62,18 +63,17 @@ open class PipAppHelper(instrumentation: Instrumentation) : StandardAppHelper( stringExtras: Map<String, String> ) { launchViaIntentAndWaitShown( - wmHelper, expectedWindowName, action, stringExtras, + wmHelper, + expectedWindowName, + action, + stringExtras, waitConditions = arrayOf(WindowManagerConditionsFactory.hasPipWindow()) ) - wmHelper.StateSyncBuilder() - .withPipShown() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withPipShown().waitForAndVerify() } - /** - * Expand the PIP window back to full screen via intent and wait until the app is visible - */ + /** Expand the PIP window back to full screen via intent and wait until the app is visible */ fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = launchViaIntentAndWaitShown(wmHelper) @@ -81,9 +81,7 @@ open class PipAppHelper(instrumentation: Instrumentation) : StandardAppHelper( clickObject(ENTER_PIP_BUTTON_ID) // Wait on WMHelper or simply wait for 3 seconds - wmHelper.StateSyncBuilder() - .withPipShown() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withPipShown().waitForAndVerify() // when entering pip, the dismiss button is visible at the start. to ensure the pip // animation is complete, wait until the pip dismiss button is no longer visible. // b/176822698: dismiss-only state will be removed in the future @@ -102,17 +100,18 @@ open class PipAppHelper(instrumentation: Instrumentation) : StandardAppHelper( clickObject(MEDIA_SESSION_START_RADIO_BUTTON_ID) } - fun checkWithCustomActionsCheckbox() = uiDevice - .findObject(By.res(`package`, WITH_CUSTOM_ACTIONS_BUTTON_ID)) - ?.takeIf { it.isCheckable } - ?.apply { if (!isChecked) clickObject(WITH_CUSTOM_ACTIONS_BUTTON_ID) } - ?: error("'With custom actions' checkbox not found") + fun checkWithCustomActionsCheckbox() = + uiDevice + .findObject(By.res(`package`, WITH_CUSTOM_ACTIONS_BUTTON_ID)) + ?.takeIf { it.isCheckable } + ?.apply { if (!isChecked) clickObject(WITH_CUSTOM_ACTIONS_BUTTON_ID) } + ?: error("'With custom actions' checkbox not found") - fun pauseMedia() = mediaController?.transportControls?.pause() - ?: error("No active media session found") + fun pauseMedia() = + mediaController?.transportControls?.pause() ?: error("No active media session found") - fun stopMedia() = mediaController?.transportControls?.stop() - ?: error("No active media session found") + fun stopMedia() = + mediaController?.transportControls?.stop() ?: error("No active media session found") @Deprecated( "Use PipAppHelper.closePipWindow(wmHelper) instead", @@ -124,55 +123,41 @@ open class PipAppHelper(instrumentation: Instrumentation) : StandardAppHelper( private fun getWindowRect(wmHelper: WindowManagerStateHelper): Rect { val windowRegion = wmHelper.getWindowRegion(this) - require(!windowRegion.isEmpty) { - "Unable to find a PIP window in the current state" - } + require(!windowRegion.isEmpty) { "Unable to find a PIP window in the current state" } return windowRegion.bounds } - /** - * Taps the pip window and dismisses it by clicking on the X button. - */ + /** Taps the pip window and dismisses it by clicking on the X button. */ open fun closePipWindow(wmHelper: WindowManagerStateHelper) { val windowRect = getWindowRect(wmHelper) uiDevice.click(windowRect.centerX(), windowRect.centerY()) // search and interact with the dismiss button val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss") uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT) - val dismissPipObject = uiDevice.findObject(dismissSelector) - ?: error("PIP window dismiss button not found") + val dismissPipObject = + uiDevice.findObject(dismissSelector) ?: error("PIP window dismiss button not found") val dismissButtonBounds = dismissPipObject.visibleBounds uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY()) // Wait for animation to complete. - wmHelper.StateSyncBuilder() - .withPipGone() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withPipGone().withHomeActivityVisible().waitForAndVerify() } - /** - * Close the pip window by pressing the expand button - */ + /** Close the pip window by pressing the expand button */ fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) { val windowRect = getWindowRect(wmHelper) uiDevice.click(windowRect.centerX(), windowRect.centerY()) // search and interact with the expand button val expandSelector = By.res(SYSTEMUI_PACKAGE, "expand_button") uiDevice.wait(Until.hasObject(expandSelector), FIND_TIMEOUT) - val expandPipObject = uiDevice.findObject(expandSelector) - ?: error("PIP window expand button not found") + val expandPipObject = + uiDevice.findObject(expandSelector) ?: error("PIP window expand button not found") val expandButtonBounds = expandPipObject.visibleBounds uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY()) - wmHelper.StateSyncBuilder() - .withPipGone() - .withFullScreenApp(this) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withPipGone().withFullScreenApp(this).waitForAndVerify() } - /** - * Double click on the PIP window to expand it - */ + /** Double click on the PIP window to expand it */ fun doubleClickPipWindow(wmHelper: WindowManagerStateHelper) { val windowRect = getWindowRect(wmHelper) Log.d(TAG, "First click") @@ -180,9 +165,7 @@ open class PipAppHelper(instrumentation: Instrumentation) : StandardAppHelper( Log.d(TAG, "Second click") uiDevice.click(windowRect.centerX(), windowRect.centerY()) Log.d(TAG, "Wait for app transition to end") - wmHelper.StateSyncBuilder() - .withAppTransitionIdle() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() waitForPipWindowToExpandFrom(wmHelper, Region.from(windowRect)) } @@ -190,13 +173,18 @@ open class PipAppHelper(instrumentation: Instrumentation) : StandardAppHelper( wmHelper: WindowManagerStateHelper, windowRect: Region ) { - wmHelper.StateSyncBuilder().add("pipWindowExpanded") { - val pipAppWindow = it.wmState.visibleWindows.firstOrNull { window -> - this.windowMatchesAnyOf(window) - } ?: return@add false - val pipRegion = pipAppWindow.frameRegion - return@add pipRegion.coversMoreThan(windowRect) - }.waitForAndVerify() + wmHelper + .StateSyncBuilder() + .add("pipWindowExpanded") { + val pipAppWindow = + it.wmState.visibleWindows.firstOrNull { window -> + this.windowMatchesAnyOf(window) + } + ?: return@add false + val pipRegion = pipAppWindow.frameRegion + return@add pipRegion.coversMoreThan(windowRect) + } + .waitForAndVerify() } companion object { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt index fc1ff7c84675..c90435223469 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt @@ -23,7 +23,9 @@ import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent -class SeamlessRotationAppHelper @JvmOverloads constructor( +class SeamlessRotationAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.SeamlessRotation.LABEL, component: ComponentNameMatcher = diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt index 0e1df411d394..de152cb5bbc9 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt @@ -23,7 +23,9 @@ import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent -class ShowWhenLockedAppHelper @JvmOverloads constructor( +class ShowWhenLockedAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.ShowWhenLockedActivity.LABEL, component: ComponentNameMatcher = diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt index e3461a74b4ce..e41599002a01 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt @@ -23,11 +23,12 @@ import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent -class SimpleAppHelper @JvmOverloads constructor( +class SimpleAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.SimpleActivity.LABEL, - component: ComponentNameMatcher = - ActivityOptions.SimpleActivity.COMPONENT.toFlickerComponent(), + component: ComponentNameMatcher = ActivityOptions.SimpleActivity.COMPONENT.toFlickerComponent(), launcherStrategy: ILauncherStrategy = LauncherStrategyFactory.getInstance(instr).launcherStrategy ) : StandardAppHelper(instr, launcherName, component, launcherStrategy) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt index f4ea37f2127a..13301906187d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt @@ -27,7 +27,9 @@ import com.android.server.wm.traces.common.ComponentNameMatcher import com.android.server.wm.traces.parser.toFlickerComponent import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper -class TwoActivitiesAppHelper @JvmOverloads constructor( +class TwoActivitiesAppHelper +@JvmOverloads +constructor( instr: Instrumentation, launcherName: String = ActivityOptions.LaunchNewActivity.LABEL, component: ComponentNameMatcher = @@ -50,9 +52,7 @@ class TwoActivitiesAppHelper @JvmOverloads constructor( button.click() device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT) - wmHelper.StateSyncBuilder() - .withFullScreenApp(secondActivityComponent) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withFullScreenApp(secondActivityComponent).waitForAndVerify() } companion object { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt index 9b8d791e0892..1a495957bb7f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt @@ -37,10 +37,10 @@ import org.junit.runners.Parameterized * Test IME window closing back to app window transitions. * * This test doesn't work on 90 degrees. According to the InputMethodService documentation: - * + * ``` * Don't show if this is not explicitly requested by the user and the input method * is fullscreen. That would be too disruptive. - * + * ``` * More details on b/190352379 * * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest` @@ -54,51 +54,35 @@ class CloseImeAutoOpenWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit = { - setup { - testApp.launchViaIntent(wmHelper) - } - teardown { - testApp.exit(wmHelper) - } - transitions { - testApp.closeIME(wmHelper) - } + setup { testApp.launchViaIntent(wmHelper) } + teardown { testApp.exit(wmHelper) } + transitions { testApp.closeIME(wmHelper) } } @Presubmit @Test fun imeAppWindowIsAlwaysVisible() { - testSpec.assertWm { - this.isAppWindowOnTop(testApp) - } + testSpec.assertWm { this.isAppWindowOnTop(testApp) } } @Presubmit @Test fun imeLayerVisibleStart() { - testSpec.assertLayersStart { - this.isVisible(ComponentNameMatcher.IME) - } + testSpec.assertLayersStart { this.isVisible(ComponentNameMatcher.IME) } } @Presubmit @Test fun imeLayerInvisibleEnd() { - testSpec.assertLayersEnd { - this.isInvisible(ComponentNameMatcher.IME) - } + testSpec.assertLayersEnd { this.isInvisible(ComponentNameMatcher.IME) } } - @Presubmit - @Test - fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + @Presubmit @Test fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() @Presubmit @Test fun imeAppLayerIsAlwaysVisible() { - testSpec.assertLayers { - this.isVisible(testApp) - } + testSpec.assertLayers { this.isVisible(testApp) } } companion object { @@ -107,12 +91,13 @@ class CloseImeAutoOpenWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( - // b/190352379 (IME doesn't show on app launch in 90 degrees) + // b/190352379 (IME doesn't show on app launch in 90 degrees) supportedRotations = listOf(Surface.ROTATION_0), - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) + supportedNavigationModes = + listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt index e2d3eb8860fc..463efe89b5e9 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt @@ -37,10 +37,10 @@ import org.junit.runners.Parameterized * Test IME window closing back to app window transitions. * * This test doesn't work on 90 degrees. According to the InputMethodService documentation: - * + * ``` * Don't show if this is not explicitly requested by the user and the input method * is fullscreen. That would be too disruptive. - * + * ``` * More details on b/190352379 * * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest` @@ -58,56 +58,37 @@ class CloseImeAutoOpenWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTes tapl.setExpectedRotationCheckEnabled(false) testApp.launchViaIntent(wmHelper) } - teardown { - testApp.exit(wmHelper) - } + teardown { testApp.exit(wmHelper) } transitions { tapl.goHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .withImeGone() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().withImeGone().waitForAndVerify() } } @Presubmit @Test fun imeAppWindowBecomesInvisible() { - testSpec.assertWm { - this.isAppWindowOnTop(testApp) - .then() - .isAppWindowNotOnTop(testApp) - } + testSpec.assertWm { this.isAppWindowOnTop(testApp).then().isAppWindowNotOnTop(testApp) } } @Presubmit @Test fun imeLayerVisibleStart() { - testSpec.assertLayersStart { - this.isVisible(ComponentNameMatcher.IME) - } + testSpec.assertLayersStart { this.isVisible(ComponentNameMatcher.IME) } } @Presubmit @Test fun imeLayerInvisibleEnd() { - testSpec.assertLayersEnd { - this.isInvisible(ComponentNameMatcher.IME) - } + testSpec.assertLayersEnd { this.isInvisible(ComponentNameMatcher.IME) } } - @Presubmit - @Test - fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + @Presubmit @Test fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() @Presubmit @Test fun imeAppLayerBecomesInvisible() { - testSpec.assertLayers { - this.isVisible(testApp) - .then() - .isInvisible(testApp) - } + testSpec.assertLayers { this.isVisible(testApp).then().isInvisible(testApp) } } companion object { @@ -118,9 +99,11 @@ class CloseImeAutoOpenWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTes .getConfigNonRotationTests( // b/190352379 (IME doesn't show on app launch in 90 degrees) supportedRotations = listOf(Surface.ROTATION_0), - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY) + supportedNavigationModes = + listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt index 923ed5e1bc34..91d9a1f17d05 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt @@ -16,7 +16,7 @@ package com.android.server.wm.flicker.ime -import android.platform.test.annotations.Postsubmit +import android.platform.test.annotations.Presubmit import android.view.Surface import android.view.WindowManagerPolicyConstants import androidx.test.filters.RequiresDevice @@ -50,47 +50,20 @@ class CloseImeEditorPopupDialogTest(testSpec: FlickerTestParameter) : BaseTest(t } transitions { imeTestApp.dismissDialog(wmHelper) - wmHelper.StateSyncBuilder() - .withImeGone() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withImeGone().waitForAndVerify() } teardown { tapl.goHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() imeTestApp.exit(wmHelper) } } - /** {@inheritDoc} */ - @Postsubmit - @Test - override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() - - /** {@inheritDoc} */ - @Postsubmit - @Test - override fun taskBarLayerIsVisibleAtStartAndEnd() = - super.taskBarLayerIsVisibleAtStartAndEnd() - - /** {@inheritDoc} */ - @Postsubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - - /** {@inheritDoc} */ - @Postsubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - @Postsubmit + @Presubmit @Test fun imeWindowBecameInvisible() = testSpec.imeWindowBecomesInvisible() - @Postsubmit + @Presubmit @Test fun imeLayerAndImeSnapshotVisibleOnScreen() { testSpec.assertLayers { @@ -103,20 +76,23 @@ class CloseImeEditorPopupDialogTest(testSpec: FlickerTestParameter) : BaseTest(t } } - @Postsubmit + @Presubmit @Test fun imeSnapshotAssociatedOnAppVisibleRegion() { testSpec.assertLayers { this.invoke("imeSnapshotAssociatedOnAppVisibleRegion") { - val imeSnapshotLayers = it.subjects.filter { subject -> - subject.name.contains( - ComponentNameMatcher.IME_SNAPSHOT.toLayerName() - ) && subject.isVisible - } + val imeSnapshotLayers = + it.subjects.filter { subject -> + subject.name.contains(ComponentNameMatcher.IME_SNAPSHOT.toLayerName()) && + subject.isVisible + } if (imeSnapshotLayers.isNotEmpty()) { - val visibleAreas = imeSnapshotLayers.mapNotNull { imeSnapshotLayer -> - imeSnapshotLayer.layer?.visibleRegion - }.toTypedArray() + val visibleAreas = + imeSnapshotLayers + .mapNotNull { imeSnapshotLayer -> + imeSnapshotLayer.layer?.visibleRegion + } + .toTypedArray() val imeVisibleRegion = RegionSubject.assertThat(visibleAreas, this, timestamp) val appVisibleRegion = it.visibleRegion(imeTestApp) if (imeVisibleRegion.region.isNotEmpty) { @@ -133,7 +109,8 @@ class CloseImeEditorPopupDialogTest(testSpec: FlickerTestParameter) : BaseTest(t fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( - supportedNavigationModes = listOf( + supportedNavigationModes = + listOf( WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY ), diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt index 32efbb0ea1cb..b9c875ab5938 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt @@ -35,8 +35,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window closing back to app window transitions. - * To run this test: `atest FlickerTests:CloseImeWindowToAppTest` + * Test IME window closing back to app window transitions. To run this test: `atest + * FlickerTests:CloseImeWindowToAppTest` */ @RequiresDevice @RunWith(Parameterized::class) @@ -51,12 +51,8 @@ class CloseImeWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest(testSpe testApp.launchViaIntent(wmHelper) testApp.openIME(wmHelper) } - teardown { - testApp.exit(wmHelper) - } - transitions { - testApp.closeIME(wmHelper) - } + teardown { testApp.exit(wmHelper) } + transitions { testApp.closeIME(wmHelper) } } /** {@inheritDoc} */ @@ -64,10 +60,13 @@ class CloseImeWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest(testSpe @Test override fun visibleWindowsShownMoreThanOneConsecutiveEntry() { testSpec.assertWm { - this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf( - ComponentNameMatcher.IME, - ComponentNameMatcher.SPLASH_SCREEN, - ComponentNameMatcher.SNAPSHOT)) + this.visibleWindowsShownMoreThanOneConsecutiveEntry( + listOf( + ComponentNameMatcher.IME, + ComponentNameMatcher.SPLASH_SCREEN, + ComponentNameMatcher.SNAPSHOT + ) + ) } } @@ -88,32 +87,25 @@ class CloseImeWindowToAppTest(testSpec: FlickerTestParameter) : BaseTest(testSpe testSpec.navBarLayerPositionAtStartAndEnd() } - @Presubmit - @Test - fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + @Presubmit @Test fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() @Presubmit @Test fun imeAppLayerIsAlwaysVisible() { - testSpec.assertLayers { - this.isVisible(testApp) - } + testSpec.assertLayers { this.isVisible(testApp) } } @Presubmit @Test fun imeAppWindowIsAlwaysVisible() { - testSpec.assertWm { - this.isAppWindowOnTop(testApp) - } + testSpec.assertWm { this.isAppWindowOnTop(testApp) } } companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt index 177e76104b3d..1dc3ca55caba 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt @@ -34,8 +34,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window closing to home transitions. - * To run this test: `atest FlickerTests:CloseImeWindowToHomeTest` + * Test IME window closing to home transitions. To run this test: `atest + * FlickerTests:CloseImeWindowToHomeTest` */ @RequiresDevice @RunWith(Parameterized::class) @@ -53,14 +53,9 @@ class CloseImeWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTest(testSp } transitions { tapl.goHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .withImeGone() - .waitForAndVerify() - } - teardown { - testApp.exit(wmHelper) + wmHelper.StateSyncBuilder().withHomeActivityVisible().withImeGone().waitForAndVerify() } + teardown { testApp.exit(wmHelper) } } /** {@inheritDoc} */ @@ -84,40 +79,25 @@ class CloseImeWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTest(testSp override fun visibleLayersShownMoreThanOneConsecutiveEntry() { testSpec.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry( - listOf( - ComponentNameMatcher.IME, - ComponentNameMatcher.SPLASH_SCREEN - ) + listOf(ComponentNameMatcher.IME, ComponentNameMatcher.SPLASH_SCREEN) ) } } - @Presubmit - @Test - fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + @Presubmit @Test fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() - @Presubmit - @Test - fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible() + @Presubmit @Test fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible() @Presubmit @Test fun imeAppWindowBecomesInvisible() { - testSpec.assertWm { - this.isAppWindowVisible(testApp) - .then() - .isAppWindowInvisible(testApp) - } + testSpec.assertWm { this.isAppWindowVisible(testApp).then().isAppWindowInvisible(testApp) } } @Presubmit @Test fun imeAppLayerBecomesInvisible() { - testSpec.assertLayers { - this.isVisible(testApp) - .then() - .isInvisible(testApp) - } + testSpec.assertLayers { this.isVisible(testApp).then().isInvisible(testApp) } } companion object { @@ -126,11 +106,12 @@ class CloseImeWindowToHomeTest(testSpec: FlickerTestParameter) : BaseTest(testSp fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( - supportedRotations = listOf(Surface.ROTATION_0), - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) + supportedRotations = listOf(Surface.ROTATION_0), + supportedNavigationModes = + listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt index 9c99d966e345..e0c5edcec762 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt @@ -15,6 +15,7 @@ */ @file:JvmName("CommonAssertions") + package com.android.server.wm.flicker.ime import com.android.server.wm.flicker.FlickerTestParameter @@ -22,17 +23,13 @@ import com.android.server.wm.traces.common.ComponentNameMatcher fun FlickerTestParameter.imeLayerBecomesVisible() { assertLayers { - this.isInvisible(ComponentNameMatcher.IME) - .then() - .isVisible(ComponentNameMatcher.IME) + this.isInvisible(ComponentNameMatcher.IME).then().isVisible(ComponentNameMatcher.IME) } } fun FlickerTestParameter.imeLayerBecomesInvisible() { assertLayers { - this.isVisible(ComponentNameMatcher.IME) - .then() - .isInvisible(ComponentNameMatcher.IME) + this.isVisible(ComponentNameMatcher.IME).then().isInvisible(ComponentNameMatcher.IME) } } @@ -46,9 +43,7 @@ fun FlickerTestParameter.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false .isNonAppWindowVisible(ComponentNameMatcher.IME) } } else { - assertWm { - this.isNonAppWindowVisible(ComponentNameMatcher.IME) - } + assertWm { this.isNonAppWindowVisible(ComponentNameMatcher.IME) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt index f0776c10bd69..073da4f092dc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt @@ -47,30 +47,22 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class LaunchAppShowImeAndDialogThemeAppTest( - testSpec: FlickerTestParameter -) : BaseTest(testSpec) { +class LaunchAppShowImeAndDialogThemeAppTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation) /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit = { setup { testApp.launchViaIntent(wmHelper) - wmHelper.StateSyncBuilder() - .withImeShown() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify() testApp.startDialogThemedActivity(wmHelper) // Verify IME insets isn't visible on dialog since it's non-IME focusable window assertFalse(testApp.getInsetsVisibleFromDialog(ime())) assertTrue(testApp.getInsetsVisibleFromDialog(statusBars())) assertTrue(testApp.getInsetsVisibleFromDialog(navigationBars())) } - teardown { - testApp.exit(wmHelper) - } - transitions { - testApp.dismissDialog(wmHelper) - } + teardown { testApp.exit(wmHelper) } + transitions { testApp.dismissDialog(wmHelper) } } /** {@inheritDoc} */ @@ -83,41 +75,29 @@ class LaunchAppShowImeAndDialogThemeAppTest( @Test override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() - /** - * Checks that [ComponentMatcher.IME] layer becomes visible during the transition - */ - @Presubmit - @Test - fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible() + /** Checks that [ComponentMatcher.IME] layer becomes visible during the transition */ + @Presubmit @Test fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible() - /** - * Checks that [ComponentMatcher.IME] layer is visible at the end of the transition - */ + /** Checks that [ComponentMatcher.IME] layer is visible at the end of the transition */ @Presubmit @Test fun imeLayerExistsEnd() { - testSpec.assertLayersEnd { - this.isVisible(ComponentNameMatcher.IME) - } + testSpec.assertLayersEnd { this.isVisible(ComponentNameMatcher.IME) } } - /** - * Checks that [ComponentMatcher.IME_SNAPSHOT] layer is invisible always. - */ + /** Checks that [ComponentMatcher.IME_SNAPSHOT] layer is invisible always. */ @Presubmit @Test fun imeSnapshotNotVisible() { - testSpec.assertLayers { - this.isInvisible(ComponentNameMatcher.IME_SNAPSHOT) - } + testSpec.assertLayers { this.isInvisible(ComponentNameMatcher.IME_SNAPSHOT) } } companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic @@ -125,10 +105,11 @@ class LaunchAppShowImeAndDialogThemeAppTest( return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( supportedRotations = listOf(Surface.ROTATION_0), - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) + supportedNavigationModes = + listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt index 85e9e75776fb..a93f1766b05f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt @@ -41,27 +41,33 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:LaunchAppShowImeOnStartTest` * * Actions: + * ``` * Make sure no apps are running on the device * Launch an app [testApp] that automatically displays IME and wait animation to complete - * + * ``` * To run only the presubmit assertions add: `-- + * ``` * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit` - * + * ``` * To run only the postsubmit assertions add: `-- + * ``` * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit` - * + * ``` * To run only the flaky assertions add: `-- + * ``` * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest` - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [CloseAppTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -83,65 +89,48 @@ class LaunchAppShowImeOnStartTest(testSpec: FlickerTestParameter) : BaseTest(tes } transitions { testApp.launchViaIntent(wmHelper) - wmHelper.StateSyncBuilder() - .withImeShown() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify() } } - /** - * Checks that [ComponentMatcher.IME] window becomes visible during the transition - */ - @Presubmit - @Test - fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible() + /** Checks that [ComponentMatcher.IME] window becomes visible during the transition */ + @Presubmit @Test fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible() - /** - * Checks that [ComponentMatcher.IME] layer becomes visible during the transition - */ - @Presubmit - @Test - fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible() + /** Checks that [ComponentMatcher.IME] layer becomes visible during the transition */ + @Presubmit @Test fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible() - /** - * Checks that [ComponentMatcher.IME] layer is invisible at the start of the transition - */ + /** Checks that [ComponentMatcher.IME] layer is invisible at the start of the transition */ @Presubmit @Test fun imeLayerNotExistsStart() { - testSpec.assertLayersStart { - this.isInvisible(ComponentNameMatcher.IME) - } + testSpec.assertLayersStart { this.isInvisible(ComponentNameMatcher.IME) } } - /** - * Checks that [ComponentMatcher.IME] layer is visible at the end of the transition - */ + /** Checks that [ComponentMatcher.IME] layer is visible at the end of the transition */ @Presubmit @Test fun imeLayerExistsEnd() { - testSpec.assertLayersEnd { - this.isVisible(ComponentNameMatcher.IME) - } + testSpec.assertLayersEnd { this.isVisible(ComponentNameMatcher.IME) } } companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( - supportedRotations = listOf(Surface.ROTATION_0), - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) + supportedRotations = listOf(Surface.ROTATION_0), + supportedNavigationModes = + listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt index 36bc6a3e8ed2..a6bd791282b8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt @@ -57,21 +57,13 @@ class OpenImeWindowAndCloseTest(testSpec: FlickerTestParameter) : BaseTest(testS testApp.launchViaIntent(wmHelper) testApp.openIME(wmHelper) } - transitions { - testApp.finishActivity(wmHelper) - } - teardown { - simpleApp.exit(wmHelper) - } + transitions { testApp.finishActivity(wmHelper) } + teardown { simpleApp.exit(wmHelper) } } - @Presubmit - @Test - fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible() + @Presubmit @Test fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible() - @Presubmit - @Test - fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() + @Presubmit @Test fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() @Presubmit @Test @@ -93,11 +85,12 @@ class OpenImeWindowAndCloseTest(testSpec: FlickerTestParameter) : BaseTest(testS fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( - supportedRotations = listOf(Surface.ROTATION_0), - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) + supportedRotations = listOf(Surface.ROTATION_0), + supportedNavigationModes = + listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt index 7d98d06fcbd4..3e18d5957eb7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt @@ -27,8 +27,10 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper +import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.snapshotStartingWindowLayerCoversExactlyOnApp +import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -37,16 +39,15 @@ import org.junit.runners.Parameterized /** * Test IME window layer will become visible when switching from the fixed orientation activity - * (e.g. Launcher activity). - * To run this test: `atest FlickerTests:OpenImeWindowFromFixedOrientationAppTest` + * (e.g. Launcher activity). To run this test: `atest + * FlickerTests:OpenImeWindowFromFixedOrientationAppTest` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenImeWindowFromFixedOrientationAppTest( - testSpec: FlickerTestParameter -) : BaseTest(testSpec) { +class OpenImeWindowFromFixedOrientationAppTest(testSpec: FlickerTestParameter) : + BaseTest(testSpec) { private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation) /** {@inheritDoc} */ @@ -59,18 +60,14 @@ class OpenImeWindowFromFixedOrientationAppTest( // Swiping out the IME activity to home. tapl.goHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() } transitions { // Bring the exist IME activity to the front in landscape mode device rotation. setRotation(Surface.ROTATION_90) imeTestApp.launchViaIntent(wmHelper) } - teardown { - imeTestApp.exit(wmHelper) - } + teardown { imeTestApp.exit(wmHelper) } } /** {@inheritDoc} */ @@ -94,6 +91,14 @@ class OpenImeWindowFromFixedOrientationAppTest( @Postsubmit @Test fun snapshotStartingWindowLayerCoversExactlyOnApp() { + Assume.assumeFalse(isShellTransitionsEnabled) + testSpec.snapshotStartingWindowLayerCoversExactlyOnApp(imeTestApp) + } + + @Presubmit + @Test + fun snapshotStartingWindowLayerCoversExactlyOnApp_ShellTransit() { + Assume.assumeTrue(isShellTransitionsEnabled) testSpec.snapshotStartingWindowLayerCoversExactlyOnApp(imeTestApp) } @@ -101,18 +106,17 @@ class OpenImeWindowFromFixedOrientationAppTest( /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( - supportedRotations = listOf(Surface.ROTATION_90), - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) + supportedRotations = listOf(Surface.ROTATION_90), + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt index f84bb4faef97..b43efea4c647 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt @@ -32,10 +32,7 @@ import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized -/** - * Test IME window opening transitions. - * To run this test: `atest FlickerTests:OpenImeWindowTest` - */ +/** Test IME window opening transitions. To run this test: `atest FlickerTests:OpenImeWindowTest` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @@ -45,40 +42,28 @@ class OpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit = { - setup { - testApp.launchViaIntent(wmHelper) - } - transitions { - testApp.openIME(wmHelper) - } + setup { testApp.launchViaIntent(wmHelper) } + transitions { testApp.openIME(wmHelper) } teardown { testApp.closeIME(wmHelper) testApp.exit(wmHelper) } } - @Presubmit - @Test - fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible() + @Presubmit @Test fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible() @Presubmit @Test fun appWindowAlwaysVisibleOnTop() { - testSpec.assertWm { - this.isAppWindowOnTop(testApp) - } + testSpec.assertWm { this.isAppWindowOnTop(testApp) } } - @Presubmit - @Test - fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible() + @Presubmit @Test fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible() @Presubmit @Test fun layerAlwaysVisible() { - testSpec.assertLayers { - this.isVisible(testApp) - } + testSpec.assertLayers { this.isVisible(testApp) } } companion object { @@ -87,11 +72,12 @@ class OpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( - supportedRotations = listOf(Surface.ROTATION_0), - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ) + supportedRotations = listOf(Surface.ROTATION_0), + supportedNavigationModes = + listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + ) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt index 184a92a32036..0a7701e54b31 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt @@ -16,7 +16,6 @@ package com.android.server.wm.flicker.ime -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.platform.test.annotations.RequiresDevice import android.view.Surface @@ -42,8 +41,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window layer will be associated with the app task when going to the overview screen. - * To run this test: `atest FlickerTests:OpenImeWindowToOverViewTest` + * Test IME window layer will be associated with the app task when going to the overview screen. To + * run this test: `atest FlickerTests:OpenImeWindowToOverViewTest` */ @RequiresDevice @RunWith(Parameterized::class) @@ -54,35 +53,30 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit = { - setup { - imeTestApp.launchViaIntent(wmHelper) - } + setup { imeTestApp.launchViaIntent(wmHelper) } transitions { device.pressRecentApps() - val builder = wmHelper.StateSyncBuilder() - .withRecentsActivityVisible() + val builder = wmHelper.StateSyncBuilder().withRecentsActivityVisible() waitNavStatusBarVisibility(builder) builder.waitForAndVerify() } teardown { device.pressHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() imeTestApp.exit(wmHelper) } } /** - * The bars (including [ComponentMatcher.STATUS_BAR] and [ComponentMatcher.NAV_BAR]) are + * The bars (including [ComponentNameMatcher.STATUS_BAR] and [ComponentNameMatcher.NAV_BAR]) are * expected to be hidden while entering overview in landscape if launcher is set to portrait * only. Because "showing portrait overview (launcher) in landscape display" is an intermediate * state depending on the touch-up to decide the intention of gesture, the display may keep in * landscape if return to app, or change to portrait if the gesture is to swipe-to-home. * - * So instead of showing landscape bars with portrait launcher at the same time - * (especially return-to-home that launcher workspace becomes visible), hide the bars until - * leave overview to have cleaner appearance. + * So instead of showing landscape bars with portrait launcher at the same time (especially + * return-to-home that launcher workspace becomes visible), hide the bars until leave overview + * to have cleaner appearance. * * b/227189877 */ @@ -90,8 +84,7 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes when { testSpec.isLandscapeOrSeascapeAtStart && !testSpec.isTablet -> stateSync.add(WindowManagerConditionsFactory.isStatusBarVisible().negate()) - else -> - stateSync.withNavOrTaskBarVisible().withStatusBarVisible() + else -> stateSync.withNavOrTaskBarVisible().withStatusBarVisible() } } @@ -109,9 +102,7 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes testSpec.navBarLayerIsVisibleAtStartAndEnd() } - /** - * Bars are expected to be hidden while entering overview in landscape (b/227189877) - */ + /** Bars are expected to be hidden while entering overview in landscape (b/227189877) */ @Presubmit @Test fun navBarLayerIsVisibleAtStartAndEndGestural() { @@ -122,8 +113,8 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes } /** - * In the legacy transitions, the nav bar is not marked as invisible. - * In the new transitions this is fixed and the nav bar shows as invisible + * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions + * this is fixed and the nav bar shows as invisible */ @Presubmit @Test @@ -132,17 +123,13 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes Assume.assumeTrue(testSpec.isLandscapeOrSeascapeAtStart) Assume.assumeTrue(testSpec.isGesturalNavigation) Assume.assumeTrue(isShellTransitionsEnabled) - testSpec.assertLayersStart { - this.isVisible(ComponentNameMatcher.NAV_BAR) - } - testSpec.assertLayersEnd { - this.isInvisible(ComponentNameMatcher.NAV_BAR) - } + testSpec.assertLayersStart { this.isVisible(ComponentNameMatcher.NAV_BAR) } + testSpec.assertLayersEnd { this.isInvisible(ComponentNameMatcher.NAV_BAR) } } /** - * In the legacy transitions, the nav bar is not marked as invisible. - * In the new transitions this is fixed and the nav bar shows as invisible + * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions + * this is fixed and the nav bar shows as invisible */ @Presubmit @Test @@ -150,17 +137,13 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes Assume.assumeTrue(testSpec.isLandscapeOrSeascapeAtStart) Assume.assumeTrue(testSpec.isGesturalNavigation) Assume.assumeFalse(testSpec.isTablet) - testSpec.assertLayersStart { - this.isVisible(ComponentNameMatcher.STATUS_BAR) - } - testSpec.assertLayersEnd { - this.isInvisible(ComponentNameMatcher.STATUS_BAR) - } + testSpec.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) } + testSpec.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) } } /** - * In the legacy transitions, the nav bar is not marked as invisible. - * In the new transitions this is fixed and the nav bar shows as invisible + * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions + * this is fixed and the nav bar shows as invisible */ @Presubmit @Test @@ -174,27 +157,30 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes /** {@inheritDoc} */ @Test @Ignore("Visibility changes depending on orientation and navigation mode") - override fun navBarLayerIsVisibleAtStartAndEnd() { } + override fun navBarLayerIsVisibleAtStartAndEnd() { + } /** {@inheritDoc} */ @Test @Ignore("Visibility changes depending on orientation and navigation mode") - override fun navBarLayerPositionAtStartAndEnd() { } + override fun navBarLayerPositionAtStartAndEnd() { + } /** {@inheritDoc} */ @Test @Ignore("Visibility changes depending on orientation and navigation mode") - override fun statusBarLayerPositionAtStartAndEnd() { } + override fun statusBarLayerPositionAtStartAndEnd() { + } /** {@inheritDoc} */ @Test @Ignore("Visibility changes depending on orientation and navigation mode") - override fun statusBarLayerIsVisibleAtStartAndEnd() { } + override fun statusBarLayerIsVisibleAtStartAndEnd() { + } - @Postsubmit + @Presubmit @Test - override fun taskBarLayerIsVisibleAtStartAndEnd() = - super.taskBarLayerIsVisibleAtStartAndEnd() + override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() @Presubmit @Test @@ -209,12 +195,8 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes Assume.assumeTrue(testSpec.isLandscapeOrSeascapeAtStart) Assume.assumeFalse(testSpec.isTablet) Assume.assumeTrue(isShellTransitionsEnabled) - testSpec.assertLayersStart { - this.isVisible(ComponentNameMatcher.STATUS_BAR) - } - testSpec.assertLayersEnd { - this.isInvisible(ComponentNameMatcher.STATUS_BAR) - } + testSpec.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) } + testSpec.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) } } @Presubmit @@ -230,11 +212,9 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes @Test fun imeLayerIsVisibleAndAssociatedWithAppWidow() { testSpec.assertLayersStart { - isVisible(ComponentNameMatcher.IME).visibleRegion(ComponentNameMatcher.IME) - .coversAtMost( - isVisible(imeTestApp) - .visibleRegion(imeTestApp).region - ) + isVisible(ComponentNameMatcher.IME) + .visibleRegion(ComponentNameMatcher.IME) + .coversAtMost(isVisible(imeTestApp).visibleRegion(imeTestApp).region) } testSpec.assertLayers { this.invoke("imeLayerIsVisibleAndAlignAppWidow") { @@ -252,8 +232,8 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic @@ -261,7 +241,8 @@ class OpenImeWindowToOverViewTest(testSpec: FlickerTestParameter) : BaseTest(tes return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90), - supportedNavigationModes = listOf( + supportedNavigationModes = + listOf( WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY ) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt index ddf61bfd34a1..4c476848ecd1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt @@ -35,8 +35,7 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window opening transitions. - * To run this test: `atest FlickerTests:ReOpenImeWindowTest` + * Test IME window opening transitions. To run this test: `atest FlickerTests:ReOpenImeWindowTest` */ @RequiresDevice @RunWith(Parameterized::class) @@ -48,24 +47,17 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit = { setup { - testApp.launchViaIntent(wmHelper) - testApp.openIME(wmHelper) - this.setRotation(testSpec.startRotation) - device.pressRecentApps() - wmHelper.StateSyncBuilder() - .withRecentsActivityVisible() - .waitForAndVerify() + testApp.launchViaIntent(wmHelper) + testApp.openIME(wmHelper) + this.setRotation(testSpec.startRotation) + device.pressRecentApps() + wmHelper.StateSyncBuilder().withRecentsActivityVisible().waitForAndVerify() } transitions { device.reopenAppFromOverview(wmHelper) - wmHelper.StateSyncBuilder() - .withFullScreenApp(testApp) - .withImeShown() - .waitForAndVerify() - } - teardown { - testApp.exit(wmHelper) + wmHelper.StateSyncBuilder().withFullScreenApp(testApp).withImeShown().waitForAndVerify() } + teardown { testApp.exit(wmHelper) } } /** {@inheritDoc} */ @@ -77,8 +69,11 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp val recentTaskComponent = ComponentNameMatcher("", "RecentTaskScreenshotSurface") testSpec.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry( - listOf(ComponentNameMatcher.SPLASH_SCREEN, - ComponentNameMatcher.SNAPSHOT, recentTaskComponent) + listOf( + ComponentNameMatcher.SPLASH_SCREEN, + ComponentNameMatcher.SNAPSHOT, + recentTaskComponent + ) ) } } @@ -90,9 +85,12 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp val component = ComponentNameMatcher("", "RecentTaskScreenshotSurface") testSpec.assertWm { this.visibleWindowsShownMoreThanOneConsecutiveEntry( - ignoreWindows = listOf(ComponentNameMatcher.SPLASH_SCREEN, + ignoreWindows = + listOf( + ComponentNameMatcher.SPLASH_SCREEN, ComponentNameMatcher.SNAPSHOT, - component) + component + ) ) } } @@ -102,14 +100,12 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp fun launcherWindowBecomesInvisible() { testSpec.assertWm { this.isAppWindowVisible(ComponentNameMatcher.LAUNCHER) - .then() - .isAppWindowInvisible(ComponentNameMatcher.LAUNCHER) + .then() + .isAppWindowInvisible(ComponentNameMatcher.LAUNCHER) } } - @Presubmit - @Test - fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible() + @Presubmit @Test fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible() @Presubmit @Test @@ -118,17 +114,13 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp // and exiting overview. Since we log 1x per frame, sometimes the activity visibility // and the app visibility are updated together, sometimes not, thus ignore activity // check at the start - testSpec.assertWm { - this.isAppWindowVisible(testApp) - } + testSpec.assertWm { this.isAppWindowVisible(testApp) } } @Presubmit @Test fun imeLayerBecomesVisible() { - testSpec.assertLayers { - this.isVisible(ComponentNameMatcher.IME) - } + testSpec.assertLayers { this.isVisible(ComponentNameMatcher.IME) } } @Presubmit @@ -148,9 +140,7 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests( - supportedRotations = listOf(Surface.ROTATION_0) - ) + .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt index 65f51e0dc834..02b3b1398e50 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt @@ -41,17 +41,15 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME windows switching with 2-Buttons or gestural navigation. - * To run this test: `atest FlickerTests:SwitchImeWindowsFromGestureNavTest` + * Test IME windows switching with 2-Buttons or gestural navigation. To run this test: `atest + * FlickerTests:SwitchImeWindowsFromGestureNavTest` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Presubmit -open class SwitchImeWindowsFromGestureNavTest( - testSpec: FlickerTestParameter -) : BaseTest(testSpec) { +open class SwitchImeWindowsFromGestureNavTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { private val testApp = SimpleAppHelper(instrumentation) private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation) @@ -66,22 +64,16 @@ open class SwitchImeWindowsFromGestureNavTest( tapl.setExpectedRotationCheckEnabled(false) this.setRotation(testSpec.startRotation) testApp.launchViaIntent(wmHelper) - wmHelper.StateSyncBuilder() - .withFullScreenApp(testApp) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify() imeTestApp.launchViaIntent(wmHelper) - wmHelper.StateSyncBuilder() - .withFullScreenApp(imeTestApp) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withFullScreenApp(imeTestApp).waitForAndVerify() imeTestApp.openIME(wmHelper) } teardown { tapl.goHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() testApp.exit(wmHelper) imeTestApp.exit(wmHelper) } @@ -89,17 +81,13 @@ open class SwitchImeWindowsFromGestureNavTest( // [Step1]: Swipe right from imeTestApp to testApp task createTag(TAG_IME_VISIBLE) tapl.launchedAppState.quickSwitchToPreviousApp() - wmHelper.StateSyncBuilder() - .withFullScreenApp(testApp) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify() createTag(TAG_IME_INVISIBLE) } transitions { // [Step2]: Swipe left to back to imeTestApp task tapl.launchedAppState.quickSwitchToPreviousAppSwipeLeft() - wmHelper.StateSyncBuilder() - .withFullScreenApp(imeTestApp) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withFullScreenApp(imeTestApp).waitForAndVerify() } } @@ -109,9 +97,7 @@ open class SwitchImeWindowsFromGestureNavTest( override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun entireScreenCovered() = super.entireScreenCovered() + @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ @Postsubmit @@ -137,8 +123,7 @@ open class SwitchImeWindowsFromGestureNavTest( /** {@inheritDoc} */ @Postsubmit @Test - override fun statusBarLayerPositionAtStartAndEnd() = - super.statusBarLayerPositionAtStartAndEnd() + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ @Postsubmit @@ -171,23 +156,15 @@ open class SwitchImeWindowsFromGestureNavTest( @FlakyTest(bugId = 244414110) @Test open fun imeLayerIsVisibleWhenSwitchingToImeApp() { - testSpec.assertLayersStart { - isVisible(ComponentNameMatcher.IME) - } - testSpec.assertLayersTag(TAG_IME_VISIBLE) { - isVisible(ComponentNameMatcher.IME) - } - testSpec.assertLayersEnd { - isVisible(ComponentNameMatcher.IME) - } + testSpec.assertLayersStart { isVisible(ComponentNameMatcher.IME) } + testSpec.assertLayersTag(TAG_IME_VISIBLE) { isVisible(ComponentNameMatcher.IME) } + testSpec.assertLayersEnd { isVisible(ComponentNameMatcher.IME) } } @Presubmit @Test fun imeLayerIsInvisibleWhenSwitchingToTestApp() { - testSpec.assertLayersTag(TAG_IME_INVISIBLE) { - isInvisible(ComponentNameMatcher.IME) - } + testSpec.assertLayersTag(TAG_IME_INVISIBLE) { isInvisible(ComponentNameMatcher.IME) } } companion object { @@ -196,9 +173,8 @@ open class SwitchImeWindowsFromGestureNavTest( fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ), + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY), supportedRotations = listOf(Surface.ROTATION_0) ) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt index bd06279d53cd..80ab01624703 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt @@ -16,7 +16,6 @@ package com.android.server.wm.flicker.ime -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -33,36 +32,35 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME windows switching with 2-Buttons or gestural navigation. - * To run this test: `atest FlickerTests:SwitchImeWindowsFromGestureNavTest` + * Test IME windows switching with 2-Buttons or gestural navigation. To run this test: `atest + * FlickerTests:SwitchImeWindowsFromGestureNavTest` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class SwitchImeWindowsFromGestureNavTest_ShellTransit( - testSpec: FlickerTestParameter -) : SwitchImeWindowsFromGestureNavTest(testSpec) { +class SwitchImeWindowsFromGestureNavTest_ShellTransit(testSpec: FlickerTestParameter) : + SwitchImeWindowsFromGestureNavTest(testSpec) { @Before override fun before() { Assume.assumeTrue(isShellTransitionsEnabled) } - @FlakyTest(bugId = 228012334) + @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() - @FlakyTest(bugId = 228012334) + @Presubmit @Test override fun imeLayerIsVisibleWhenSwitchingToImeApp() = super.imeLayerIsVisibleWhenSwitchingToImeApp() - @FlakyTest(bugId = 228012334) + @Presubmit @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() - @FlakyTest(bugId = 228012334) + @Presubmit @Test override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() @@ -73,8 +71,8 @@ class SwitchImeWindowsFromGestureNavTest_ShellTransit( override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** - * Checks that [ComponentMatcher.NAV_BAR] window is visible and above the app windows at the start - * and end of the WM trace + * Checks that [ComponentMatcher.NAV_BAR] window is visible and above the app windows at the + * start and end of the WM trace */ @Presubmit @Test diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt index 1e575e614e36..49bf86d0c266 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt @@ -40,15 +40,18 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:ActivitiesTransitionTest` * * Actions: + * ``` * Launch an app * Launch a secondary activity within the app * Close the secondary activity back to the initial one - * + * ``` * Notes: + * ``` * 1. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -63,15 +66,11 @@ class ActivitiesTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSp tapl.setExpectedRotation(testSpec.startRotation) testApp.launchViaIntent(wmHelper) } - teardown { - testApp.exit(wmHelper) - } + teardown { testApp.exit(wmHelper) } transitions { testApp.openSecondActivity(device, wmHelper) tapl.pressBack() - wmHelper.StateSyncBuilder() - .withFullScreenApp(testApp) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify() } } @@ -81,9 +80,9 @@ class ActivitiesTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSp override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() /** - * Checks that the [ActivityOptions.LaunchNewActivity] activity is visible at - * the start of the transition, that [ActivityOptions.SimpleActivity] becomes visible during - * the transition, and that [ActivityOptions.LaunchNewActivity] is again visible at the end + * Checks that the [ActivityOptions.LaunchNewActivity] activity is visible at the start of the + * transition, that [ActivityOptions.SimpleActivity] becomes visible during the transition, and + * that [ActivityOptions.LaunchNewActivity] is again visible at the end */ @Presubmit @Test @@ -109,9 +108,7 @@ class ActivitiesTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSp @Presubmit @Test fun launcherWindowNotOnTop() { - testSpec.assertWm { - this.isAppWindowNotOnTop(ComponentNameMatcher.LAUNCHER) - } + testSpec.assertWm { this.isAppWindowNotOnTop(ComponentNameMatcher.LAUNCHER) } } /** @@ -127,14 +124,13 @@ class ActivitiesTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSp /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt index 480226fd86da..d0d7bbb42330 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt @@ -35,22 +35,21 @@ import org.junit.runners.Parameterized * * To run this test: `atest FlickerTests:OpenAppAfterCameraTest` * - * Notes: - * Some default assertions are inherited [OpenAppTransition] + * Notes: Some default assertions are inherited [OpenAppTransition] */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppAfterCameraTest( - testSpec: FlickerTestParameter -) : OpenAppFromLauncherTransition(testSpec) { +open class OpenAppAfterCameraTest(testSpec: FlickerTestParameter) : + OpenAppFromLauncherTransition(testSpec) { @Before open fun before() { Assume.assumeFalse(isShellTransitionsEnabled) } - private val cameraApp = CameraAppHelper(instrumentation) /** {@inheritDoc} */ + private val cameraApp = CameraAppHelper(instrumentation) + /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { super.transition(this) @@ -62,26 +61,21 @@ open class OpenAppAfterCameraTest( // 2. Press home button (button nav mode) / swipe up to home (gesture nav mode) tapl.goHome() } - teardown { - testApp.exit(wmHelper) - } - transitions { - testApp.launchViaIntent(wmHelper) - } + teardown { testApp.exit(wmHelper) } + transitions { testApp.launchViaIntent(wmHelper) } } companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest_ShellTransit.kt index 9e271061cc19..cb61e35eaedc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest_ShellTransit.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest_ShellTransit.kt @@ -33,16 +33,14 @@ import org.junit.runners.Parameterized * * To run this test: `atest FlickerTests:OpenAppAfterCameraTest_ShellTransit` * - * Notes: - * Some default assertions are inherited [OpenAppTransition] + * Notes: Some default assertions are inherited [OpenAppTransition] */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppAfterCameraTest_ShellTransit( - testSpec: FlickerTestParameter -) : OpenAppAfterCameraTest(testSpec) { +class OpenAppAfterCameraTest_ShellTransit(testSpec: FlickerTestParameter) : + OpenAppAfterCameraTest(testSpec) { @Before override fun before() { Assume.assumeFalse(isShellTransitionsEnabled) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt index 8bc1b61f2fbd..7ccfeb7f6edd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt @@ -37,24 +37,26 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:OpenAppColdFromIcon` * * Actions: + * ``` * Make sure no apps are running on the device * Launch an app [testApp] by clicking it's icon on all apps and wait animation to complete - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [OpenAppTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppColdFromIcon( - testSpec: FlickerTestParameter -) : OpenAppFromLauncherTransition(testSpec) { +class OpenAppColdFromIcon(testSpec: FlickerTestParameter) : + OpenAppFromLauncherTransition(testSpec) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { @@ -69,21 +71,17 @@ class OpenAppColdFromIcon( this.setRotation(testSpec.startRotation) } transitions { - tapl.goHome() + tapl + .goHome() .switchToAllApps() .getAppIcon(testApp.launcherName) .launch(testApp.`package`) } - teardown { - testApp.exit(wmHelper) - } + teardown { testApp.exit(wmHelper) } } /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowAsTopWindowAtEnd() = - super.appWindowAsTopWindowAtEnd() + @Postsubmit @Test override fun appWindowAsTopWindowAtEnd() = super.appWindowAsTopWindowAtEnd() /** {@inheritDoc} */ @Postsubmit @@ -92,35 +90,22 @@ class OpenAppColdFromIcon( super.appWindowReplacesLauncherAsTopWindow() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appLayerBecomesVisible() = - super.appLayerBecomesVisible() + @Postsubmit @Test override fun appLayerBecomesVisible() = super.appLayerBecomesVisible() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() + @Postsubmit @Test override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow() + @Postsubmit @Test override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowBecomesVisible() = super.appWindowBecomesVisible() + @Postsubmit @Test override fun appWindowBecomesVisible() = super.appWindowBecomesVisible() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun entireScreenCovered() = super.entireScreenCovered() + @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun focusChanges() = super.focusChanges() + @Postsubmit @Test override fun focusChanges() = super.focusChanges() /** {@inheritDoc} */ @Postsubmit @@ -150,8 +135,7 @@ class OpenAppColdFromIcon( /** {@inheritDoc} */ @Postsubmit @Test - override fun statusBarLayerPositionAtStartAndEnd() = - super.statusBarLayerPositionAtStartAndEnd() + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ @Postsubmit @@ -177,22 +161,19 @@ class OpenAppColdFromIcon( super.visibleWindowsShownMoreThanOneConsecutiveEntry() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd() + @Postsubmit @Test override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd() companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt index 0c169809b63f..7cd8526dc0f7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt @@ -38,25 +38,27 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:OpenAppColdTest` * * Actions: + * ``` * Make sure no apps are running on the device * Launch an app [testApp] and wait animation to complete - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [OpenAppTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @FlickerServiceCompatible @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppColdTest( - testSpec: FlickerTestParameter -) : OpenAppFromLauncherTransition(testSpec) { +open class OpenAppColdTest(testSpec: FlickerTestParameter) : + OpenAppFromLauncherTransition(testSpec) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { @@ -65,24 +67,17 @@ open class OpenAppColdTest( removeAllTasksButHome() this.setRotation(testSpec.startRotation) } - teardown { - testApp.exit(wmHelper) - } - transitions { - testApp.launchViaIntent(wmHelper) - } + teardown { testApp.exit(wmHelper) } + transitions { testApp.launchViaIntent(wmHelper) } } /** {@inheritDoc} */ @FlakyTest(bugId = 206753786) @Test - override fun navBarLayerPositionAtStartAndEnd() = - super.navBarLayerPositionAtStartAndEnd() + override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Presubmit - @Test - override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() + @Presubmit @Test override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() /** {@inheritDoc} */ @FlakyTest(bugId = 240238245) @@ -94,14 +89,13 @@ open class OpenAppColdTest( /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt index bece406d5a96..23748bef96fd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt @@ -22,32 +22,27 @@ import com.android.server.wm.flicker.replacesLayer import com.android.server.wm.traces.common.ComponentNameMatcher import org.junit.Test -/** - * Base class for app launch tests - */ -abstract class OpenAppFromLauncherTransition( - testSpec: FlickerTestParameter -) : OpenAppTransition(testSpec) { +/** Base class for app launch tests */ +abstract class OpenAppFromLauncherTransition(testSpec: FlickerTestParameter) : + OpenAppTransition(testSpec) { - /** - * Checks that the focus changes from the [ComponentMatcher.LAUNCHER] to [testApp] - */ + /** Checks that the focus changes from the [ComponentMatcher.LAUNCHER] to [testApp] */ @Presubmit @Test open fun focusChanges() { - testSpec.assertEventLog { - this.focusChanges("NexusLauncherActivity", testApp.`package`) - } + testSpec.assertEventLog { this.focusChanges("NexusLauncherActivity", testApp.`package`) } } /** - * Checks that [ComponentMatcher.LAUNCHER] layer is visible at the start of the transition, - * and is replaced by [testApp], which remains visible until the end + * Checks that [ComponentMatcher.LAUNCHER] layer is visible at the start of the transition, and + * is replaced by [testApp], which remains visible until the end */ open fun appLayerReplacesLauncher() { testSpec.replacesLayer( - ComponentNameMatcher.LAUNCHER, testApp, - ignoreEntriesWithRotationLayer = true, ignoreSnapshot = true, + ComponentNameMatcher.LAUNCHER, + testApp, + ignoreEntriesWithRotationLayer = true, + ignoreSnapshot = true, ignoreSplashscreen = true ) } @@ -64,21 +59,15 @@ abstract class OpenAppFromLauncherTransition( this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER) .then() .isAppWindowOnTop( - testApp - .or(ComponentNameMatcher.SNAPSHOT) - .or(ComponentNameMatcher.SPLASH_SCREEN) + testApp.or(ComponentNameMatcher.SNAPSHOT).or(ComponentNameMatcher.SPLASH_SCREEN) ) } } - /** - * Checks that [testApp] window is the top window at the en dof the trace - */ + /** Checks that [testApp] window is the top window at the en dof the trace */ @Presubmit @Test open fun appWindowAsTopWindowAtEnd() { - testSpec.assertWmEnd { - this.isAppWindowOnTop(testApp) - } + testSpec.assertWmEnd { this.isAppWindowOnTop(testApp) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt index a9972ce4610d..2469faeaaa66 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt @@ -54,18 +54,14 @@ open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter) : get() = { // Needs to run at start of transition, // so before the transition defined in super.transition - transitions { - device.wakeUp() - } + transitions { device.wakeUp() } super.transition(this) // Needs to run at the end of the setup, so after the setup defined in super.transition setup { device.sleep() - wmHelper.StateSyncBuilder() - .withoutTopVisibleAppWindows() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify() } } @@ -86,13 +82,9 @@ open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter) : override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow() /** {@inheritDoc} */ - @Test - @Ignore("Display is off at the start") - override fun navBarLayerPositionAtStartAndEnd() { } + @Test @Ignore("Display is off at the start") override fun navBarLayerPositionAtStartAndEnd() {} - /** - * Checks the position of the [ComponentMatcher.NAV_BAR] at the end of the transition - */ + /** Checks the position of the [ComponentMatcher.NAV_BAR] at the end of the transition */ @Postsubmit @Test fun navBarLayerPositionAtEnd() { @@ -103,15 +95,13 @@ open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter) : /** {@inheritDoc} */ @Test @Ignore("Display is off at the start") - override fun statusBarLayerPositionAtStartAndEnd() { } + override fun statusBarLayerPositionAtStartAndEnd() {} /** * Checks the position of the [ComponentMatcher.STATUS_BAR] at the start and end of the * transition */ - @Postsubmit - @Test - fun statusBarLayerPositionEnd() = testSpec.statusBarLayerPositionAtEnd() + @Postsubmit @Test fun statusBarLayerPositionEnd() = testSpec.statusBarLayerPositionAtEnd() /** {@inheritDoc} */ @Postsubmit @@ -124,9 +114,7 @@ open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter) : override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appLayerBecomesVisible() = super.appLayerBecomesVisible() + @Postsubmit @Test override fun appLayerBecomesVisible() = super.appLayerBecomesVisible() /** {@inheritDoc} */ @Postsubmit @@ -134,9 +122,7 @@ open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter) : override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowBecomesVisible() = super.appWindowBecomesVisible() + @Postsubmit @Test override fun appWindowBecomesVisible() = super.appWindowBecomesVisible() /** {@inheritDoc} */ @Postsubmit @@ -151,23 +137,19 @@ open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter) : super.visibleLayersShownMoreThanOneConsecutiveEntry() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowIsTopWindowAtEnd() = - super.appWindowIsTopWindowAtEnd() + @Postsubmit @Test override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd() companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt index 950c54c84a1c..c26b665953da 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt @@ -58,49 +58,40 @@ open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter) : get() = { // Needs to run at start of transition, // so before the transition defined in super.transition - transitions { - device.wakeUp() - } + transitions { device.wakeUp() } super.transition(this) // Needs to run at the end of the setup, so after the setup defined in super.transition setup { device.sleep() - wmHelper.StateSyncBuilder() - .withoutTopVisibleAppWindows() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify() } } /** - * Checks that we start of with no top windows and then [testApp] becomes the first and - * only top window of the transition, with snapshot or splash screen windows optionally showing - * first. + * Checks that we start of with no top windows and then [testApp] becomes the first and only top + * window of the transition, with snapshot or splash screen windows optionally showing first. */ @Test @Postsubmit open fun appWindowBecomesFirstAndOnlyTopWindow() { testSpec.assertWm { this.hasNoVisibleAppWindow() - .then() - .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true) - .then() - .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true) - .then() - .isAppWindowOnTop(testApp) + .then() + .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true) + .then() + .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true) + .then() + .isAppWindowOnTop(testApp) } } - /** - * Checks that the screen is locked at the start of the transition - */ + /** Checks that the screen is locked at the start of the transition */ @Test @Postsubmit fun screenLockedStart() { - testSpec.assertWmStart { - isKeyguardShowing() - } + testSpec.assertWmStart { isKeyguardShowing() } } /** {@inheritDoc} */ @@ -117,11 +108,9 @@ open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter) : /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end") - override fun navBarLayerPositionAtStartAndEnd() { } + override fun navBarLayerPositionAtStartAndEnd() {} - /** - * Checks the position of the [ComponentNameMatcher.NAV_BAR] at the end of the transition - */ + /** Checks the position of the [ComponentNameMatcher.NAV_BAR] at the end of the transition */ @Postsubmit @Test fun navBarLayerPositionAtEnd() { @@ -132,40 +121,31 @@ open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter) : /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end") - override fun statusBarLayerPositionAtStartAndEnd() { } + override fun statusBarLayerPositionAtStartAndEnd() {} /** * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the * transition */ - @Postsubmit - @Test - fun statusBarLayerPositionEnd() = testSpec.statusBarLayerPositionAtEnd() + @Postsubmit @Test fun statusBarLayerPositionEnd() = testSpec.statusBarLayerPositionAtEnd() /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end") - override fun navBarLayerIsVisibleAtStartAndEnd() = - super.navBarLayerIsVisibleAtStartAndEnd() + override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd() /** {@inheritDoc} */ - @Postsubmit - @Test - fun navBarLayerIsVisibleAtEnd() = testSpec.navBarLayerIsVisibleAtEnd() + @Postsubmit @Test fun navBarLayerIsVisibleAtEnd() = testSpec.navBarLayerIsVisibleAtEnd() /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end") override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() - @Postsubmit - @Test - fun navBarWindowIsVisibleAtEnd() = testSpec.navBarWindowIsVisibleAtEnd() + @Postsubmit @Test fun navBarWindowIsVisibleAtEnd() = testSpec.navBarWindowIsVisibleAtEnd() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appLayerBecomesVisible() = super.appLayerBecomesVisible() + @Postsubmit @Test override fun appLayerBecomesVisible() = super.appLayerBecomesVisible() /** {@inheritDoc} */ @Postsubmit @@ -173,14 +153,10 @@ open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter) : override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow() + @Postsubmit @Test override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowBecomesVisible() = super.appWindowBecomesVisible() + @Postsubmit @Test override fun appWindowBecomesVisible() = super.appWindowBecomesVisible() /** {@inheritDoc} */ @Postsubmit @@ -195,10 +171,7 @@ open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter) : super.visibleLayersShownMoreThanOneConsecutiveEntry() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowIsTopWindowAtEnd() = - super.appWindowIsTopWindowAtEnd() + @Postsubmit @Test override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd() /** {@inheritDoc} */ @Presubmit @@ -210,14 +183,13 @@ open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter) : /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt index c07989425074..0b4361ce8f2e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt @@ -33,8 +33,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test cold launching an app from a notification from the lock screen when there is an app - * overlaid on the lock screen. + * Test cold launching an app from a notification from the lock screen when there is an app overlaid + * on the lock screen. * * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWithLockOverlayApp` */ @@ -46,7 +46,7 @@ import org.junit.runners.Parameterized class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParameter) : OpenAppFromLockNotificationCold(testSpec) { private val showWhenLockedApp: ShowWhenLockedAppHelper = - ShowWhenLockedAppHelper(instrumentation) + ShowWhenLockedAppHelper(instrumentation) // Although we are technically still locked here, the overlay app means we should open the // notification shade as if we were unlocked. @@ -61,19 +61,13 @@ class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParamet // Launch an activity that is shown when the device is locked showWhenLockedApp.launchViaIntent(wmHelper) - wmHelper.StateSyncBuilder() - .withFullScreenApp(showWhenLockedApp) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withFullScreenApp(showWhenLockedApp).waitForAndVerify() device.sleep() - wmHelper.StateSyncBuilder() - .withoutTopVisibleAppWindows() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify() } - teardown { - showWhenLockedApp.exit(wmHelper) - } + teardown { showWhenLockedApp.exit(wmHelper) } } @Test @@ -81,10 +75,10 @@ class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParamet fun showWhenLockedAppWindowBecomesVisible() { testSpec.assertWm { this.hasNoVisibleAppWindow() - .then() - .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true) - .then() - .isAppWindowOnTop(showWhenLockedApp) + .then() + .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true) + .then() + .isAppWindowOnTop(showWhenLockedApp) } } @@ -93,10 +87,10 @@ class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParamet fun showWhenLockedAppLayerBecomesVisible() { testSpec.assertLayers { this.isInvisible(showWhenLockedApp) - .then() - .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) - .then() - .isVisible(showWhenLockedApp) + .then() + .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) + .then() + .isVisible(showWhenLockedApp) } } @@ -106,22 +100,19 @@ class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParamet override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow() + @Postsubmit @Test override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow() companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt index f574c9e12c96..3cc23909b603 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt @@ -27,40 +27,26 @@ import org.junit.Assume import org.junit.Ignore import org.junit.Test -/** - * Base class for app launch tests from lock screen - */ +/** Base class for app launch tests from lock screen */ abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) { - /** - * Defines the transition used to run the test - */ + /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit = { super.transition(this) setup { device.sleep() - wmHelper.StateSyncBuilder() - .withoutTopVisibleAppWindows() - .waitForAndVerify() - } - teardown { - testApp.exit(wmHelper) - } - transitions { - testApp.launchViaIntent(wmHelper) + wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify() } + teardown { testApp.exit(wmHelper) } + transitions { testApp.launchViaIntent(wmHelper) } } - /** - * Check that we go from no focus to focus on the [testApp] - */ + /** Check that we go from no focus to focus on the [testApp] */ @Presubmit @Test open fun focusChanges() { - testSpec.assertEventLog { - this.focusChanges("", testApp.`package`) - } + testSpec.assertEventLog { this.focusChanges("", testApp.`package`) } } /** @@ -72,24 +58,20 @@ abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter) : open fun appWindowBecomesFirstAndOnlyTopWindow() { testSpec.assertWm { this.hasNoVisibleAppWindow() - .then() - .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true) - .then() - .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true) - .then() - .isAppWindowOnTop(testApp) + .then() + .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true) + .then() + .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true) + .then() + .isAppWindowOnTop(testApp) } } - /** - * Checks that the screen is locked at the start of the transition - */ + /** Checks that the screen is locked at the start of the transition */ @Presubmit @Test fun screenLockedStart() { - testSpec.assertLayersStart { - isEmpty() - } + testSpec.assertLayersStart { isEmpty() } } /** {@inheritDoc} */ @@ -100,26 +82,24 @@ abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter) : /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end") - override fun navBarLayerPositionAtStartAndEnd() { } + override fun navBarLayerPositionAtStartAndEnd() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end") - override fun statusBarLayerPositionAtStartAndEnd() { } + override fun statusBarLayerPositionAtStartAndEnd() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end") - override fun taskBarLayerIsVisibleAtStartAndEnd() { } + override fun taskBarLayerIsVisibleAtStartAndEnd() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end") - override fun taskBarWindowIsAlwaysVisible() { } + override fun taskBarWindowIsAlwaysVisible() {} - /** - * Checks the position of the [ComponentMatcher.NAV_BAR] at the end of the transition - */ + /** Checks the position of the [ComponentMatcher.NAV_BAR] at the end of the transition */ @Presubmit @Test open fun navBarLayerPositionAtEnd() { @@ -127,17 +107,13 @@ abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter) : testSpec.navBarLayerPositionAtEnd() } - /** - * Checks the position of the [ComponentMatcher.STATUS_BAR] at the end of the transition - */ - @Presubmit - @Test - fun statusBarLayerPositionAtEnd() = testSpec.statusBarLayerPositionAtEnd() + /** Checks the position of the [ComponentMatcher.STATUS_BAR] at the end of the transition */ + @Presubmit @Test fun statusBarLayerPositionAtEnd() = testSpec.statusBarLayerPositionAtEnd() /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end") - override fun statusBarLayerIsVisibleAtStartAndEnd() { } + override fun statusBarLayerIsVisibleAtStartAndEnd() {} /** * Checks that the [ComponentMatcher.STATUS_BAR] layer is visible at the end of the trace @@ -147,8 +123,6 @@ abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter) : @Presubmit @Test fun statusBarLayerIsVisibleAtEnd() { - testSpec.assertLayersEnd { - this.isVisible(ComponentNameMatcher.STATUS_BAR) - } + testSpec.assertLayersEnd { this.isVisible(ComponentNameMatcher.STATUS_BAR) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt index dca3affc8f25..6802d7a5a372 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt @@ -39,9 +39,8 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Postsubmit -open class OpenAppFromNotificationCold( - testSpec: FlickerTestParameter -) : OpenAppFromNotificationWarm(testSpec) { +open class OpenAppFromNotificationCold(testSpec: FlickerTestParameter) : + OpenAppFromNotificationWarm(testSpec) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { @@ -60,14 +59,13 @@ open class OpenAppFromNotificationCold( /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt index e8a6a69cc50b..1ae0d5374c59 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt @@ -54,9 +54,8 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Postsubmit -open class OpenAppFromNotificationWarm( - testSpec: FlickerTestParameter -) : OpenAppTransition(testSpec) { +open class OpenAppFromNotificationWarm(testSpec: FlickerTestParameter) : + OpenAppTransition(testSpec) { override val testApp: NotificationAppHelper = NotificationAppHelper(instrumentation) open val openingNotificationsFromLockScreen = false @@ -68,14 +67,10 @@ open class OpenAppFromNotificationWarm( device.wakeUpAndGoToHomeScreen() this.setRotation(testSpec.startRotation) testApp.launchViaIntent(wmHelper) - wmHelper.StateSyncBuilder() - .withFullScreenApp(testApp) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify() testApp.postNotification(wmHelper) device.pressHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() } transitions { @@ -87,10 +82,10 @@ open class OpenAppFromNotificationWarm( instrumentation.context.getSystemService(WindowManager::class.java) ?: error("Unable to connect to WindowManager service") val metricInsets = wm.currentWindowMetrics.windowInsets - val insets = metricInsets.getInsetsIgnoringVisibility( - WindowInsets.Type.statusBars() - or WindowInsets.Type.displayCutout() - ) + val insets = + metricInsets.getInsetsIgnoringVisibility( + WindowInsets.Type.statusBars() or WindowInsets.Type.displayCutout() + ) startY = insets.top + 100 endY = device.displayHeight / 2 @@ -104,23 +99,16 @@ open class OpenAppFromNotificationWarm( instrumentation.uiAutomation.syncInputTransactions() // Launch the activity by clicking the notification - val notification = device.wait( - Until.findObject( - By.text("Flicker Test Notification") - ), 2000L - ) + val notification = + device.wait(Until.findObject(By.text("Flicker Test Notification")), 2000L) notification?.click() ?: error("Notification not found") instrumentation.uiAutomation.syncInputTransactions() // Wait for the app to launch - wmHelper.StateSyncBuilder() - .withFullScreenApp(testApp) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify() } - teardown { - testApp.exit(wmHelper) - } + teardown { testApp.exit(wmHelper) } } /** {@inheritDoc} */ @@ -137,8 +125,7 @@ open class OpenAppFromNotificationWarm( /** {@inheritDoc} */ @Postsubmit @Test - override fun statusBarLayerPositionAtStartAndEnd() = - super.statusBarLayerPositionAtStartAndEnd() + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ @Postsubmit @@ -147,14 +134,10 @@ open class OpenAppFromNotificationWarm( super.visibleLayersShownMoreThanOneConsecutiveEntry() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowBecomesVisible() = appWindowBecomesVisible_warmStart() + @Postsubmit @Test override fun appWindowBecomesVisible() = appWindowBecomesVisible_warmStart() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appLayerBecomesVisible() = appLayerBecomesVisible_warmStart() + @Postsubmit @Test override fun appLayerBecomesVisible() = appLayerBecomesVisible_warmStart() /** {@inheritDoc} */ @Postsubmit @@ -162,9 +145,7 @@ open class OpenAppFromNotificationWarm( override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun entireScreenCovered() = super.entireScreenCovered() + @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ @Postsubmit @@ -183,33 +164,24 @@ open class OpenAppFromNotificationWarm( super.visibleWindowsShownMoreThanOneConsecutiveEntry() /** {@inheritDoc} */ - @Postsubmit - @Test - override fun appWindowIsTopWindowAtEnd() = - super.appWindowIsTopWindowAtEnd() + @Postsubmit @Test override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd() @Postsubmit @Test open fun notificationAppWindowVisibleAtEnd() { - testSpec.assertWmEnd { - this.isAppWindowVisible(testApp) - } + testSpec.assertWmEnd { this.isAppWindowVisible(testApp) } } @Postsubmit @Test open fun notificationAppWindowOnTopAtEnd() { - testSpec.assertWmEnd { - this.isAppWindowOnTop(testApp) - } + testSpec.assertWmEnd { this.isAppWindowOnTop(testApp) } } @Postsubmit @Test open fun notificationAppLayerVisibleAtEnd() { - testSpec.assertLayersEnd { - this.isVisible(testApp) - } + testSpec.assertLayersEnd { this.isVisible(testApp) } } /** {@inheritDoc} */ @@ -241,8 +213,7 @@ open class OpenAppFromNotificationWarm( } /** - * Checks that the [ComponentNameMatcher.TASK_BAR] layer is visible at the end of the - * transition + * Checks that the [ComponentNameMatcher.TASK_BAR] layer is visible at the end of the transition * * Note: Large screen only */ @@ -256,27 +227,24 @@ open class OpenAppFromNotificationWarm( /** {@inheritDoc} */ @Test @Ignore("Display is locked at the start") - override fun taskBarWindowIsAlwaysVisible() = - super.taskBarWindowIsAlwaysVisible() + override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible() /** {@inheritDoc} */ @Test @Ignore("Display is locked at the start") - override fun taskBarLayerIsVisibleAtStartAndEnd() = - super.taskBarLayerIsVisibleAtStartAndEnd() + override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt index 0bf57caefcda..fd8a38c3f6b7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -38,31 +38,31 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:OpenAppFromOverviewTest` * * Actions: + * ``` * Launch [testApp] * Press recents * Relaunch an app [testApp] by selecting it in the overview screen, and wait animation to * complete (only this action is traced) - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [OpenAppTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @FlickerServiceCompatible @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromOverviewTest( - testSpec: FlickerTestParameter -) : OpenAppFromLauncherTransition(testSpec) { +open class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : + OpenAppFromLauncherTransition(testSpec) { - /** - * Defines the transition used to run the test - */ + /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit get() = { super.transition(this) @@ -70,9 +70,7 @@ open class OpenAppFromOverviewTest( tapl.setExpectedRotationCheckEnabled(false) testApp.launchViaIntent(wmHelper) tapl.goHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() // By default, launcher doesn't rotate on phones, but rotates on tablets if (testSpec.isTablet) { tapl.setExpectedRotation(testSpec.startRotation) @@ -80,23 +78,17 @@ open class OpenAppFromOverviewTest( tapl.setExpectedRotation(Surface.ROTATION_0) } tapl.workspace.switchToOverview() - wmHelper.StateSyncBuilder() - .withRecentsActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withRecentsActivityVisible().waitForAndVerify() this.setRotation(testSpec.startRotation) } transitions { tapl.overview.currentTask.open() - wmHelper.StateSyncBuilder() - .withFullScreenApp(testApp) - .waitForAndVerify() + wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify() } } /** {@inheritDoc} */ - @Presubmit - @Test - override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() + @Presubmit @Test override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() /** {@inheritDoc} */ @FlakyTest @@ -117,14 +109,13 @@ open class OpenAppFromOverviewTest( /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt index 11f3cced5b4d..559728739111 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt @@ -45,16 +45,19 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:OpenAppNonResizeableTest` * * Actions: + * ``` * Lock the device. * Launch an app on top of the lock screen [testApp] and wait animation to complete - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [OpenAppTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @FlickerServiceCompatible @@ -80,15 +83,11 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : } } - /** - * Checks if [testApp] is visible at the end of the transition - */ + /** Checks if [testApp] is visible at the end of the transition */ @Presubmit @Test fun appWindowBecomesVisibleAtEnd() { - testSpec.assertWmEnd { - this.isAppWindowVisible(testApp) - } + testSpec.assertWmEnd { this.isAppWindowVisible(testApp) } } /** @@ -114,9 +113,7 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : @Test fun taskBarLayerIsVisibleAtEnd() { Assume.assumeTrue(testSpec.isTablet) - testSpec.assertLayersEnd { - this.isVisible(ComponentNameMatcher.TASK_BAR) - } + testSpec.assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) } } /** @@ -127,59 +124,43 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : @Presubmit @Test override fun statusBarLayerIsVisibleAtStartAndEnd() { - testSpec.assertLayersEnd { - this.isVisible(ComponentNameMatcher.STATUS_BAR) - } + testSpec.assertLayersEnd { this.isVisible(ComponentNameMatcher.STATUS_BAR) } } /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end") - override fun taskBarLayerIsVisibleAtStartAndEnd() { - } + override fun taskBarLayerIsVisibleAtStartAndEnd() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end") - override fun navBarLayerIsVisibleAtStartAndEnd() { - } + override fun navBarLayerIsVisibleAtStartAndEnd() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end") - override fun taskBarWindowIsAlwaysVisible() { - } + override fun taskBarWindowIsAlwaysVisible() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end") - override fun navBarWindowIsAlwaysVisible() { - } + override fun navBarWindowIsAlwaysVisible() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end") - override fun statusBarWindowIsAlwaysVisible() { - } + override fun statusBarWindowIsAlwaysVisible() {} - /** - * Checks the position of the [ComponentMatcher.STATUS_BAR] at the end of the - * transition - */ - @Presubmit - @Test - fun statusBarLayerPositionEnd() = testSpec.statusBarLayerPositionAtEnd() + /** Checks the position of the [ComponentMatcher.STATUS_BAR] at the end of the transition */ + @Presubmit @Test fun statusBarLayerPositionEnd() = testSpec.statusBarLayerPositionAtEnd() - /** - * Checks the [ComponentMatcher.NAV_BAR] is visible at the end of the transition - */ + /** Checks the [ComponentMatcher.NAV_BAR] is visible at the end of the transition */ @Postsubmit @Test fun navBarLayerIsVisibleAtEnd() { Assume.assumeFalse(testSpec.isTablet) - testSpec.assertLayersEnd { - this.isVisible(ComponentNameMatcher.NAV_BAR) - } + testSpec.assertLayersEnd { this.isVisible(ComponentNameMatcher.NAV_BAR) } } /** {@inheritDoc} */ @@ -205,9 +186,7 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : } /** {@inheritDoc} */ - @FlakyTest - @Test - override fun entireScreenCovered() = super.entireScreenCovered() + @FlakyTest @Test override fun entireScreenCovered() = super.entireScreenCovered() @FlakyTest(bugId = 218470989) @Test @@ -216,23 +195,22 @@ open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : @FlakyTest(bugId = 227143265) @Test - override fun appWindowBecomesTopWindow() = - super.appWindowBecomesTopWindow() + override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow() companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( - supportedNavigationModes = - listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY), + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY), supportedRotations = listOf(Surface.ROTATION_0) ) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt index face7da6f623..4fd251aefffc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt @@ -27,9 +27,7 @@ import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.traces.common.ComponentNameMatcher import org.junit.Test -/** - * Base class for app launch tests - */ +/** Base class for app launch tests */ abstract class OpenAppTransition(testSpec: FlickerTestParameter) : BaseTest(testSpec) { protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation) @@ -40,14 +38,12 @@ abstract class OpenAppTransition(testSpec: FlickerTestParameter) : BaseTest(test device.wakeUpAndGoToHomeScreen() this.setRotation(testSpec.startRotation) } - teardown { - testApp.exit(wmHelper) - } + teardown { testApp.exit(wmHelper) } } /** - * Checks that the [testApp] layer doesn't exist or is invisible at the start of the - * transition, but is created and/or becomes visible during the transition. + * Checks that the [testApp] layer doesn't exist or is invisible at the start of the transition, + * but is created and/or becomes visible during the transition. */ @Presubmit @Test @@ -85,12 +81,10 @@ abstract class OpenAppTransition(testSpec: FlickerTestParameter) : BaseTest(test * Checks that the [testApp] window doesn't exist at the start of the transition, that it is * created (invisible - optional) and becomes visible during the transition * - * The `isAppWindowInvisible` step is optional because we log once per frame, upon logging, - * the window may be visible or not depending on what was processed until that moment. + * The `isAppWindowInvisible` step is optional because we log once per frame, upon logging, the + * window may be visible or not depending on what was processed until that moment. */ - @Presubmit - @Test - open fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart() + @Presubmit @Test open fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart() protected fun appWindowBecomesVisible_coldStart() { testSpec.assertWm { @@ -125,9 +119,7 @@ abstract class OpenAppTransition(testSpec: FlickerTestParameter) : BaseTest(test this.isAppWindowNotOnTop(testApp) .then() .isAppWindowOnTop( - testApp - .or(ComponentNameMatcher.SNAPSHOT) - .or(ComponentNameMatcher.SPLASH_SCREEN) + testApp.or(ComponentNameMatcher.SNAPSHOT).or(ComponentNameMatcher.SPLASH_SCREEN) ) } } @@ -139,8 +131,6 @@ abstract class OpenAppTransition(testSpec: FlickerTestParameter) : BaseTest(test @Presubmit @Test open fun appWindowIsTopWindowAtEnd() { - testSpec.assertWmEnd { - this.isAppWindowOnTop(testApp) - } + testSpec.assertWmEnd { this.isAppWindowOnTop(testApp) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index f7fb9e339e59..03741c8f968c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -37,17 +37,20 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:OpenAppWarmTest` * * Actions: + * ``` * Launch [testApp] * Press home * Relaunch an app [testApp] and wait animation to complete (only this action is traced) - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [OpenAppTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @FlickerServiceCompatible @@ -56,9 +59,7 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppFromLauncherTransition(testSpec) { - /** - * Defines the transition used to run the test - */ + /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit get() = { super.transition(this) @@ -66,17 +67,11 @@ open class OpenAppWarmTest(testSpec: FlickerTestParameter) : tapl.setExpectedRotationCheckEnabled(false) testApp.launchViaIntent(wmHelper) tapl.goHome() - wmHelper.StateSyncBuilder() - .withHomeActivityVisible() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() this.setRotation(testSpec.startRotation) } - teardown { - testApp.exit(wmHelper) - } - transitions { - testApp.launchViaIntent(wmHelper) - } + teardown { testApp.exit(wmHelper) } + transitions { testApp.launchViaIntent(wmHelper) } } /** {@inheritDoc} */ @@ -85,9 +80,7 @@ open class OpenAppWarmTest(testSpec: FlickerTestParameter) : override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ - @Presubmit - @Test - override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() + @Presubmit @Test override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() /** {@inheritDoc} */ @Presubmit @@ -103,14 +96,13 @@ open class OpenAppWarmTest(testSpec: FlickerTestParameter) : /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt index 18cc55b80198..bc2fe469a010 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt @@ -49,7 +49,9 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:OverrideTaskTransitionTest` * * Actions: + * ``` * Launches SimpleActivity with alpha_2000ms animation + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -70,16 +72,17 @@ class OverrideTaskTransitionTest(val testSpec: FlickerTestParameter) { } transitions { instrumentation.context.startActivity( - testApp.openAppIntent, createCustomTaskAnimation()) - wmHelper.StateSyncBuilder() - .add(WindowManagerConditionsFactory.isWMStateComplete()) - .withAppTransitionIdle() - .withWindowSurfaceAppeared(testApp) - .waitForAndVerify() - } - teardown { - testApp.exit() + testApp.openAppIntent, + createCustomTaskAnimation() + ) + wmHelper + .StateSyncBuilder() + .add(WindowManagerConditionsFactory.isWMStateComplete()) + .withAppTransitionIdle() + .withWindowSurfaceAppeared(testApp) + .waitForAndVerify() } + teardown { testApp.exit() } } } @@ -98,16 +101,22 @@ class OverrideTaskTransitionTest(val testSpec: FlickerTestParameter) { } private fun createCustomTaskAnimation(): Bundle { - return android.app.ActivityOptions.makeCustomTaskAnimation(instrumentation.context, - R.anim.show_2000ms, 0, Handler.getMain(), null, null).toBundle() + return android.app.ActivityOptions.makeCustomTaskAnimation( + instrumentation.context, + R.anim.show_2000ms, + 0, + Handler.getMain(), + null, + null + ) + .toBundle() } companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt index a43833014308..06486ca85292 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt @@ -45,9 +45,11 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:ActivitiesTransitionTest` * * Actions: + * ``` * Launch the NewTaskLauncherApp [mTestApp] * Open a new task (SimpleActivity) from the NewTaskLauncherApp [mTestApp] * Go back to the NewTaskLauncherApp [mTestApp] + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -61,36 +63,28 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit = { - setup { - testApp.launchViaIntent(wmHelper) - } - teardown { - testApp.exit(wmHelper) - } + setup { testApp.launchViaIntent(wmHelper) } + teardown { testApp.exit(wmHelper) } transitions { testApp.openNewTask(device, wmHelper) tapl.pressBack() - wmHelper.StateSyncBuilder() - .withAppTransitionIdle() - .waitForAndVerify() + wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() } } /** - * Checks that the [wallpaper] window is never visible when performing task transitions. - * A solid color background should be shown instead. + * Checks that the [wallpaper] window is never visible when performing task transitions. A solid + * color background should be shown instead. */ @Postsubmit @Test fun wallpaperWindowIsNeverVisible() { - testSpec.assertWm { - this.isNonAppWindowInvisible(wallpaper) - } + testSpec.assertWm { this.isNonAppWindowInvisible(wallpaper) } } /** - * Checks that the [wallpaper] layer is never visible when performing task transitions. - * A solid color background should be shown instead. + * Checks that the [wallpaper] layer is never visible when performing task transitions. A solid + * color background should be shown instead. */ @Postsubmit @Test @@ -103,33 +97,25 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { /** * Check that the [ComponentNameMatcher.LAUNCHER] window is never visible when performing task - * transitions. - * A solid color background should be shown above it. + * transitions. A solid color background should be shown above it. */ @Postsubmit @Test fun launcherWindowIsNeverVisible() { - testSpec.assertWm { - this.isAppWindowInvisible(ComponentNameMatcher.LAUNCHER) - } + testSpec.assertWm { this.isAppWindowInvisible(ComponentNameMatcher.LAUNCHER) } } /** * Checks that the [ComponentNameMatcher.LAUNCHER] layer is never visible when performing task - * transitions. - * A solid color background should be shown above it. + * transitions. A solid color background should be shown above it. */ @Postsubmit @Test fun launcherLayerIsNeverVisible() { - testSpec.assertLayers { - this.isInvisible(ComponentNameMatcher.LAUNCHER) - } + testSpec.assertLayers { this.isInvisible(ComponentNameMatcher.LAUNCHER) } } - /** - * Checks that a color background is visible while the task transition is occurring. - */ + /** Checks that a color background is visible while the task transition is occurring. */ @Postsubmit @Test fun colorLayerIsVisibleDuringTransition() { @@ -138,8 +124,8 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { testSpec.assertLayers { this.invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") { - it.visibleRegion(LAUNCH_NEW_TASK_ACTIVITY).coversExactly(displayBounds) - } + it.visibleRegion(LAUNCH_NEW_TASK_ACTIVITY).coversExactly(displayBounds) + } .isInvisible(bgColorLayer) .then() // Transitioning @@ -163,8 +149,8 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { } /** - * Checks that we start with the LaunchNewTask activity on top and then open up - * the SimpleActivity and then go back to the LaunchNewTask activity. + * Checks that we start with the LaunchNewTask activity on top and then open up the + * SimpleActivity and then go back to the LaunchNewTask activity. */ @Postsubmit @Test @@ -183,9 +169,7 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { } /** {@inheritDoc} */ - @Postsubmit - @Test - override fun entireScreenCovered() = super.entireScreenCovered() + @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() /** {@inheritDoc} */ @Postsubmit @@ -206,8 +190,7 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { /** {@inheritDoc} */ @Postsubmit @Test - override fun statusBarLayerPositionAtStartAndEnd() = - super.statusBarLayerPositionAtStartAndEnd() + override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd() /** {@inheritDoc} */ @Postsubmit @@ -255,8 +238,7 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt index 87ae8435deab..3f1a4180409f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt @@ -45,18 +45,17 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest` * * Actions: + * ``` * Launch an app [testApp1] * Launch another app [testApp2] * Swipe right from the bottom of the screen to quick switch back to the first app [testApp1] - * + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchBetweenTwoAppsBackTest( - testSpec: FlickerTestParameter -) : BaseTest(testSpec) { +open class QuickSwitchBetweenTwoAppsBackTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) { private val testApp1 = SimpleAppHelper(instrumentation) private val testApp2 = NonResizeableAppHelper(instrumentation) @@ -71,12 +70,13 @@ open class QuickSwitchBetweenTwoAppsBackTest( tapl.setExpectedRotation(testSpec.startRotation) testApp1.launchViaIntent(wmHelper) testApp2.launchViaIntent(wmHelper) - startDisplayBounds = wmHelper.currentState.layerState - .physicalDisplayBounds ?: error("Display not found") + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found") } transitions { tapl.launchedAppState.quickSwitchToPreviousApp() - wmHelper.StateSyncBuilder() + wmHelper + .StateSyncBuilder() .withFullScreenApp(testApp1) .withNavOrTaskBarVisible() .withStatusBarVisible() @@ -96,9 +96,7 @@ open class QuickSwitchBetweenTwoAppsBackTest( @Presubmit @Test open fun startsWithApp2WindowsCoverFullScreen() { - testSpec.assertWmStart { - this.visibleRegion(testApp2).coversExactly(startDisplayBounds) - } + testSpec.assertWmStart { this.visibleRegion(testApp2).coversExactly(startDisplayBounds) } } /** @@ -113,15 +111,11 @@ open class QuickSwitchBetweenTwoAppsBackTest( } } - /** - * Checks that the transition starts with [testApp2] being the top window. - */ + /** Checks that the transition starts with [testApp2] being the top window. */ @Presubmit @Test open fun startsWithApp2WindowBeingOnTop() { - testSpec.assertWmStart { - this.isAppWindowOnTop(testApp2) - } + testSpec.assertWmStart { this.isAppWindowOnTop(testApp2) } } /** @@ -131,21 +125,17 @@ open class QuickSwitchBetweenTwoAppsBackTest( @Presubmit @Test open fun endsWithApp1WindowsCoveringFullScreen() { - testSpec.assertWmEnd { - this.visibleRegion(testApp1).coversExactly(startDisplayBounds) - } + testSpec.assertWmEnd { this.visibleRegion(testApp1).coversExactly(startDisplayBounds) } } /** - * Checks that [testApp1] layers fill the entire screen (i.e. is "fullscreen") at the end of - * the transition once we have fully quick switched from [testApp2] back to the [testApp1]. + * Checks that [testApp1] layers fill the entire screen (i.e. is "fullscreen") at the end of the + * transition once we have fully quick switched from [testApp2] back to the [testApp1]. */ @Presubmit @Test fun endsWithApp1LayersCoveringFullScreen() { - testSpec.assertLayersEnd { - this.visibleRegion(testApp1).coversExactly(startDisplayBounds) - } + testSpec.assertLayersEnd { this.visibleRegion(testApp1).coversExactly(startDisplayBounds) } } /** @@ -155,14 +145,12 @@ open class QuickSwitchBetweenTwoAppsBackTest( @Presubmit @Test open fun endsWithApp1BeingOnTop() { - testSpec.assertWmEnd { - this.isAppWindowOnTop(testApp1) - } + testSpec.assertWmEnd { this.isAppWindowOnTop(testApp1) } } /** - * Checks that [testApp1]'s window starts off invisible and becomes visible at some point - * before the end of the transition and then stays visible until the end of the transition. + * Checks that [testApp1]'s window starts off invisible and becomes visible at some point before + * the end of the transition and then stays visible until the end of the transition. */ @Presubmit @Test @@ -177,45 +165,35 @@ open class QuickSwitchBetweenTwoAppsBackTest( } /** - * Checks that [testApp1]'s layer starts off invisible and becomes visible at some point - * before the end of the transition and then stays visible until the end of the transition. + * Checks that [testApp1]'s layer starts off invisible and becomes visible at some point before + * the end of the transition and then stays visible until the end of the transition. */ @Presubmit @Test open fun app1LayerBecomesAndStaysVisible() { - testSpec.assertLayers { - this.isInvisible(testApp1) - .then() - .isVisible(testApp1) - } + testSpec.assertLayers { this.isInvisible(testApp1).then().isVisible(testApp1) } } /** - * Checks that [testApp2]'s window starts off visible and becomes invisible at some point - * before the end of the transition and then stays invisible until the end of the transition. + * Checks that [testApp2]'s window starts off visible and becomes invisible at some point before + * the end of the transition and then stays invisible until the end of the transition. */ @Presubmit @Test open fun app2WindowBecomesAndStaysInvisible() { testSpec.assertWm { - this.isAppWindowVisible(testApp2) - .then() - .isAppWindowInvisible(testApp2) + this.isAppWindowVisible(testApp2).then().isAppWindowInvisible(testApp2) } } /** - * Checks that [testApp2]'s layer starts off visible and becomes invisible at some point - * before the end of the transition and then stays invisible until the end of the transition. + * Checks that [testApp2]'s layer starts off visible and becomes invisible at some point before + * the end of the transition and then stays invisible until the end of the transition. */ @Presubmit @Test open fun app2LayerBecomesAndStaysInvisible() { - testSpec.assertLayers { - this.isVisible(testApp2) - .then() - .isInvisible(testApp2) - } + testSpec.assertLayers { this.isVisible(testApp2).then().isInvisible(testApp2) } } /** @@ -274,9 +252,8 @@ open class QuickSwitchBetweenTwoAppsBackTest( fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ), + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY), supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90) ) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt index 21652d922feb..7a1350e5bbfa 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt @@ -38,18 +38,18 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest` * * Actions: + * ``` * Launch an app [testApp1] * Launch another app [testApp2] * Swipe right from the bottom of the screen to quick switch back to the first app [testApp1] - * + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchBetweenTwoAppsBackTest_ShellTransit( - testSpec: FlickerTestParameter -) : QuickSwitchBetweenTwoAppsBackTest(testSpec) { +open class QuickSwitchBetweenTwoAppsBackTest_ShellTransit(testSpec: FlickerTestParameter) : + QuickSwitchBetweenTwoAppsBackTest(testSpec) { @Before override fun before() { Assume.assumeTrue(isShellTransitionsEnabled) @@ -61,8 +61,8 @@ open class QuickSwitchBetweenTwoAppsBackTest_ShellTransit( override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** - * Checks that [ComponentMatcher.NAV_BAR] window is visible and above the app windows at the start - * and end of the WM trace + * Checks that [ComponentMatcher.NAV_BAR] window is visible and above the app windows at the + * start and end of the WM trace */ @Presubmit @Test diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt index 64259105411a..0a21044f6dda 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt @@ -45,18 +45,19 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsForwardTest` * * Actions: + * ``` * Launch an app [testApp1] * Launch another app [testApp2] * Swipe right from the bottom of the screen to quick switch back to the first app [testApp1] * Swipe left from the bottom of the screen to quick switch forward to the second app [testApp2] + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchBetweenTwoAppsForwardTest( - testSpec: FlickerTestParameter -) : BaseTest(testSpec) { +open class QuickSwitchBetweenTwoAppsForwardTest(testSpec: FlickerTestParameter) : + BaseTest(testSpec) { private val testApp1 = SimpleAppHelper(instrumentation) private val testApp2 = NonResizeableAppHelper(instrumentation) @@ -68,22 +69,24 @@ open class QuickSwitchBetweenTwoAppsForwardTest( /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit = { setup { - tapl.setExpectedRotation(testSpec.startRotation) + tapl.setExpectedRotation(testSpec.startRotation) - testApp1.launchViaIntent(wmHelper) - testApp2.launchViaIntent(wmHelper) - tapl.launchedAppState.quickSwitchToPreviousApp() - wmHelper.StateSyncBuilder() - .withFullScreenApp(testApp1) - .withNavOrTaskBarVisible() - .withStatusBarVisible() - .waitForAndVerify() - startDisplayBounds = wmHelper.currentState.layerState - .physicalDisplayBounds ?: error("Display not found") + testApp1.launchViaIntent(wmHelper) + testApp2.launchViaIntent(wmHelper) + tapl.launchedAppState.quickSwitchToPreviousApp() + wmHelper + .StateSyncBuilder() + .withFullScreenApp(testApp1) + .withNavOrTaskBarVisible() + .withStatusBarVisible() + .waitForAndVerify() + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found") } transitions { tapl.launchedAppState.quickSwitchToPreviousAppSwipeLeft() - wmHelper.StateSyncBuilder() + wmHelper + .StateSyncBuilder() .withFullScreenApp(testApp2) .withNavOrTaskBarVisible() .withStatusBarVisible() @@ -121,15 +124,11 @@ open class QuickSwitchBetweenTwoAppsForwardTest( } } - /** - * Checks that the transition starts with [testApp1] being the top window. - */ + /** Checks that the transition starts with [testApp1] being the top window. */ @Presubmit @Test open fun startsWithApp1WindowBeingOnTop() { - testSpec.assertWmStart { - this.isAppWindowOnTop(testApp1) - } + testSpec.assertWmStart { this.isAppWindowOnTop(testApp1) } } /** @@ -139,9 +138,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest( @Presubmit @Test open fun endsWithApp2WindowsCoveringFullScreen() { - testSpec.assertWmEnd { - this.visibleRegion(testApp2).coversExactly(startDisplayBounds) - } + testSpec.assertWmEnd { this.visibleRegion(testApp2).coversExactly(startDisplayBounds) } } /** @@ -164,9 +161,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest( @Presubmit @Test open fun endsWithApp2BeingOnTop() { - testSpec.assertWmEnd { - this.isAppWindowOnTop(testApp2) - } + testSpec.assertWmEnd { this.isAppWindowOnTop(testApp2) } } /** @@ -178,10 +173,10 @@ open class QuickSwitchBetweenTwoAppsForwardTest( open fun app2WindowBecomesAndStaysVisible() { testSpec.assertWm { this.isAppWindowInvisible(testApp2) - .then() - .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) - .then() - .isAppWindowVisible(testApp2) + .then() + .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) + .then() + .isAppWindowVisible(testApp2) } } @@ -192,11 +187,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest( @Presubmit @Test open fun app2LayerBecomesAndStaysVisible() { - testSpec.assertLayers { - this.isInvisible(testApp2) - .then() - .isVisible(testApp2) - } + testSpec.assertLayers { this.isInvisible(testApp2).then().isVisible(testApp2) } } /** @@ -207,9 +198,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest( @Test open fun app1WindowBecomesAndStaysInvisible() { testSpec.assertWm { - this.isAppWindowVisible(testApp1) - .then() - .isAppWindowInvisible(testApp1) + this.isAppWindowVisible(testApp1).then().isAppWindowInvisible(testApp1) } } @@ -220,11 +209,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest( @Presubmit @Test open fun app1LayerBecomesAndStaysInvisible() { - testSpec.assertLayers { - this.isVisible(testApp1) - .then() - .isInvisible(testApp1) - } + testSpec.assertLayers { this.isVisible(testApp1).then().isInvisible(testApp1) } } /** @@ -237,12 +222,12 @@ open class QuickSwitchBetweenTwoAppsForwardTest( open fun app2WindowIsVisibleOnceApp1WindowIsInvisible() { testSpec.assertWm { this.isAppWindowVisible(testApp1) - .then() - .isAppWindowVisible(ComponentNameMatcher.LAUNCHER, isOptional = true) - .then() - .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) - .then() - .isAppWindowVisible(testApp2) + .then() + .isAppWindowVisible(ComponentNameMatcher.LAUNCHER, isOptional = true) + .then() + .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) + .then() + .isAppWindowVisible(testApp2) } } @@ -256,12 +241,12 @@ open class QuickSwitchBetweenTwoAppsForwardTest( open fun app2LayerIsVisibleOnceApp1LayerIsInvisible() { testSpec.assertLayers { this.isVisible(testApp1) - .then() - .isVisible(ComponentNameMatcher.LAUNCHER, isOptional = true) - .then() - .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) - .then() - .isVisible(testApp2) + .then() + .isVisible(ComponentNameMatcher.LAUNCHER, isOptional = true) + .then() + .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) + .then() + .isVisible(testApp2) } } @@ -281,12 +266,11 @@ open class QuickSwitchBetweenTwoAppsForwardTest( @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests( - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ), - supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90) - ) + .getConfigNonRotationTests( + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY), + supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90) + ) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt index 2f72fad23942..03647c9801c7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt @@ -38,18 +38,19 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsForwardTest` * * Actions: + * ``` * Launch an app [testApp1] * Launch another app [testApp2] * Swipe right from the bottom of the screen to quick switch back to the first app [testApp1] * Swipe left from the bottom of the screen to quick switch forward to the second app [testApp2] + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit( - testSpec: FlickerTestParameter -) : QuickSwitchBetweenTwoAppsForwardTest(testSpec) { +open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit(testSpec: FlickerTestParameter) : + QuickSwitchBetweenTwoAppsForwardTest(testSpec) { @Before override fun before() { Assume.assumeTrue(isShellTransitionsEnabled) @@ -61,8 +62,8 @@ open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit( override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** - * Checks that [ComponentMatcher.NAV_BAR] window is visible and above the app windows at the start - * and end of the WM trace + * Checks that [ComponentMatcher.NAV_BAR] window is visible and above the app windows at the + * start and end of the WM trace */ @Presubmit @Test diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt index 702c2cf0ace6..3cb985a925b1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt @@ -45,10 +45,11 @@ import org.junit.runners.Parameterized * To run this test: `atest FlickerTests:QuickSwitchFromLauncherTest` * * Actions: + * ``` * Launch an app * Navigate home to show launcher * Swipe right from the bottom of the screen to quick switch back to the app - * + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @@ -66,25 +67,25 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes testApp.launchViaIntent(wmHelper) tapl.goHome() - wmHelper.StateSyncBuilder() + wmHelper + .StateSyncBuilder() .withHomeActivityVisible() .withWindowSurfaceDisappeared(testApp) .waitForAndVerify() - startDisplayBounds = wmHelper.currentState.layerState - .physicalDisplayBounds ?: error("Display not found") + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found") } transitions { tapl.workspace.quickSwitchToPreviousApp() - wmHelper.StateSyncBuilder() + wmHelper + .StateSyncBuilder() .withFullScreenApp(testApp) .withNavOrTaskBarVisible() .withStatusBarVisible() .waitForAndVerify() } - teardown { - testApp.exit(wmHelper) - } + teardown { testApp.exit(wmHelper) } } /** @@ -94,9 +95,7 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes @Presubmit @Test fun endsWithAppWindowsCoveringFullScreen() { - testSpec.assertWmEnd { - this.visibleRegion(testApp).coversExactly(startDisplayBounds) - } + testSpec.assertWmEnd { this.visibleRegion(testApp).coversExactly(startDisplayBounds) } } /** @@ -106,9 +105,7 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes @Presubmit @Test fun endsWithAppLayersCoveringFullScreen() { - testSpec.assertLayersEnd { - this.visibleRegion(testApp).coversExactly(startDisplayBounds) - } + testSpec.assertLayersEnd { this.visibleRegion(testApp).coversExactly(startDisplayBounds) } } /** @@ -118,20 +115,14 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes @Presubmit @Test fun endsWithAppBeingOnTop() { - testSpec.assertWmEnd { - this.isAppWindowOnTop(testApp) - } + testSpec.assertWmEnd { this.isAppWindowOnTop(testApp) } } - /** - * Checks that the transition starts with the home activity being tagged as visible. - */ + /** Checks that the transition starts with the home activity being tagged as visible. */ @Presubmit @Test fun startsWithHomeActivityFlaggedVisible() { - testSpec.assertWmStart { - this.isHomeActivityVisible() - } + testSpec.assertWmStart { this.isHomeActivityVisible() } } /** @@ -164,9 +155,7 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes @Presubmit @Test fun startsWithLauncherBeingOnTop() { - testSpec.assertWmStart { - this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER) - } + testSpec.assertWmStart { this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER) } } /** @@ -176,9 +165,7 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes @Presubmit @Test fun endsWithHomeActivityFlaggedInvisible() { - testSpec.assertWmEnd { - this.isHomeActivityInvisible() - } + testSpec.assertWmEnd { this.isHomeActivityInvisible() } } /** @@ -188,11 +175,7 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes @Presubmit @Test fun appWindowBecomesAndStaysVisible() { - testSpec.assertWm { - this.isAppWindowInvisible(testApp) - .then() - .isAppWindowVisible(testApp) - } + testSpec.assertWm { this.isAppWindowInvisible(testApp).then().isAppWindowVisible(testApp) } } /** @@ -202,63 +185,59 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes @Presubmit @Test fun appLayerBecomesAndStaysVisible() { - testSpec.assertLayers { - this.isInvisible(testApp) - .then() - .isVisible(testApp) - } + testSpec.assertLayers { this.isInvisible(testApp).then().isVisible(testApp) } } /** * Checks that the [ComponentMatcher.LAUNCHER] window starts off visible and becomes invisible - * at some point before - * the end of the transition and then stays invisible until the end of the transition. + * at some point before the end of the transition and then stays invisible until the end of the + * transition. */ @Presubmit @Test fun launcherWindowBecomesAndStaysInvisible() { testSpec.assertWm { this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER) - .then() - .isAppWindowNotOnTop(ComponentNameMatcher.LAUNCHER) + .then() + .isAppWindowNotOnTop(ComponentNameMatcher.LAUNCHER) } } /** - * Checks that the [ComponentMatcher.LAUNCHER] layer starts off visible and becomes invisible - * at some point before - * the end of the transition and then stays invisible until the end of the transition. + * Checks that the [ComponentMatcher.LAUNCHER] layer starts off visible and becomes invisible at + * some point before the end of the transition and then stays invisible until the end of the + * transition. */ @Presubmit @Test fun launcherLayerBecomesAndStaysInvisible() { testSpec.assertLayers { this.isVisible(ComponentNameMatcher.LAUNCHER) - .then() - .isInvisible(ComponentNameMatcher.LAUNCHER) + .then() + .isInvisible(ComponentNameMatcher.LAUNCHER) } } /** * Checks that the [ComponentMatcher.LAUNCHER] window is visible at least until the app window - * is visible. Ensures - * that at any point, either the launcher or [testApp] windows are at least partially visible. + * is visible. Ensures that at any point, either the launcher or [testApp] windows are at least + * partially visible. */ @Presubmit @Test fun appWindowIsVisibleOnceLauncherWindowIsInvisible() { testSpec.assertWm { this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER) - .then() - .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) - .then() - .isAppWindowVisible(testApp) + .then() + .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) + .then() + .isAppWindowVisible(testApp) } } /** - * Checks that the [ComponentMatcher.LAUNCHER] layer is visible at least until the app layer - * is visible. Ensures that at any point, either the launcher or [testApp] layers are at least + * Checks that the [ComponentMatcher.LAUNCHER] layer is visible at least until the app layer is + * visible. Ensures that at any point, either the launcher or [testApp] layers are at least * partially visible. */ @Presubmit @@ -266,10 +245,10 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes fun appLayerIsVisibleOnceLauncherLayerIsInvisible() { testSpec.assertLayers { this.isVisible(ComponentNameMatcher.LAUNCHER) - .then() - .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) - .then() - .isVisible(testApp) + .then() + .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true) + .then() + .isVisible(testApp) } } @@ -284,8 +263,8 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() /** - * Checks that [ComponentMatcher.NAV_BAR] window is visible and above the app windows at the start - * and end of the WM trace + * Checks that [ComponentMatcher.NAV_BAR] window is visible and above the app windows at the + * start and end of the WM trace */ @Presubmit @Test @@ -316,13 +295,12 @@ class QuickSwitchFromLauncherTest(testSpec: FlickerTestParameter) : BaseTest(tes @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests( - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY - ), - // TODO: Test with 90 rotation - supportedRotations = listOf(Surface.ROTATION_0) - ) + .getConfigNonRotationTests( + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY), + // TODO: Test with 90 rotation + supportedRotations = listOf(Surface.ROTATION_0) + ) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt index 3ca58f2b70c0..1973ec0a98a8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt @@ -35,51 +35,54 @@ import org.junit.runners.Parameterized * Test opening an app and cycling through app rotations * * Currently runs: + * ``` * 0 -> 90 degrees * 90 -> 0 degrees - * + * ``` * Actions: + * ``` * Launch an app (via intent) * Set initial device orientation * Start tracing * Change device orientation * Stop tracing - * + * ``` * To run this test: `atest FlickerTests:ChangeAppRotationTest` * * To run only the presubmit assertions add: `-- + * ``` * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit` - * + * ``` * To run only the postsubmit assertions add: `-- + * ``` * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit` - * + * ``` * To run only the flaky assertions add: `-- + * ``` * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest` - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [RotationTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ChangeAppRotationTest( - testSpec: FlickerTestParameter -) : RotationTransition(testSpec) { +class ChangeAppRotationTest(testSpec: FlickerTestParameter) : RotationTransition(testSpec) { override val testApp = SimpleAppHelper(instrumentation) override val transition: FlickerBuilder.() -> Unit get() = { super.transition(this) - setup { - testApp.launchViaIntent(wmHelper) - } + setup { testApp.launchViaIntent(wmHelper) } } /** @@ -89,14 +92,12 @@ class ChangeAppRotationTest( @Presubmit @Test fun focusChanges() { - testSpec.assertEventLog { - this.focusChanges(testApp.`package`) - } + testSpec.assertEventLog { this.focusChanges(testApp.`package`) } } /** - * Checks that the [ComponentMatcher.ROTATION] layer appears during the transition, - * doesn't flicker, and disappears before the transition is complete + * Checks that the [ComponentMatcher.ROTATION] layer appears during the transition, doesn't + * flicker, and disappears before the transition is complete */ fun rotationLayerAppearsAndVanishesAssertion() { testSpec.assertLayers { @@ -110,8 +111,8 @@ class ChangeAppRotationTest( } /** - * Checks that the [ComponentMatcher.ROTATION] layer appears during the transition, - * doesn't flicker, and disappears before the transition is complete + * Checks that the [ComponentMatcher.ROTATION] layer appears during the transition, doesn't + * flicker, and disappears before the transition is complete */ @Presubmit @Test @@ -122,21 +123,19 @@ class ChangeAppRotationTest( /** {@inheritDoc} */ @FlakyTest(bugId = 206753786) @Test - override fun navBarLayerPositionAtStartAndEnd() = - super.navBarLayerPositionAtStartAndEnd() + override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() companion object { /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigRotationTests() + return FlickerTestParameterFactory.getInstance().getConfigRotationTests() } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index afe2ea678e39..4faeb246037e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt @@ -25,23 +25,15 @@ import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.traces.common.ComponentNameMatcher import org.junit.Test -/** - * Base class for app rotation tests - */ +/** Base class for app rotation tests */ abstract class RotationTransition(testSpec: FlickerTestParameter) : BaseTest(testSpec) { protected abstract val testApp: StandardAppHelper /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit = { - setup { - this.setRotation(testSpec.startRotation) - } - teardown { - testApp.exit(wmHelper) - } - transitions { - this.setRotation(testSpec.endRotation) - } + setup { this.setRotation(testSpec.startRotation) } + teardown { testApp.exit(wmHelper) } + transitions { this.setRotation(testSpec.endRotation) } } /** {@inheritDoc} */ @@ -50,18 +42,17 @@ abstract class RotationTransition(testSpec: FlickerTestParameter) : BaseTest(tes override fun visibleLayersShownMoreThanOneConsecutiveEntry() { testSpec.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry( - ignoreLayers = listOf( - ComponentNameMatcher.SPLASH_SCREEN, - ComponentNameMatcher.SNAPSHOT, - ComponentNameMatcher("", "SecondaryHomeHandle") - ) + ignoreLayers = + listOf( + ComponentNameMatcher.SPLASH_SCREEN, + ComponentNameMatcher.SNAPSHOT, + ComponentNameMatcher("", "SecondaryHomeHandle") + ) ) } } - /** - * Checks that [testApp] layer covers the entire screen at the start of the transition - */ + /** Checks that [testApp] layer covers the entire screen at the start of the transition */ @Presubmit @Test open fun appLayerRotates_StartingPos() { @@ -72,9 +63,7 @@ abstract class RotationTransition(testSpec: FlickerTestParameter) : BaseTest(tes } } - /** - * Checks that [testApp] layer covers the entire screen at the end of the transition - */ + /** Checks that [testApp] layer covers the entire screen at the end of the transition */ @Presubmit @Test open fun appLayerRotates_EndingPos() { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt index 11e07ebff19e..a08db29fe7a4 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt @@ -38,46 +38,51 @@ import org.junit.runners.Parameterized * Test opening an app and cycling through app rotations using seamless rotations * * Currently runs: + * ``` * 0 -> 90 degrees * 0 -> 90 degrees (with starved UI thread) * 90 -> 0 degrees * 90 -> 0 degrees (with starved UI thread) - * + * ``` * Actions: + * ``` * Launch an app in fullscreen and supporting seamless rotation (via intent) * Set initial device orientation * Start tracing * Change device orientation * Stop tracing - * + * ``` * To run this test: `atest FlickerTests:SeamlessAppRotationTest` * * To run only the presubmit assertions add: `-- + * ``` * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit` - * + * ``` * To run only the postsubmit assertions add: `-- + * ``` * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit` - * + * ``` * To run only the flaky assertions add: `-- + * ``` * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest` - * + * ``` * Notes: + * ``` * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [RotationTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup + * ``` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class SeamlessAppRotationTest( - testSpec: FlickerTestParameter -) : RotationTransition(testSpec) { +open class SeamlessAppRotationTest(testSpec: FlickerTestParameter) : RotationTransition(testSpec) { override val testApp = SeamlessRotationAppHelper(instrumentation) /** {@inheritDoc} */ @@ -87,17 +92,16 @@ open class SeamlessAppRotationTest( setup { testApp.launchViaIntent( wmHelper, - stringExtras = mapOf( - ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD - to testSpec.starveUiThread.toString() - ) + stringExtras = + mapOf( + ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD to + testSpec.starveUiThread.toString() + ) ) } } - /** - * Checks that [testApp] window is always in full screen - */ + /** Checks that [testApp] window is always in full screen */ @Presubmit @Test fun appWindowFullScreen() { @@ -105,16 +109,15 @@ open class SeamlessAppRotationTest( this.invoke("isFullScreen") { val appWindow = it.windowState(testApp.`package`) val flags = appWindow.windowState?.attributes?.flags ?: 0 - appWindow.verify("isFullScreen") + appWindow + .verify("isFullScreen") .that(flags.and(WindowManager.LayoutParams.FLAG_FULLSCREEN)) .isGreaterThan(0) } } } - /** - * Checks that [testApp] window is always with seamless rotation - */ + /** Checks that [testApp] window is always with seamless rotation */ @Presubmit @Test fun appWindowSeamlessRotation() { @@ -122,38 +125,33 @@ open class SeamlessAppRotationTest( this.invoke("isRotationSeamless") { val appWindow = it.windowState(testApp.`package`) val rotationAnimation = appWindow.windowState?.attributes?.rotationAnimation ?: 0 - appWindow.verify("isRotationSeamless") + appWindow + .verify("isRotationSeamless") .that( - rotationAnimation - .and(WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS) + rotationAnimation.and( + WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS + ) ) .isGreaterThan(0) } } } - /** - * Checks that [testApp] window is always visible - */ + /** Checks that [testApp] window is always visible */ @Presubmit @Test fun appLayerAlwaysVisible() { - testSpec.assertLayers { - isVisible(testApp) - } + testSpec.assertLayers { isVisible(testApp) } } - /** - * Checks that [testApp] layer covers the entire screen during the whole transition - */ + /** Checks that [testApp] layer covers the entire screen during the whole transition */ @Presubmit @Test fun appLayerRotates() { testSpec.assertLayers { this.invoke("entireScreenCovered") { entry -> entry.entry.displays.map { display -> - entry.visibleRegion(testApp) - .coversExactly(display.layerStackSpace) + entry.visibleRegion(testApp).coversExactly(display.layerStackSpace) } } } @@ -162,20 +160,17 @@ open class SeamlessAppRotationTest( /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. App is full screen") - override fun statusBarLayerPositionAtStartAndEnd() { - } + override fun statusBarLayerPositionAtStartAndEnd() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. App is full screen") - override fun statusBarLayerIsVisibleAtStartAndEnd() { - } + override fun statusBarLayerIsVisibleAtStartAndEnd() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. App is full screen") - override fun statusBarWindowIsAlwaysVisible() { - } + override fun statusBarWindowIsAlwaysVisible() {} /** * Checks that the [ComponentNameMatcher.STATUS_BAR] window is invisible during the whole @@ -184,9 +179,7 @@ open class SeamlessAppRotationTest( @Presubmit @Test fun statusBarWindowIsAlwaysInvisible() { - testSpec.assertWm { - this.isAboveAppWindowInvisible(ComponentNameMatcher.STATUS_BAR) - } + testSpec.assertWm { this.isAboveAppWindowInvisible(ComponentNameMatcher.STATUS_BAR) } } /** @@ -196,20 +189,14 @@ open class SeamlessAppRotationTest( @Presubmit @Test fun statusBarLayerIsAlwaysInvisible() { - testSpec.assertLayers { - this.isInvisible(ComponentNameMatcher.STATUS_BAR) - } + testSpec.assertLayers { this.isInvisible(ComponentNameMatcher.STATUS_BAR) } } - /** - * Checks that the focus doesn't change during animation - */ + /** Checks that the focus doesn't change during animation */ @Presubmit @Test fun focusDoesNotChange() { - testSpec.assertEventLog { - this.focusDoesNotChange() - } + testSpec.assertEventLog { this.focusDoesNotChange() } } /** {@inheritDoc} */ @@ -219,15 +206,16 @@ open class SeamlessAppRotationTest( companion object { private val FlickerTestParameter.starveUiThread - get() = config.getOrDefault( - ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD, false) as Boolean + get() = + config.getOrDefault(ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD, false) + as Boolean private fun createConfig( sourceConfig: FlickerTestParameter, starveUiThread: Boolean ): FlickerTestParameter { - val newConfig = sourceConfig.config.toMutableMap() - .also { + val newConfig = + sourceConfig.config.toMutableMap().also { it[ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD] = starveUiThread } val nameExt = if (starveUiThread) "_BUSY_UI_THREAD" else "" @@ -235,27 +223,26 @@ open class SeamlessAppRotationTest( } /** - * Creates the test configurations for seamless rotation based on the default rotation - * tests from [FlickerTestParameterFactory.getConfigRotationTests], but adding an - * additional flag ([ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD]) to indicate - * if the app should starve the UI thread of not + * Creates the test configurations for seamless rotation based on the default rotation tests + * from [FlickerTestParameterFactory.getConfigRotationTests], but adding an additional flag + * ([ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD]) to indicate if the app should + * starve the UI thread of not */ @JvmStatic private fun getConfigurations(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigRotationTests() - .flatMap { sourceConfig -> - val defaultRun = createConfig(sourceConfig, starveUiThread = false) - val busyUiRun = createConfig(sourceConfig, starveUiThread = true) - listOf(defaultRun, busyUiRun) - } + return FlickerTestParameterFactory.getInstance().getConfigRotationTests().flatMap { + sourceConfig -> + val defaultRun = createConfig(sourceConfig, starveUiThread = false) + val busyUiRun = createConfig(sourceConfig, starveUiThread = true) + listOf(defaultRun, busyUiRun) + } } /** * Creates the test configurations. * - * See [FlickerTestParameterFactory.getConfigRotationTests] for configuring - * repetitions, screen orientation and navigation modes. + * See [FlickerTestParameterFactory.getConfigRotationTests] for configuring repetitions, + * screen orientation and navigation modes. */ @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt index 8aa3e2583071..4d69d26e46db 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt +++ b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt @@ -42,6 +42,8 @@ class AndroidFrameworkIssueRegistry : IssueRegistry() { SaferParcelChecker.ISSUE_UNSAFE_API_USAGE, PackageVisibilityDetector.ISSUE_PACKAGE_NAME_NO_PACKAGE_VISIBILITY_FILTERS, RegisterReceiverFlagDetector.ISSUE_RECEIVER_EXPORTED_FLAG, + PermissionMethodDetector.ISSUE_PERMISSION_METHOD_USAGE, + PermissionMethodDetector.ISSUE_CAN_BE_PERMISSION_METHOD, ) override val api: Int diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt b/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt index 82eb8ed8f621..3d5d01c9b7a0 100644 --- a/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt +++ b/tools/lint/checks/src/main/java/com/google/android/lint/Constants.kt @@ -34,3 +34,7 @@ val ENFORCE_PERMISSION_METHODS = listOf( Method(CLASS_ACTIVITY_MANAGER_SERVICE, "checkPermission"), Method(CLASS_ACTIVITY_MANAGER_INTERNAL, "enforceCallingPermission") ) + +const val ANNOTATION_PERMISSION_METHOD = "android.content.pm.PermissionMethod" +const val ANNOTATION_PERMISSION_NAME = "android.content.pm.PermissionName" +const val ANNOTATION_PERMISSION_RESULT = "android.content.pm.PackageManager.PermissionResult" diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/PermissionMethodDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/PermissionMethodDetector.kt new file mode 100644 index 000000000000..68a450d956a8 --- /dev/null +++ b/tools/lint/checks/src/main/java/com/google/android/lint/PermissionMethodDetector.kt @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.android.lint + +import com.android.tools.lint.client.api.UElementHandler +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.SourceCodeScanner +import com.android.tools.lint.detector.api.getUMethod +import com.intellij.psi.PsiType +import org.jetbrains.uast.UAnnotation +import org.jetbrains.uast.UBlockExpression +import org.jetbrains.uast.UCallExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UExpression +import org.jetbrains.uast.UIfExpression +import org.jetbrains.uast.UMethod +import org.jetbrains.uast.UQualifiedReferenceExpression +import org.jetbrains.uast.UReturnExpression +import org.jetbrains.uast.getContainingUMethod + +/** + * Stops incorrect usage of {@link PermissionMethod} + * TODO: add tests once re-enabled (b/240445172, b/247542171) + */ +class PermissionMethodDetector : Detector(), SourceCodeScanner { + + override fun getApplicableUastTypes(): List<Class<out UElement>> = + listOf(UAnnotation::class.java, UMethod::class.java) + + override fun createUastHandler(context: JavaContext): UElementHandler = + PermissionMethodHandler(context) + + private inner class PermissionMethodHandler(val context: JavaContext) : UElementHandler() { + override fun visitMethod(node: UMethod) { + if (hasPermissionMethodAnnotation(node)) return + if (onlyCallsPermissionMethod(node)) { + val location = context.getLocation(node.javaPsi.modifierList) + val fix = fix() + .annotate(ANNOTATION_PERMISSION_METHOD) + .range(location) + .autoFix() + .build() + + context.report( + ISSUE_CAN_BE_PERMISSION_METHOD, + location, + "Annotate method with @PermissionMethod", + fix + ) + } + } + + override fun visitAnnotation(node: UAnnotation) { + if (node.qualifiedName != ANNOTATION_PERMISSION_METHOD) return + val method = node.getContainingUMethod() ?: return + + if (!isPermissionMethodReturnType(method)) { + context.report( + ISSUE_PERMISSION_METHOD_USAGE, + context.getLocation(node), + """ + Methods annotated with `@PermissionMethod` should return `void`, \ + `boolean`, or `@PackageManager.PermissionResult int`." + """.trimIndent() + ) + } + + if (method.returnType == PsiType.INT && + method.annotations.none { it.hasQualifiedName(ANNOTATION_PERMISSION_RESULT) } + ) { + context.report( + ISSUE_PERMISSION_METHOD_USAGE, + context.getLocation(node), + """ + Methods annotated with `@PermissionMethod` that return `int` should \ + also be annotated with `@PackageManager.PermissionResult.`" + """.trimIndent() + ) + } + } + } + + companion object { + + private val EXPLANATION_PERMISSION_METHOD_USAGE = """ + `@PermissionMethod` should annotate methods that ONLY perform permission lookups. \ + Said methods should return `boolean`, `@PackageManager.PermissionResult int`, or return \ + `void` and potentially throw `SecurityException`. + """.trimIndent() + + @JvmField + val ISSUE_PERMISSION_METHOD_USAGE = Issue.create( + id = "PermissionMethodUsage", + briefDescription = "@PermissionMethod used incorrectly", + explanation = EXPLANATION_PERMISSION_METHOD_USAGE, + category = Category.CORRECTNESS, + priority = 5, + severity = Severity.ERROR, + implementation = Implementation( + PermissionMethodDetector::class.java, + Scope.JAVA_FILE_SCOPE + ), + enabledByDefault = true + ) + + private val EXPLANATION_CAN_BE_PERMISSION_METHOD = """ + Methods that only call other methods annotated with @PermissionMethod (and do NOTHING else) can themselves \ + be annotated with @PermissionMethod. For example: + ``` + void wrapperHelper() { + // Context.enforceCallingPermission is annotated with @PermissionMethod + context.enforceCallingPermission(SOME_PERMISSION) + } + ``` + """.trimIndent() + + @JvmField + val ISSUE_CAN_BE_PERMISSION_METHOD = Issue.create( + id = "CanBePermissionMethod", + briefDescription = "Method can be annotated with @PermissionMethod", + explanation = EXPLANATION_CAN_BE_PERMISSION_METHOD, + category = Category.SECURITY, + priority = 5, + severity = Severity.WARNING, + implementation = Implementation( + PermissionMethodDetector::class.java, + Scope.JAVA_FILE_SCOPE + ), + enabledByDefault = false + ) + + private fun hasPermissionMethodAnnotation(method: UMethod): Boolean = method.annotations + .any { + it.hasQualifiedName(ANNOTATION_PERMISSION_METHOD) + } + + private fun isPermissionMethodReturnType(method: UMethod): Boolean = + listOf(PsiType.VOID, PsiType.INT, PsiType.BOOLEAN).contains(method.returnType) + + /** + * Identifies methods that... + * DO call other methods annotated with @PermissionMethod + * DO NOT do anything else + */ + private fun onlyCallsPermissionMethod(method: UMethod): Boolean { + val body = method.uastBody as? UBlockExpression ?: return false + if (body.expressions.isEmpty()) return false + for (expression in body.expressions) { + when (expression) { + is UQualifiedReferenceExpression -> { + if (!isPermissionMethodCall(expression.selector)) return false + } + is UReturnExpression -> { + if (!isPermissionMethodCall(expression.returnExpression)) return false + } + is UCallExpression -> { + if (!isPermissionMethodCall(expression)) return false + } + is UIfExpression -> { + if (expression.thenExpression !is UReturnExpression) return false + if (!isPermissionMethodCall(expression.condition)) return false + } + else -> return false + } + } + return true + } + + private fun isPermissionMethodCall(expression: UExpression?): Boolean { + return when (expression) { + is UQualifiedReferenceExpression -> + return isPermissionMethodCall(expression.selector) + is UCallExpression -> { + val calledMethod = expression.resolve()?.getUMethod() ?: return false + return hasPermissionMethodAnnotation(calledMethod) + } + else -> false + } + } + } +} |