diff options
157 files changed, 5197 insertions, 3504 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 18665e7f4d22..f49cdbf403f0 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -241,7 +241,7 @@ public class JobInfo implements Parcelable { /** * Default value for all regular jobs. As noted in {@link JobScheduler}, - * these jobs have a general maximum execution time of 10 minutes. + * these jobs have a general execution time of 10 minutes. * Receives the standard job management policy. */ public static final int PRIORITY_DEFAULT = 300; @@ -250,7 +250,7 @@ public class JobInfo implements Parcelable { * This task should be ordered ahead of most other tasks. It may be * deferred a little, but if it doesn't run at some point, the user may think * something is wrong. Assuming all constraints remain satisfied - * (including ideal system load conditions), these jobs will have a maximum + * (including ideal system load conditions), these jobs can have an * execution time of at least 4 minutes. Setting all of your jobs to high * priority will not be beneficial to your app and in fact may hurt its * performance in the long run. @@ -260,7 +260,7 @@ public class JobInfo implements Parcelable { /** * This task should be run ahead of all other tasks. Only Expedited Jobs * {@link Builder#setExpedited(boolean)} can have this priority and as such, - * are subject to the same maximum execution time details noted in + * are subject to the same execution time details noted in * {@link Builder#setExpedited(boolean)}. * A sample task of max priority: receiving a text message and processing it to * show a notification @@ -1414,6 +1414,15 @@ public class JobInfo implements Parcelable { * you also need to define the network traffic used by each work item * when constructing them. * + * <p class="note"> + * Prior to Android version {@link Build.VERSION_CODES#TIRAMISU}, JobScheduler used the + * estimated transfer numbers in a similar fashion to + * {@link #setMinimumNetworkChunkBytes(long)} (to estimate if the work would complete + * within the time available to job). In other words, JobScheduler treated the transfer as + * all-or-nothing. Starting from Android version {@link Build.VERSION_CODES#TIRAMISU}, + * JobScheduler will only use the estimated transfer numbers in this manner if minimum + * chunk sizes have not been provided via {@link #setMinimumNetworkChunkBytes(long)}. + * * @param downloadBytes The estimated size of network traffic that will * be downloaded by this job, in bytes. * @param uploadBytes The estimated size of network traffic that will be @@ -1756,14 +1765,19 @@ public class JobInfo implements Parcelable { * * <p> * Assuming all constraints remain satisfied (including ideal system load conditions), - * expedited jobs will have a maximum execution time of at least 1 minute. If your + * expedited jobs can have an execution time of at least 1 minute. If your * app has remaining expedited job quota, then the expedited job <i>may</i> potentially run * longer until remaining quota is used up. Just like with regular jobs, quota is not * consumed while the app is on top and visible to the user. * - * <p> + * <p class="note"> * Note: Even though expedited jobs are meant to run as soon as possible, they may be * deferred if the system is under heavy load or requested constraints are not satisfied. + * This delay may be true for expedited jobs of the foreground app on Android version + * {@link Build.VERSION_CODES#S}, but starting from Android version + * {@link Build.VERSION_CODES#TIRAMISU}, expedited jobs for the foreground app are + * guaranteed to be started before {@link JobScheduler#schedule(JobInfo)} returns (assuming + * all requested constraints are satisfied), similar to foreground services. * * @see JobInfo#isExpedited() */ @@ -1799,6 +1813,9 @@ public class JobInfo implements Parcelable { * and in the background, or the job failed due to unsatisfied constraints, * this job should be expected to behave like other jobs without this flag. * + * <p> + * Jobs marked as important-while-foreground are given {@link #PRIORITY_HIGH} by default. + * * @param importantWhileForeground whether to relax doze restrictions for this job when the * app is in the foreground. False by default. * @see JobInfo#isImportantWhileForeground() @@ -1831,8 +1848,9 @@ public class JobInfo implements Parcelable { * the specific user of this device. For example, fetching top headlines * of interest to the current user. * <p> - * Starting with Android version {@link Build.VERSION_CODES#TIRAMISU}, prefetch jobs are - * not allowed to have deadlines (set via {@link #setOverrideDeadline(long)}. + * Apps targeting Android version {@link Build.VERSION_CODES#TIRAMISU} or later are + * not allowed to have deadlines (set via {@link #setOverrideDeadline(long)} on their + * prefetch jobs. * <p> * The system may use this signal to relax the network constraints you * originally requested, such as allowing a diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java index 1429c45ef2d2..ed72530d8608 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java @@ -167,7 +167,7 @@ public class JobParameters implements Parcelable { /** * The job used up its maximum execution time and timed out. Each individual job has a maximum * execution time limit, regardless of how much total quota the app has. See the note on - * {@link JobScheduler} for the execution time limits. + * {@link JobScheduler} and {@link JobInfo} for the execution time limits. */ public static final int STOP_REASON_TIMEOUT = 3; /** diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java index dd0d07f68547..c3fc7b16ebdf 100644 --- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java +++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java @@ -16,8 +16,10 @@ package android.app.tare; +import android.annotation.Nullable; import android.annotation.SystemService; import android.content.Context; +import android.util.Log; /** * Provides access to the resource economy service. @@ -26,6 +28,69 @@ import android.content.Context; */ @SystemService(Context.RESOURCE_ECONOMY_SERVICE) public class EconomyManager { + private static final String TAG = "TARE-" + EconomyManager.class.getSimpleName(); + + /** + * 1 ARC = 1 GIGA-CAKE! + * + * @hide + */ + public static final long CAKE_IN_ARC = 1_000_000_000L; + + /** @hide */ + public static long arcToCake(int arcs) { + return arcs * CAKE_IN_ARC; + } + + /** + * Parses a configuration string to get the value in cakes. + * + * @hide + */ + public static long parseCreditValue(@Nullable final String val, final long defaultValCakes) { + String trunc; + if (val == null || (trunc = val.trim()).isEmpty()) { + return defaultValCakes; + } + long multiplier; + if (trunc.endsWith("c")) { + trunc = trunc.substring(0, trunc.length() - 1); + multiplier = 1; + } else if (trunc.endsWith("ck")) { + trunc = trunc.substring(0, trunc.length() - 2); + multiplier = 1; + } else if (trunc.endsWith("A")) { + trunc = trunc.substring(0, trunc.length() - 1); + multiplier = CAKE_IN_ARC; + } else if (trunc.endsWith("ARC")) { + trunc = trunc.substring(0, trunc.length() - 3); + multiplier = CAKE_IN_ARC; + } else { + // Don't risk using the wrong units + Log.e(TAG, "Couldn't determine units of credit value: " + val); + return defaultValCakes; + } + + // Allow people to shorten notation (eg. Mc for Megacake). + if (trunc.endsWith("k")) { + trunc = trunc.substring(0, trunc.length() - 1); + multiplier *= 1_000; + } else if (trunc.endsWith("M")) { + trunc = trunc.substring(0, trunc.length() - 1); + multiplier *= 1_000_000; + } else if (trunc.endsWith("G")) { + trunc = trunc.substring(0, trunc.length() - 1); + multiplier *= 1_000_000_000; + } + + try { + return Long.parseLong(trunc) * multiplier; + } catch (NumberFormatException e) { + Log.e(TAG, "Malformed config string: " + val + " to " + trunc, e); + return defaultValCakes; + } + } + // Keys for AlarmManager TARE factors /** @hide */ public static final String KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED = @@ -276,179 +341,201 @@ public class EconomyManager { // Default values AlarmManager factors /** @hide */ - public static final int DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED = 500; + public static final long DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES = arcToCake(500); /** @hide */ - public static final int DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP = 200; + public static final long DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES = + arcToCake(256); /** @hide */ - public static final int DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP = 160; + public static final long DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES = arcToCake(160); /** @hide */ - public static final int DEFAULT_AM_MAX_SATIATED_BALANCE = 1440; + public static final long DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES = arcToCake(960); /** @hide */ - public static final int DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT = 4000; + public static final long DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES = arcToCake(2880); /** @hide */ - public static final int DEFAULT_AM_HARD_CONSUMPTION_LIMIT = 28_800; + public static final long DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES = arcToCake(15_000); // TODO: add AlarmManager modifier default values /** @hide */ - public static final int DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT = 0; + public static final long DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT_CAKES = arcToCake(0); /** @hide */ - public static final float DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING = 0.01f; + // 10 megacakes = .01 ARC + public static final long DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING_CAKES = 10_000_000; /** @hide */ - public static final int DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX = 500; + public static final long DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX_CAKES = arcToCake(500); /** @hide */ - public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT = 3; + public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES = arcToCake(3); /** @hide */ - public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING = 0; + public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES = arcToCake(0); /** @hide */ - public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX = 60; + public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX_CAKES = arcToCake(60); /** @hide */ - public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_INSTANT = 5; + public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_INSTANT_CAKES = + arcToCake(5); /** @hide */ - public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_ONGOING = 0; + public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_ONGOING_CAKES = + arcToCake(0); /** @hide */ - public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_MAX = 500; + public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_MAX_CAKES = + arcToCake(500); /** @hide */ - public static final int DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT = 5; + public static final long DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES = + arcToCake(5); /** @hide */ - public static final int DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING = 0; + public static final long DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES = + arcToCake(0); /** @hide */ - public static final int DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX = 500; + public static final long DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES = arcToCake(500); /** @hide */ - public static final int DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT = 10; + public static final long DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT_CAKES = arcToCake(10); /** @hide */ - public static final int DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING = 0; + public static final long DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING_CAKES = arcToCake(0); /** @hide */ - public static final int DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX = 500; + public static final long DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX_CAKES = arcToCake(500); /** @hide */ - public static final int DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT = 10; + public static final long DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES = arcToCake(10); /** @hide */ - public static final int DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING = 0; + public static final long DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES = arcToCake(0); /** @hide */ - public static final int DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX = 500; + public static final long DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX_CAKES = arcToCake(500); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP = 3; + public static final long DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES = + arcToCake(3); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP = 3; + public static final long DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES = + arcToCake(3); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP = 3; + public static final long DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES = arcToCake(3); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP = 3; + public static final long DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES = arcToCake(3); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP = 1; + public static final long DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES = + arcToCake(1); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP = 1; + public static final long DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES = arcToCake(1); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP = 1; + public static final long DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP_CAKES = + arcToCake(1); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP = 1; + public static final long DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP_CAKES = arcToCake(1); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP = 5; + public static final long DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES = arcToCake(5); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE = 5; + public static final long + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES = arcToCake(5); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE = 4; + public static final long + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE_CAKES = arcToCake(4); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE = 4; + public static final long DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES = arcToCake(4); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE = 3; + public static final long DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES = arcToCake(3); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE = 3; + public static final long + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE_CAKES = + arcToCake(3); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE = 2; + public static final long DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES = + arcToCake(2); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE = - 2; + public static final long + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES = + arcToCake(2); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE = 1; + public static final long DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE_CAKES = + arcToCake(1); /** @hide */ - public static final int DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE = 10; + public static final long DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES = arcToCake(10); // Default values JobScheduler factors // TODO: add time_since_usage variable to min satiated balance factors /** @hide */ - public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED = 20000; + public static final long DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES = arcToCake(15000); /** @hide */ - public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP = 10000; + public static final long DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES = + arcToCake(7500); /** @hide */ - public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP = 2000; + public static final long DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES = arcToCake(2000); /** @hide */ - public static final int DEFAULT_JS_MAX_SATIATED_BALANCE = 60000; + public static final long DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES = arcToCake(60000); /** @hide */ - public static final int DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT = 100_000; + public static final long DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES = arcToCake(29_000); /** @hide */ - public static final int DEFAULT_JS_HARD_CONSUMPTION_LIMIT = 460_000; + // TODO: set hard limit based on device type (phone vs tablet vs etc) + battery size + public static final long DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES = arcToCake(250_000); // TODO: add JobScheduler modifier default values /** @hide */ - public static final int DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT = 0; + public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES = arcToCake(0); /** @hide */ - public static final float DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING = 0.5f; + public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES = CAKE_IN_ARC / 2; /** @hide */ - public static final int DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX = 15000; + public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES = arcToCake(15000); /** @hide */ - public static final int DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT = 1; + public static final long DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES = arcToCake(1); /** @hide */ - public static final int DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING = 0; + public static final long DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES = arcToCake(0); /** @hide */ - public static final int DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX = 10; + public static final long DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES = arcToCake(10); /** @hide */ - public static final int DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT = 5; + public static final long DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES = + arcToCake(5); /** @hide */ - public static final int DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING = 0; + public static final long DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES = + arcToCake(0); /** @hide */ - public static final int DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX = 5000; + public static final long DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES = arcToCake(5000); /** @hide */ - public static final int DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT = 10; + public static final long DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES = arcToCake(10); /** @hide */ - public static final int DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING = 0; + public static final long DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES = arcToCake(0); /** @hide */ - public static final int DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX = 5000; + public static final long DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX_CAKES = arcToCake(5000); /** @hide */ - public static final int DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT = 10; + public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES = arcToCake(10); /** @hide */ - public static final int DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING = 0; + public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES = arcToCake(0); /** @hide */ - public static final int DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX = 5000; + public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES = arcToCake(5000); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_MAX_START_CTP = 3; + public static final long DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES = arcToCake(3); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP = 2; + public static final long DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES = arcToCake(2); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_HIGH_START_CTP = 3; + public static final long DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES = arcToCake(3); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP = 2; + public static final long DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES = arcToCake(2); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP = 3; + public static final long DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES = arcToCake(3); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP = 2; + public static final long DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES = arcToCake(2); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_LOW_START_CTP = 3; + public static final long DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES = arcToCake(3); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP = 2; + public static final long DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES = arcToCake(2); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_MIN_START_CTP = 3; + public static final long DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES = arcToCake(3); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP = 2; + public static final long DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES = arcToCake(2); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP = 30; + public static final long DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES = arcToCake(30); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE = 10; + public static final long DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE_CAKES = arcToCake(10); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE = 5; + public static final long DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE_CAKES = arcToCake(5); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE = 8; + public static final long DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE_CAKES = arcToCake(8); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE = 4; + public static final long DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE_CAKES = arcToCake(4); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE = 6; + public static final long DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE_CAKES = arcToCake(6); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE = 3; + public static final long DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE_CAKES = arcToCake(3); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE = 4; + public static final long DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE_CAKES = arcToCake(4); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE = 2; + public static final long DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE_CAKES = arcToCake(2); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE = 2; + public static final long DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE_CAKES = arcToCake(2); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE = 1; + public static final long DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE_CAKES = arcToCake(1); /** @hide */ - public static final int DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE = 60; + public static final long DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES = arcToCake(60); } diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java index c8ec89475050..c86353c84467 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -690,6 +690,12 @@ class JobConcurrencyManager { int projectedRunningCount = numRunningJobs; while ((nextPending = pendingJobQueue.next()) != null) { if (mRunningJobs.contains(nextPending)) { + // Should never happen. + Slog.wtf(TAG, "Pending queue contained a running job"); + if (DEBUG) { + Slog.e(TAG, "Pending+running job: " + nextPending); + } + pendingJobQueue.remove(nextPending); continue; } @@ -1137,7 +1143,8 @@ class JobConcurrencyManager { } } - if (mActiveServices.size() >= STANDARD_CONCURRENCY_LIMIT) { + final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue(); + if (mActiveServices.size() >= STANDARD_CONCURRENCY_LIMIT || pendingJobQueue.size() == 0) { worker.clearPreferredUid(); // We're over the limit (because the TOP app scheduled a lot of EJs). Don't start // running anything new until we get back below the limit. @@ -1145,7 +1152,6 @@ class JobConcurrencyManager { return; } - final PendingJobQueue pendingJobQueue = mService.getPendingJobQueue(); if (worker.getPreferredUid() != JobServiceContext.NO_PREFERRED_UID) { updateCounterConfigLocked(); // Preemption case needs special care. @@ -1162,6 +1168,12 @@ class JobConcurrencyManager { pendingJobQueue.resetIterator(); while ((nextPending = pendingJobQueue.next()) != null) { if (mRunningJobs.contains(nextPending)) { + // Should never happen. + Slog.wtf(TAG, "Pending queue contained a running job"); + if (DEBUG) { + Slog.e(TAG, "Pending+running job: " + nextPending); + } + pendingJobQueue.remove(nextPending); continue; } @@ -1239,6 +1251,12 @@ class JobConcurrencyManager { while ((nextPending = pendingJobQueue.next()) != null) { if (mRunningJobs.contains(nextPending)) { + // Should never happen. + Slog.wtf(TAG, "Pending queue contained a running job"); + if (DEBUG) { + Slog.e(TAG, "Pending+running job: " + nextPending); + } + pendingJobQueue.remove(nextPending); continue; } 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 9e131339595f..358c32722b8b 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1749,8 +1749,13 @@ public class JobSchedulerService extends com.android.server.SystemService if (!removed) { // We never create JobStatus objects for the express purpose of removing them, and this // method is only ever called for jobs that were saved in the JobStore at some point, - // so if we can't find it, something went seriously wrong. - Slog.wtfStack(TAG, "Job didn't exist in JobStore"); + // so if we can't find it, something may be wrong. As of Android T, there is a + // legitimate code path where removed is false --- when an actively running job is + // cancelled (eg. via JobScheduler.cancel() or the app scheduling a new job with the + // same job ID), we remove it from the JobStore and tell the JobServiceContext to stop + // running the job. Once the job stops running, we then call this method again. + // TODO: rework code so we don't intentionally call this method twice for the same job + Slog.w(TAG, "Job didn't exist in JobStore"); } if (mReadyToRock) { for (int i = 0; i < mControllers.size(); i++) { 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 c2e81882eed2..d0f719b13b89 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java @@ -16,43 +16,43 @@ package com.android.server.tare; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP; -import static android.app.tare.EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT; -import static android.app.tare.EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT; -import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE; -import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED; -import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX; -import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING_CAKES; import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE; import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP; import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE; @@ -97,12 +97,12 @@ import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING; import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE; import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE; import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE; -import static com.android.server.tare.TareUtils.arcToCake; import static com.android.server.tare.TareUtils.cakeToString; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; +import android.provider.DeviceConfig; import android.provider.Settings; import android.util.IndentingPrintWriter; import android.util.KeyValueListParser; @@ -157,14 +157,15 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { AlarmManagerEconomicPolicy(InternalResourceService irs) { super(irs); mInternalResourceService = irs; - loadConstants(""); + loadConstants("", null); } @Override - void setup() { - super.setup(); + void setup(@NonNull DeviceConfig.Properties properties) { + super.setup(properties); ContentResolver resolver = mInternalResourceService.getContext().getContentResolver(); - loadConstants(Settings.Global.getString(resolver, TARE_ALARM_MANAGER_CONSTANTS)); + loadConstants(Settings.Global.getString(resolver, TARE_ALARM_MANAGER_CONSTANTS), + properties); } @Override @@ -178,6 +179,11 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { @Override long getMaxSatiatedBalance() { + // TODO(230501287): adjust balance based on whether the app has the SCHEDULE_EXACT_ALARM + // permission granted. Apps without the permission granted shouldn't need a high balance + // since they won't be able to use exact alarms. Apps with the permission granted could + // have a higher balance, or perhaps just those with the USE_EXACT_ALARM permission since + // that is limited to specific use cases. return mMaxSatiatedBalance; } @@ -209,7 +215,8 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { return mRewards.get(rewardId); } - private void loadConstants(String policyValuesString) { + private void loadConstants(String policyValuesString, + @Nullable DeviceConfig.Properties properties) { mActions.clear(); mRewards.clear(); @@ -219,145 +226,159 @@ public class AlarmManagerEconomicPolicy extends EconomicPolicy { Slog.e(TAG, "Global setting key incorrect: ", e); } - mMinSatiatedBalanceExempted = arcToCake(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, - DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED)); - mMinSatiatedBalanceOther = arcToCake(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, - DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP)); - mMaxSatiatedBalance = arcToCake(mParser.getInt(KEY_AM_MAX_SATIATED_BALANCE, - DEFAULT_AM_MAX_SATIATED_BALANCE)); - mInitialSatiatedConsumptionLimit = arcToCake(mParser.getInt( - KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT)); + mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties, + KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, + DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES); + mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties, + KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, + DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES); + mMaxSatiatedBalance = getConstantAsCake(mParser, properties, + KEY_AM_MAX_SATIATED_BALANCE, + DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES); + mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties, + KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES); mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit, - arcToCake(mParser.getInt( - KEY_AM_HARD_CONSUMPTION_LIMIT, DEFAULT_AM_HARD_CONSUMPTION_LIMIT))); + getConstantAsCake(mParser, properties, + KEY_AM_HARD_CONSUMPTION_LIMIT, DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES)); - final long exactAllowWhileIdleWakeupBasePrice = arcToCake( - mParser.getInt(KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE)); + final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake(mParser, properties, + KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE, + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES); mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP)), + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES), exactAllowWhileIdleWakeupBasePrice)); mActions.put(ACTION_ALARM_WAKEUP_EXACT, new Action(ACTION_ALARM_WAKEUP_EXACT, - arcToCake(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP)), - arcToCake(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP, + DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE, + DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES))); final long inexactAllowWhileIdleWakeupBasePrice = - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE)); + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE_CAKES); mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE, new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE, - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP)), + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES), inexactAllowWhileIdleWakeupBasePrice)); mActions.put(ACTION_ALARM_WAKEUP_INEXACT, new Action(ACTION_ALARM_WAKEUP_INEXACT, - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP)), - arcToCake(mParser.getInt( + DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES), + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE)))); - - final long exactAllowWhileIdleNonWakeupBasePrice = - arcToCake(mParser.getInt( - KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE)); + DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES))); + final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties, + KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE, + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES); mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE, new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE, - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP)), + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES), exactAllowWhileIdleNonWakeupBasePrice)); + mActions.put(ACTION_ALARM_NONWAKEUP_EXACT, new Action(ACTION_ALARM_NONWAKEUP_EXACT, - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP)), - arcToCake(mParser.getInt( + DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES), + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE)))); - - final long inexactAllowWhileIdleNonWakeupBasePrice = - arcToCake(mParser.getInt( - KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE)); - + DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES))); + + final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake(mParser, properties, + KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE, + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES); + final long inexactAllowWhileIdleNonWakeupCtp = getConstantAsCake(mParser, properties, + KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP, + DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP_CAKES); mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE, new Action(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE, - arcToCake(mParser.getInt( - KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP)), + inexactAllowWhileIdleNonWakeupCtp, inexactAllowWhileIdleNonWakeupBasePrice)); + mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT, new Action(ACTION_ALARM_NONWAKEUP_INEXACT, - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP)), - arcToCake(mParser.getInt( + DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP_CAKES), + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE)))); + DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE_CAKES))); mActions.put(ACTION_ALARM_CLOCK, new Action(ACTION_ALARM_CLOCK, - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP, - DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP)), - arcToCake(mParser.getInt( + DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES), + getConstantAsCake(mParser, properties, KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE)))); + DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES))); mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY, - arcToCake(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_INSTANT, - DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT)), - (long) (arcToCake(1) * mParser.getFloat(KEY_AM_REWARD_TOP_ACTIVITY_ONGOING, - DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING)), - arcToCake(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_MAX, - DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX)))); + getConstantAsCake(mParser, properties, + KEY_AM_REWARD_TOP_ACTIVITY_INSTANT, + DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT_CAKES), + getConstantAsCake(mParser, properties, + KEY_AM_REWARD_TOP_ACTIVITY_ONGOING, + DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING_CAKES), + getConstantAsCake(mParser, properties, + KEY_AM_REWARD_TOP_ACTIVITY_MAX, + DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX_CAKES))); mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN, - arcToCake(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT, - DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT)), - arcToCake(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING, - DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING)), - arcToCake(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_MAX, - DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX)))); + getConstantAsCake(mParser, properties, + KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT, + DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES), + getConstantAsCake(mParser, properties, + KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING, + DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES), + getConstantAsCake(mParser, properties, + KEY_AM_REWARD_NOTIFICATION_SEEN_MAX, + DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX_CAKES))); mRewards.put(REWARD_NOTIFICATION_INTERACTION, new Reward(REWARD_NOTIFICATION_INTERACTION, - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT, - DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT)), - arcToCake(mParser.getInt( + DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES), + getConstantAsCake(mParser, properties, KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING, - DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING)), - arcToCake(mParser.getInt( + DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES), + getConstantAsCake(mParser, properties, KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX, - DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX)))); + DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES))); mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION, - arcToCake(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT, - DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT)), - arcToCake(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING, - DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING)), - arcToCake(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_MAX, - DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX)))); + getConstantAsCake(mParser, properties, + KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT, + DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT_CAKES), + getConstantAsCake(mParser, properties, + KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING, + DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING_CAKES), + getConstantAsCake(mParser, properties, + KEY_AM_REWARD_WIDGET_INTERACTION_MAX, + DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX_CAKES))); mRewards.put(REWARD_OTHER_USER_INTERACTION, new Reward(REWARD_OTHER_USER_INTERACTION, - arcToCake(mParser.getInt(KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT, - DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT)), - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, + KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT, + DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES), + getConstantAsCake(mParser, properties, KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING, - DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING)), - arcToCake(mParser.getInt( + DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES), + getConstantAsCake(mParser, properties, KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX, - DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX)))); + DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX_CAKES))); } @Override 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 2109a8575777..c3eb5bf51161 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java @@ -18,6 +18,7 @@ package com.android.server.tare; import android.annotation.NonNull; import android.annotation.Nullable; +import android.provider.DeviceConfig; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.SparseArray; @@ -57,10 +58,10 @@ public class CompleteEconomicPolicy extends EconomicPolicy { } @Override - void setup() { - super.setup(); + void setup(@NonNull DeviceConfig.Properties properties) { + super.setup(properties); for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) { - mEnabledEconomicPolicies.valueAt(i).setup(); + mEnabledEconomicPolicies.valueAt(i).setup(properties); } updateMaxBalances(); } 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 3a26aae217c2..d401373c0066 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java @@ -16,6 +16,8 @@ package com.android.server.tare; +import static android.app.tare.EconomyManager.parseCreditValue; + import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING; import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE; import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE; @@ -27,7 +29,9 @@ import android.annotation.CallSuper; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.provider.DeviceConfig; import android.util.IndentingPrintWriter; +import android.util.KeyValueListParser; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -170,7 +174,7 @@ public abstract class EconomicPolicy { } @CallSuper - void setup() { + void setup(@NonNull DeviceConfig.Properties properties) { for (int i = 0; i < NUM_COST_MODIFIERS; ++i) { final Modifier modifier = COST_MODIFIER_BY_INDEX[i]; if (modifier != null) { @@ -409,6 +413,22 @@ public abstract class EconomicPolicy { return "UNKNOWN_REWARD:" + Integer.toHexString(eventId); } + protected long getConstantAsCake(@NonNull KeyValueListParser parser, + @Nullable DeviceConfig.Properties properties, String key, long defaultValCake) { + // Don't cross the streams! Mixing Settings/local user config changes with DeviceConfig + // config can cause issues since the scales may be different, so use one or the other. + if (parser.size() > 0) { + // User settings take precedence. Just stick with the Settings constants, even if there + // are invalid values. It's not worth the time to evaluate all the key/value pairs to + // make sure there are valid ones before deciding. + return parseCreditValue(parser.getString(key, null), defaultValCake); + } + if (properties != null) { + return parseCreditValue(properties.getString(key, null), defaultValCake); + } + return defaultValCake; + } + protected static void dumpActiveModifiers(IndentingPrintWriter pw) { for (int i = 0; i < NUM_COST_MODIFIERS; ++i) { pw.print("Modifier "); 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 ce4604f80f50..2118eeb45d70 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java @@ -731,7 +731,7 @@ public class InternalResourceService extends SystemService { registerListeners(); mCurrentBatteryLevel = getCurrentBatteryLevel(); mHandler.post(this::setupHeavyWork); - mCompleteEconomicPolicy.setup(); + mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties()); } } @@ -1014,10 +1014,17 @@ public class InternalResourceService extends SystemService { Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS), false, this); mContentResolver.registerContentObserver( Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS), false, this); - onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TARE)); + onPropertiesChanged(getAllDeviceConfigProperties()); updateEnabledStatus(); } + @NonNull + DeviceConfig.Properties getAllDeviceConfigProperties() { + // Don't want to cache the Properties object locally in case it ends up being large, + // especially since it'll only be used once/infrequently (during setup or on a change). + return DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TARE); + } + @Override public void onChange(boolean selfChange, Uri uri) { if (uri.equals(Settings.Global.getUriFor(Settings.Global.ENABLE_TARE))) { @@ -1030,6 +1037,7 @@ public class InternalResourceService extends SystemService { @Override public void onPropertiesChanged(DeviceConfig.Properties properties) { + boolean economicPolicyUpdated = false; synchronized (mLock) { for (String name : properties.getKeyset()) { if (name == null) { @@ -1039,6 +1047,12 @@ public class InternalResourceService extends SystemService { case KEY_DC_ENABLE_TARE: updateEnabledStatus(); break; + default: + if (!economicPolicyUpdated + && (name.startsWith("am") || name.startsWith("js"))) { + updateEconomicPolicy(); + economicPolicyUpdated = true; + } } } } @@ -1072,7 +1086,7 @@ public class InternalResourceService extends SystemService { mCompleteEconomicPolicy.tearDown(); mCompleteEconomicPolicy = new CompleteEconomicPolicy(InternalResourceService.this); if (mIsEnabled && mBootPhase >= PHASE_SYSTEM_SERVICES_READY) { - mCompleteEconomicPolicy.setup(); + mCompleteEconomicPolicy.setup(getAllDeviceConfigProperties()); if (initialLimit != mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit() || hardLimit != mCompleteEconomicPolicy.getHardSatiatedConsumptionLimit()) { 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 99b93ce5c22c..948f0a71510c 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java @@ -16,48 +16,48 @@ package com.android.server.tare; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_CTP; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_CTP; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_CTP; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_CTP; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE; -import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP; -import static android.app.tare.EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT; -import static android.app.tare.EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT; -import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE; -import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED; -import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX_CAKES; +import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES; import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE; import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP; import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE; @@ -106,12 +106,12 @@ import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING; import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE; import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE; import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE; -import static com.android.server.tare.TareUtils.arcToCake; import static com.android.server.tare.TareUtils.cakeToString; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; +import android.provider.DeviceConfig; import android.provider.Settings; import android.util.IndentingPrintWriter; import android.util.KeyValueListParser; @@ -159,14 +159,15 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy { JobSchedulerEconomicPolicy(InternalResourceService irs) { super(irs); mInternalResourceService = irs; - loadConstants(""); + loadConstants("", null); } @Override - void setup() { - super.setup(); + void setup(@NonNull DeviceConfig.Properties properties) { + super.setup(properties); ContentResolver resolver = mInternalResourceService.getContext().getContentResolver(); - loadConstants(Settings.Global.getString(resolver, TARE_JOB_SCHEDULER_CONSTANTS)); + loadConstants(Settings.Global.getString(resolver, TARE_JOB_SCHEDULER_CONSTANTS), + properties); } @Override @@ -211,7 +212,8 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy { return mRewards.get(rewardId); } - private void loadConstants(String policyValuesString) { + private void loadConstants(String policyValuesString, + @Nullable DeviceConfig.Properties properties) { mActions.clear(); mRewards.clear(); @@ -221,118 +223,153 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy { Slog.e(TAG, "Global setting key incorrect: ", e); } - mMinSatiatedBalanceExempted = arcToCake( - mParser.getInt(KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, - DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED)); - mMinSatiatedBalanceOther = arcToCake( - mParser.getInt(KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, - DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP)); - mMaxSatiatedBalance = arcToCake(mParser.getInt(KEY_JS_MAX_SATIATED_BALANCE, - DEFAULT_JS_MAX_SATIATED_BALANCE)); - mInitialSatiatedConsumptionLimit = arcToCake(mParser.getInt( - KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT)); + mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties, + KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, + DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES); + mMinSatiatedBalanceOther = getConstantAsCake(mParser, properties, + KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, + DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES); + mMaxSatiatedBalance = getConstantAsCake(mParser, properties, + KEY_JS_MAX_SATIATED_BALANCE, + DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES); + mInitialSatiatedConsumptionLimit = getConstantAsCake(mParser, properties, + KEY_JS_INITIAL_CONSUMPTION_LIMIT, + DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES); mHardSatiatedConsumptionLimit = Math.max(mInitialSatiatedConsumptionLimit, - arcToCake(mParser.getInt( - KEY_JS_HARD_CONSUMPTION_LIMIT, DEFAULT_JS_HARD_CONSUMPTION_LIMIT))); + getConstantAsCake(mParser, properties, + KEY_JS_HARD_CONSUMPTION_LIMIT, + DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES)); mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START, - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_CTP, - DEFAULT_JS_ACTION_JOB_MAX_START_CTP)), - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE, - DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_MAX_START_CTP, + DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE, + DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE_CAKES))); mActions.put(ACTION_JOB_MAX_RUNNING, new Action(ACTION_JOB_MAX_RUNNING, - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MAX_RUNNING_CTP, - DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP)), - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE, - DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_MAX_RUNNING_CTP, + DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE, + DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE_CAKES))); mActions.put(ACTION_JOB_HIGH_START, new Action(ACTION_JOB_HIGH_START, - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_START_CTP, - DEFAULT_JS_ACTION_JOB_HIGH_START_CTP)), - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE, - DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_HIGH_START_CTP, + DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE, + DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE_CAKES))); mActions.put(ACTION_JOB_HIGH_RUNNING, new Action(ACTION_JOB_HIGH_RUNNING, - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP, - DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP)), - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE, - DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP, + DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE, + DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE_CAKES))); mActions.put(ACTION_JOB_DEFAULT_START, new Action(ACTION_JOB_DEFAULT_START, - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_START_CTP, - DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP)), - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE, - DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_DEFAULT_START_CTP, + DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE, + DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE_CAKES))); mActions.put(ACTION_JOB_DEFAULT_RUNNING, new Action(ACTION_JOB_DEFAULT_RUNNING, - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP, - DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP)), - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE, - DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP, + DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE, + DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE_CAKES))); mActions.put(ACTION_JOB_LOW_START, new Action(ACTION_JOB_LOW_START, - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_LOW_START_CTP, - DEFAULT_JS_ACTION_JOB_LOW_START_CTP)), - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE, - DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_LOW_START_CTP, + DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE, + DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE_CAKES))); mActions.put(ACTION_JOB_LOW_RUNNING, new Action(ACTION_JOB_LOW_RUNNING, - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_LOW_RUNNING_CTP, - DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP)), - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE, - DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_LOW_RUNNING_CTP, + DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE, + DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE_CAKES))); mActions.put(ACTION_JOB_MIN_START, new Action(ACTION_JOB_MIN_START, - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MIN_START_CTP, - DEFAULT_JS_ACTION_JOB_MIN_START_CTP)), - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE, - DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_MIN_START_CTP, + DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE, + DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE_CAKES))); mActions.put(ACTION_JOB_MIN_RUNNING, new Action(ACTION_JOB_MIN_RUNNING, - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MIN_RUNNING_CTP, - DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP)), - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE, - DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_MIN_RUNNING_CTP, + DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE, + DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE_CAKES))); mActions.put(ACTION_JOB_TIMEOUT, new Action(ACTION_JOB_TIMEOUT, - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP, - DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP)), - arcToCake(mParser.getInt(KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE, - DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE)))); + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP, + DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE, + DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES))); mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY, - arcToCake(mParser.getInt(KEY_JS_REWARD_TOP_ACTIVITY_INSTANT, - DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT)), - (long) (arcToCake(1) * mParser.getFloat(KEY_JS_REWARD_TOP_ACTIVITY_ONGOING, - DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING)), - arcToCake(mParser.getInt(KEY_JS_REWARD_TOP_ACTIVITY_MAX, - DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX)))); + getConstantAsCake(mParser, properties, + KEY_JS_REWARD_TOP_ACTIVITY_INSTANT, + DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_REWARD_TOP_ACTIVITY_ONGOING, + DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_REWARD_TOP_ACTIVITY_MAX, + DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES))); mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN, - arcToCake(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT, - DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT)), - arcToCake(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING, - DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING)), - arcToCake(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_MAX, - DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX)))); + getConstantAsCake(mParser, properties, + KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT, + DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING, + DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_REWARD_NOTIFICATION_SEEN_MAX, + DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES))); mRewards.put(REWARD_NOTIFICATION_INTERACTION, new Reward(REWARD_NOTIFICATION_INTERACTION, - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT, - DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT)), - arcToCake(mParser.getInt( + DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES), + getConstantAsCake(mParser, properties, KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING, - DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING)), - arcToCake(mParser.getInt( + DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES), + getConstantAsCake(mParser, properties, KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX, - DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX)))); + DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES))); mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION, - arcToCake(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT, - DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT)), - arcToCake(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING, - DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING)), - arcToCake(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_MAX, - DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX)))); + getConstantAsCake(mParser, properties, + KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT, + DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING, + DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES), + getConstantAsCake(mParser, properties, + KEY_JS_REWARD_WIDGET_INTERACTION_MAX, + DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX_CAKES))); mRewards.put(REWARD_OTHER_USER_INTERACTION, new Reward(REWARD_OTHER_USER_INTERACTION, - arcToCake(mParser.getInt(KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT, - DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT)), - arcToCake(mParser.getInt( + getConstantAsCake(mParser, properties, + KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT, + DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES), + getConstantAsCake(mParser, properties, KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING, - DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING)), - arcToCake(mParser.getInt( + DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES), + getConstantAsCake(mParser, properties, KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX, - DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX)))); + DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES))); } @Override 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 72d506972dd1..e338ed1c6987 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/README.md +++ b/apex/jobscheduler/service/java/com/android/server/tare/README.md @@ -18,16 +18,17 @@ The key tenets of TARE are: In an ideal world, the system could be said to most efficiently allocate resources by maximizing its profits — by maximizing the aggregate sum of the difference between an action's price (that the app ends up paying) and the cost to produce by the system. This assumes that more important -actions have a higher price than less important actions. With this assumption, maximizing profits -implies that the system runs the most important work first and proceeds in decreasing order of -importance. Of course, that also means the system will not run anything where an app would pay less -for the action than the system's cost to produce that action. Some of this breaks down when we throw -TOP apps into the mix — TOP apps pay 0 for all actions, even though the CTP may be greater -than 0. This is to ensure ideal user experience for the app the user is actively interacting with. -Similar caveats exist for system-critical processes (such as the OS itself) and apps running -foreground services (since those could be critical to user experience, as is the case for media and -navigation apps). Excluding those caveats/special situations, maximizing profits of actions -performed by apps in the background should be the target. +actions have a higher price than less important actions and all actors have perfect information and +convey that information accurately. With these assumptions, maximizing profits implies that the +system runs the most important work first and proceeds in decreasing order of importance. Of course, +that also means the system will not run anything where an app would pay less for the action than the +system's cost to produce that action. Some of this breaks down when we throw TOP apps into the mix +— TOP apps pay 0 for all actions, even though the CTP may be greater than 0. This is to ensure +ideal user experience for the app the user is actively interacting with. Similar caveats exist for +system-critical processes (such as the OS itself) and apps running foreground services (since those +could be critical to user experience, as is the case for media and navigation apps). Excluding those +caveats/special situations, maximizing profits of actions performed by apps in the background should +be the target. To achieve the goal laid out by TARE, we use Android Resource Credits (ARCs for short) as the internal/representative currency of the system. @@ -101,11 +102,37 @@ Tare Improvement Proposal #1 (TIP1) separated allocation (to apps) from supply ( allowed apps to accrue credits as appropriate while still limiting the total number of credits consumed. +# Potential Future Changes + +These are some ideas for further changes. There's no guarantee that they'll be implemented. + +* Include additional components and policies for them. TARE may benefit from adding policies for + components such as broadcast dispatching, network traffic, location requests, and sensor usage. +* Have a separate "account" for critical/special actions. In other words, have two accounts for each + app, where one acts like a special savings account and is only allowed to be used for special + actions such as expedited job execution. The second account would have a lower maximum than the + main account, but would help to make sure that normal actions don't interfere too much with more + critical actions. +* Transferring credits from one app to another. For apps that rely on others for some pieces of + work, it may be beneficial to allow the requesting app to transfer, donate, or somehow make + available some of its own credits to the app doing the work in order to make sure the working app + has enough credits available to do the work. +* Formulate values based on device hardware. For example, adjust the consumption limit based on the + battery size, or the price and/or CTP of actions based on hardware efficiency. +* Price discovery via an auction system. Instead of just setting a fixed price that may be modified + by device and app states, let an app say how much it's willing to pay for a specific action and + then have a small auction when the system needs to decide which app to perform the action for + first or how much to charge the app. + # Definitions * ARC: Android Resource Credits are the "currency" units used as an abstraction layer over the real battery drain. They allow the system to standardize costs and prices across various devices. * Cake: A lie; also the smallest unit of an ARC (1 cake = one-billionth of an ARC = 1 nano-ARC). When the apps request to do something, we shall let them eat cake. -* NARC: The smallest unit of an ARC. A narc is 1 nano-ARC. +* Cost to produce (CTP): An economic term that refers to the total cost incurred by a business to + produce a specific quantity of a product or offer a service. In TARE's context, CTP is meant to be + the estimated cost t ohe system to accomplish a certain action. These "actions" are basically APIs + that apps use to get something done. So the idea is to define the base cost for an app to use a + specific API. * Satiated: used to refer to when the device is fully charged (at 100% battery level)
\ No newline at end of file diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java index 87db8637ddac..6b6984f6ac17 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java @@ -16,6 +16,8 @@ package com.android.server.tare; +import static android.app.tare.EconomyManager.CAKE_IN_ARC; + import android.annotation.NonNull; import android.annotation.SuppressLint; import android.util.IndentingPrintWriter; @@ -26,8 +28,6 @@ import java.text.SimpleDateFormat; import java.time.Clock; class TareUtils { - private static final long CAKE_IN_ARC = 1_000_000_000L; - @SuppressLint("SimpleDateFormat") private static final SimpleDateFormat sDumpDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); @@ -35,10 +35,6 @@ class TareUtils { @VisibleForTesting static Clock sSystemClock = Clock.systemUTC(); - static long arcToCake(int arcs) { - return arcs * CAKE_IN_ARC; - } - static void dumpTime(IndentingPrintWriter pw, long time) { pw.print(sDumpDateFormat.format(time)); } @@ -56,7 +52,7 @@ class TareUtils { if (cakes == 0) { return "0 ARCs"; } - final long sub = Math.abs(cakes) % CAKE_IN_ARC; + final long sub = cakes % CAKE_IN_ARC; final long arcs = cakeToArc(cakes); if (arcs == 0) { return sub == 1 @@ -65,11 +61,11 @@ class TareUtils { } StringBuilder sb = new StringBuilder(); sb.append(arcs); - if (sub > 0) { - sb.append(".").append(sub / (CAKE_IN_ARC / 1000)); + if (sub != 0) { + sb.append(".").append(String.format("%03d", Math.abs(sub) / (CAKE_IN_ARC / 1000))); } sb.append(" ARC"); - if (arcs != 1 || sub > 0) { + if (arcs != 1 || sub != 0) { sb.append("s"); } return sb.toString(); diff --git a/core/api/module-lib-lint-baseline.txt b/core/api/module-lib-lint-baseline.txt index 0c1ebb3b2208..27436ce35867 100644 --- a/core/api/module-lib-lint-baseline.txt +++ b/core/api/module-lib-lint-baseline.txt @@ -1,4 +1,6 @@ // Baseline format: 1.0 +SamShouldBeLast: android.app.Activity#convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions): + SAM-compatible parameters (such as parameter 1, "callback", in android.app.Activity.convertToTranslucent) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int): SAM-compatible parameters (such as parameter 1, "listener", in android.app.ActivityManager.addOnUidImportanceListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler): @@ -9,6 +11,24 @@ SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, an SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.app.PendingIntent#send(int, android.app.PendingIntent.OnFinished, android.os.Handler): SAM-compatible parameters (such as parameter 2, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.content.pm.ApplicationInfo#dump(android.util.Printer, String): + SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ApplicationInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.content.pm.PackageItemInfo#dumpBack(android.util.Printer, String): + SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpBack) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.content.pm.PackageItemInfo#dumpFront(android.util.Printer, String): + SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler): + SAM-compatible parameters (such as parameter 1, "listener", in android.location.LocationManager.addNmeaListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, android.location.LocationListener, android.os.Looper): + SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper): + SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper): + SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(String, android.location.LocationListener, android.os.Looper): + SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(android.location.Criteria, android.location.LocationListener, android.os.Looper): + SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.AudioManager#abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes): SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.abandonAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int): diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index 1b45e88584fe..025e8629fc20 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -1,36 +1,20 @@ // Baseline format: 1.0 ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions(): - -ExecutorRegistration: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler): - Registration methods should have overload that accepts delivery Executor: `setOnRtpRxNoticeListener` + Method should return Collection<CharSequence> (or subclass) instead of raw array; was `java.lang.CharSequence[]` GenericException: android.app.prediction.AppPredictor#finalize(): - + Methods must not throw generic exceptions (`java.lang.Throwable`) GenericException: android.hardware.location.ContextHubClient#finalize(): - + Methods must not throw generic exceptions (`java.lang.Throwable`) GenericException: android.service.autofill.augmented.FillWindow#finalize(): - - - -IntentBuilderName: android.app.search.SearchAction#getIntent(): - -IntentBuilderName: android.app.smartspace.SmartspaceAction#getIntent(): - Methods creating an Intent should be named `create<Foo>Intent()`, was `getIntent` + Methods must not throw generic exceptions (`java.lang.Throwable`) KotlinKeyword: android.app.Notification#when: - + Avoid field names that are Kotlin hard keywords ("when"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords -MissingGetterMatchingBuilder: android.os.NewUserRequest.Builder#setAdmin(): - android.os.NewUserRequest does not declare a `getAdmin()` method matching method android.os.NewUserRequest.Builder.setAdmin() -MissingGetterMatchingBuilder: android.os.NewUserRequest.Builder#setEphemeral(): - android.os.NewUserRequest does not declare a `getEphemeral()` method matching method android.os.NewUserRequest.Builder.setEphemeral() -MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUid(int): - android.security.keystore.KeyGenParameterSpec does not declare a `getUid()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUid(int) -MissingGetterMatchingBuilder: android.service.autofill.Dataset.Builder#setFieldInlinePresentation(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.service.autofill.InlinePresentation): - android.service.autofill.Dataset does not declare a `getFieldInlinePresentation()` method matching method android.service.autofill.Dataset.Builder.setFieldInlinePresentation(android.view.autofill.AutofillId,android.view.autofill.AutofillValue,java.util.regex.Pattern,android.service.autofill.InlinePresentation) MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean): android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean) MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String): @@ -38,175 +22,135 @@ MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#set MissingNullability: android.media.soundtrigger.SoundTriggerDetectionService#onUnbind(android.content.Intent) parameter #0: - + Missing nullability on parameter `intent` in method `onUnbind` MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #0: - + Missing nullability on parameter `inputId` in method `onEvent` MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #1: - + Missing nullability on parameter `eventType` in method `onEvent` MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #2: - + Missing nullability on parameter `eventArgs` in method `onEvent` MissingNullability: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context) parameter #0: - + Missing nullability on parameter `base` in method `attachBaseContext` MissingNullability: android.provider.ContactsContract.MetadataSync#CONTENT_URI: - + Missing nullability on field `CONTENT_URI` in class `class android.provider.ContactsContract.MetadataSync` MissingNullability: android.provider.ContactsContract.MetadataSync#METADATA_AUTHORITY_URI: - + Missing nullability on field `METADATA_AUTHORITY_URI` in class `class android.provider.ContactsContract.MetadataSync` MissingNullability: android.provider.ContactsContract.MetadataSyncState#CONTENT_URI: - + Missing nullability on field `CONTENT_URI` in class `class android.provider.ContactsContract.MetadataSyncState` MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #0: - + Missing nullability on parameter `context` in method `attachInfo` MissingNullability: android.provider.SearchIndexablesProvider#attachInfo(android.content.Context, android.content.pm.ProviderInfo) parameter #1: - + Missing nullability on parameter `info` in method `attachInfo` MissingNullability: android.service.autofill.augmented.AugmentedAutofillService#onUnbind(android.content.Intent) parameter #0: - + Missing nullability on parameter `intent` in method `onUnbind` MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0: - + Missing nullability on parameter `fd` in method `dump` MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1: - + Missing nullability on parameter `pw` in method `dump` MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #2: - + Missing nullability on parameter `args` in method `dump` MissingNullability: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context) parameter #0: - + Missing nullability on parameter `base` in method `attachBaseContext` MissingNullability: android.telephony.NetworkService#onUnbind(android.content.Intent) parameter #0: - -MissingNullability: android.telephony.SubscriptionPlan.Builder#createRecurringDaily(java.time.ZonedDateTime) parameter #0: - -MissingNullability: android.telephony.SubscriptionPlan.Builder#createRecurringMonthly(java.time.ZonedDateTime) parameter #0: - -MissingNullability: android.telephony.SubscriptionPlan.Builder#createRecurringWeekly(java.time.ZonedDateTime) parameter #0: - + Missing nullability on parameter `intent` in method `onUnbind` MissingNullability: android.telephony.data.DataService#onUnbind(android.content.Intent) parameter #0: - + Missing nullability on parameter `intent` in method `onUnbind` MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String): - + Missing nullability on method `setServiceId` return MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String) parameter #0: - - - -NoSettingsProvider: android.provider.Settings.Secure#FAST_PAIR_SCAN_ENABLED: - New setting keys are not allowed (Field: FAST_PAIR_SCAN_ENABLED); use getters/setters in relevant manager class - - -OnNameExpected: android.service.smartspace.SmartspaceService#notifySmartspaceEvent(android.app.smartspace.SmartspaceSessionId, android.app.smartspace.SmartspaceTargetEvent): - Methods implemented by developers should follow the on<Something> style, was `notifySmartspaceEvent` + Missing nullability on parameter `serviceId` in method `setServiceId` ProtectedMember: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context): - + Protected methods not allowed; must be public: method android.printservice.recommendation.RecommendationService.attachBaseContext(android.content.Context)} ProtectedMember: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]): - + Protected methods not allowed; must be public: method android.service.contentcapture.ContentCaptureService.dump(java.io.FileDescriptor,java.io.PrintWriter,String[])} ProtectedMember: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context): - - - -RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimAmount(): - Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) -RethrowRemoteException: android.app.WallpaperManager#getWallpaperDimmingAmount(): - Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) -RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimAmount(float): - Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) -RethrowRemoteException: android.app.WallpaperManager#setWallpaperDimmingAmount(float): - Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) + Protected methods not allowed; must be public: method android.service.notification.NotificationAssistantService.attachBaseContext(android.content.Context)} SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 6, "callback", in android.accounts.AccountManager.addAccount) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean): - + SAM-compatible parameters (such as parameter 1, "listener", in android.accounts.AccountManager.addOnAccountsUpdatedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, String[]): - + SAM-compatible parameters (such as parameter 1, "listener", in android.accounts.AccountManager.addOnAccountsUpdatedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 4, "callback", in android.accounts.AccountManager.confirmCredentials) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#editProperties(String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 3, "callback", in android.accounts.AccountManager.editProperties) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 3, "callback", in android.accounts.AccountManager.finishSession) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler): - + SAM-compatible parameters (such as parameter 3, "callback", in android.accounts.AccountManager.getAccountsByTypeAndFeatures) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#getAuthToken(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 5, "callback", in android.accounts.AccountManager.getAuthToken) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#getAuthToken(android.accounts.Account, String, android.os.Bundle, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 5, "callback", in android.accounts.AccountManager.getAuthToken) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#getAuthToken(android.accounts.Account, String, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 4, "callback", in android.accounts.AccountManager.getAuthToken) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#getAuthTokenByFeatures(String, String, String[], android.app.Activity, android.os.Bundle, android.os.Bundle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 7, "callback", in android.accounts.AccountManager.getAuthTokenByFeatures) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler): - + SAM-compatible parameters (such as parameter 3, "callback", in android.accounts.AccountManager.hasFeatures) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#isCredentialsUpdateSuggested(android.accounts.Account, String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler): - + SAM-compatible parameters (such as parameter 3, "callback", in android.accounts.AccountManager.isCredentialsUpdateSuggested) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler): - + SAM-compatible parameters (such as parameter 2, "callback", in android.accounts.AccountManager.removeAccount) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 3, "callback", in android.accounts.AccountManager.removeAccount) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#renameAccount(android.accounts.Account, String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler): - + SAM-compatible parameters (such as parameter 3, "callback", in android.accounts.AccountManager.renameAccount) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#startAddAccountSession(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 6, "callback", in android.accounts.AccountManager.startAddAccountSession) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#startUpdateCredentialsSession(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 5, "callback", in android.accounts.AccountManager.startUpdateCredentialsSession) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.accounts.AccountManager#updateCredentials(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler): - + SAM-compatible parameters (such as parameter 5, "callback", in android.accounts.AccountManager.updateCredentials) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.app.AlarmManager#set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler): - + SAM-compatible parameters (such as parameter 4, "listener", in android.app.AlarmManager.set) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.app.AlarmManager#setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler): - + SAM-compatible parameters (such as parameter 4, "listener", in android.app.AlarmManager.setExact) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.app.AlarmManager#setWindow(int, long, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler): - + SAM-compatible parameters (such as parameter 5, "listener", in android.app.AlarmManager.setWindow) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.app.WallpaperInfo#dump(android.util.Printer, String): - + SAM-compatible parameters (such as parameter 1, "pw", in android.app.WallpaperInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.app.WallpaperManager#addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler): - -SamShouldBeLast: android.app.admin.DevicePolicyManager#installSystemUpdate(android.content.ComponentName, android.net.Uri, java.util.concurrent.Executor, android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback): - + SAM-compatible parameters (such as parameter 1, "listener", in android.app.WallpaperManager.addOnColorsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.content.IntentFilter#dump(android.util.Printer, String): - + SAM-compatible parameters (such as parameter 1, "du", in android.content.IntentFilter.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.content.pm.ApplicationInfo#dump(android.util.Printer, String): - + SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ApplicationInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.content.pm.PackageItemInfo#dumpBack(android.util.Printer, String): - + SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpBack) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.content.pm.PackageItemInfo#dumpFront(android.util.Printer, String): - + SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.content.pm.ResolveInfo#dump(android.util.Printer, String): - + SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ResolveInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.location.Location#dump(android.util.Printer, String): - + SAM-compatible parameters (such as parameter 1, "pw", in android.location.Location.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler): - -SamShouldBeLast: android.location.LocationManager#registerGnssMeasurementsCallback(java.util.concurrent.Executor, android.location.GnssMeasurementsEvent.Callback): - -SamShouldBeLast: android.location.LocationManager#registerGnssNavigationMessageCallback(java.util.concurrent.Executor, android.location.GnssNavigationMessage.Callback): - -SamShouldBeLast: android.location.LocationManager#registerGnssStatusCallback(java.util.concurrent.Executor, android.location.GnssStatus.Callback): - + SAM-compatible parameters (such as parameter 1, "listener", in android.location.LocationManager.addNmeaListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, android.location.LocationListener, android.os.Looper): - -SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, java.util.concurrent.Executor, android.location.LocationListener): - -SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, java.util.concurrent.Executor, android.location.LocationListener): - + SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper): - -SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, java.util.concurrent.Executor, android.location.LocationListener): - + SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(String, android.location.LocationListener, android.os.Looper): - + SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(android.location.Criteria, android.location.LocationListener, android.os.Looper): - + SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.AudioFocusRequest.Builder#setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler): - + SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioFocusRequest.Builder.setOnAudioFocusChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int): - + SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.AudioRecord#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): - -SamShouldBeLast: android.media.AudioRecord#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback): - -SamShouldBeLast: android.media.AudioRecordingMonitor#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback): - + SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioRecord.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.AudioRouting#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): - + SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioRouting.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): - + SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions +SamShouldBeLast: android.media.MediaCodec#setOnFrameRenderedListener(android.media.MediaCodec.OnFrameRenderedListener, android.os.Handler): + SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaCodec.setOnFrameRenderedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.MediaPlayer#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaPlayer.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.MediaPlayer#setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler): @@ -215,82 +159,65 @@ SamShouldBeLast: android.media.MediaPlayer#setOnDrmPreparedListener(android.medi SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaPlayer.setOnDrmPreparedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.MediaPlayer#setOnMediaTimeDiscontinuityListener(android.media.MediaPlayer.OnMediaTimeDiscontinuityListener, android.os.Handler): SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaPlayer.setOnMediaTimeDiscontinuityListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions -SamShouldBeLast: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler): - SAM-compatible parameters (such as parameter 2, "listener", in android.media.MediaPlayer.setOnRtpRxNoticeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.MediaPlayer#setOnSubtitleDataListener(android.media.MediaPlayer.OnSubtitleDataListener, android.os.Handler): SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaPlayer.setOnSubtitleDataListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.MediaRecorder#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler): - -SamShouldBeLast: android.media.MediaRecorder#registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback): - + SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaRecorder.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName): - + SAM-compatible parameters (such as parameter 1, "sessionListener", in android.media.session.MediaSessionManager.addOnActiveSessionsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.session.MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler): - + SAM-compatible parameters (such as parameter 1, "sessionListener", in android.media.session.MediaSessionManager.addOnActiveSessionsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.media.session.MediaSessionManager#addOnSession2TokensChangedListener(android.media.session.MediaSessionManager.OnSession2TokensChangedListener, android.os.Handler): - -SamShouldBeLast: android.media.session.MediaSessionManager#registerCallback(java.util.concurrent.Executor, android.media.session.MediaSessionManager.Callback): - + SAM-compatible parameters (such as parameter 1, "listener", in android.media.session.MediaSessionManager.addOnSession2TokensChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.nfc.NfcAdapter#enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle): - + SAM-compatible parameters (such as parameter 2, "callback", in android.nfc.NfcAdapter.enableReaderMode) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.nfc.NfcAdapter#ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler): - + SAM-compatible parameters (such as parameter 3, "tagRemovedListener", in android.nfc.NfcAdapter.ignore) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.nfc.NfcAdapter#setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity): - + SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setBeamPushUrisCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.nfc.NfcAdapter#setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...): - + SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setNdefPushMessageCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.nfc.NfcAdapter#setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...): - + SAM-compatible parameters (such as parameter 1, "callback", in android.nfc.NfcAdapter.setOnNdefPushCompleteCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.Binder#attachInterface(android.os.IInterface, String): - + SAM-compatible parameters (such as parameter 1, "owner", in android.os.Binder.attachInterface) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.Binder#linkToDeath(android.os.IBinder.DeathRecipient, int): - + SAM-compatible parameters (such as parameter 1, "recipient", in android.os.Binder.linkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.Binder#unlinkToDeath(android.os.IBinder.DeathRecipient, int): - + SAM-compatible parameters (such as parameter 1, "recipient", in android.os.Binder.unlinkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.Handler#dump(android.util.Printer, String): - + SAM-compatible parameters (such as parameter 1, "pw", in android.os.Handler.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.Handler#postAtTime(Runnable, Object, long): - + SAM-compatible parameters (such as parameter 1, "r", in android.os.Handler.postAtTime) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.Handler#postAtTime(Runnable, long): - + SAM-compatible parameters (such as parameter 1, "r", in android.os.Handler.postAtTime) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.Handler#postDelayed(Runnable, Object, long): - + SAM-compatible parameters (such as parameter 1, "r", in android.os.Handler.postDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.Handler#postDelayed(Runnable, long): - + SAM-compatible parameters (such as parameter 1, "r", in android.os.Handler.postDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.Handler#removeCallbacks(Runnable, Object): - + SAM-compatible parameters (such as parameter 1, "r", in android.os.Handler.removeCallbacks) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.IBinder#linkToDeath(android.os.IBinder.DeathRecipient, int): - + SAM-compatible parameters (such as parameter 1, "recipient", in android.os.IBinder.linkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.IBinder#unlinkToDeath(android.os.IBinder.DeathRecipient, int): - + SAM-compatible parameters (such as parameter 1, "recipient", in android.os.IBinder.unlinkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.RecoverySystem#verifyPackage(java.io.File, android.os.RecoverySystem.ProgressListener, java.io.File): - + SAM-compatible parameters (such as parameter 2, "listener", in android.os.RecoverySystem.verifyPackage) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], String, int, String): SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], android.net.Uri, String): SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.View#postDelayed(Runnable, long): - + SAM-compatible parameters (such as parameter 1, "action", in android.view.View.postDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.View#postOnAnimationDelayed(Runnable, long): - + SAM-compatible parameters (such as parameter 1, "action", in android.view.View.postOnAnimationDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.View#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long): - + SAM-compatible parameters (such as parameter 2, "what", in android.view.View.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.Window#addOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener, android.os.Handler): - + SAM-compatible parameters (such as parameter 1, "listener", in android.view.Window.addOnFrameMetricsAvailableListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.accessibility.AccessibilityManager#addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, android.os.Handler): - + SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addAccessibilityStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, android.os.Handler): - + SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addTouchExplorationStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.webkit.WebChromeClient#onShowFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams): - - - -ServiceName: android.content.Context#CLOUDSEARCH_SERVICE: - - -UserHandleName: android.app.search.SearchAction.Builder#setUserHandle(android.os.UserHandle): - Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `setUserHandle` -UserHandleName: android.app.search.SearchTarget.Builder#setUserHandle(android.os.UserHandle): - -UserHandleName: android.app.smartspace.SmartspaceAction.Builder#setUserHandle(android.os.UserHandle): - Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `setUserHandle` + SAM-compatible parameters (such as parameter 2, "filePathCallback", in android.webkit.WebChromeClient.onShowFileChooser) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index 01604e6becf0..0a906bee6fad 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -1,920 +1,270 @@ // Baseline format: 1.0 AcronymName: android.app.NotificationChannel#isImportanceLockedByOEM(): - + Acronyms should not be capitalized in method names: was `isImportanceLockedByOEM`, should this be `isImportanceLockedByOem`? AcronymName: android.app.NotificationChannel#setImportanceLockedByOEM(boolean): - - - -ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION: - -ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED: - -ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER: - -ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER: - -ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_ADDITIONAL_CALL_INFO: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CALL_RAT_TYPE: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CHILD_NUMBER: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CNA: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CNAP: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CODEC: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_DIALSTRING: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_DISPLAY_TEXT: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_EMERGENCY_CALL: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_IS_CALL_PULL: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_OI: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_OIR: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_REMOTE_URI: - -ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_USSD: - -ActionValue: android.telephony.ims.ImsReasonInfo#EXTRA_MSG_SERVICE_NOT_AUTHORIZED: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#ACTION_CLEANUP: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#ACTION_DOWNLOAD_RESULT_INTERNAL: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#ACTION_FILE_DESCRIPTOR_REQUEST: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_FD_COUNT: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_FINAL_URI: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_FREE_URI_LIST: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_PAUSED_LIST: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_PAUSED_URI_LIST: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_SERVICE_ID: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_TEMP_FILES_IN_USE: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_TEMP_FILE_ROOT: - -ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_TEMP_LIST: - - - -AllUpper: android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes: - Constant field names must be named with only upper case characters: `android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes`, should be `S_DEFAULT_ATTRIBUTES`? - - -ArrayReturn: android.app.UiAutomation#executeShellCommandRw(String): - -ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[]) parameter #3: - -ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int) parameter #3: - -ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#getKeyphrases(): - -ArrayReturn: android.location.GnssMeasurementsEvent#GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]) parameter #1: - + Acronyms should not be capitalized in method names: was `setImportanceLockedByOEM`, should this be `setImportanceLockedByOem`? + + ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10: - + Method parameter should be Collection<Descriptor> (or subclass) instead of raw array; was `android.media.audiofx.AudioEffect.Descriptor[]` ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #11: - -ArrayReturn: android.metrics.LogMaker#LogMaker(Object[]) parameter #0: - -ArrayReturn: android.metrics.LogMaker#deserialize(Object[]) parameter #0: - -ArrayReturn: android.metrics.LogMaker#serialize(): - -ArrayReturn: android.net.TestNetworkManager#createTunInterface(android.net.LinkAddress[]) parameter #0: - -ArrayReturn: android.os.HwBlob#wrapArray(boolean[]): - -ArrayReturn: android.os.HwBlob#wrapArray(byte[]): - -ArrayReturn: android.os.HwBlob#wrapArray(double[]): - -ArrayReturn: android.os.HwBlob#wrapArray(float[]): - -ArrayReturn: android.os.HwBlob#wrapArray(int[]): - -ArrayReturn: android.os.HwBlob#wrapArray(long[]): - -ArrayReturn: android.os.HwBlob#wrapArray(short[]): - -ArrayReturn: android.os.NativeHandle#NativeHandle(java.io.FileDescriptor[], int[], boolean) parameter #0: - -ArrayReturn: android.os.NativeHandle#getFileDescriptors(): - -ArrayReturn: android.security.keystore.AttestationUtils#attestDeviceIds(android.content.Context, int[], byte[]): - -ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - -ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]) parameter #1: - -ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - -ArrayReturn: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0: - + Method parameter should be Collection<Descriptor> (or subclass) instead of raw array; was `android.media.audiofx.AudioEffect.Descriptor[]` ArrayReturn: android.view.Display#getSupportedWideColorGamut(): - + Method should return Collection<ColorSpace> (or subclass) instead of raw array; was `android.graphics.ColorSpace[]` ArrayReturn: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0: - -ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions(): - + Method parameter should be Collection<View> (or subclass) instead of raw array; was `android.view.View[]` ArrayReturn: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillOptions(CharSequence[]) parameter #0: - -ArrayReturn: android.view.inspector.InspectableProperty#enumMapping(): - -ArrayReturn: android.view.inspector.InspectableProperty#flagMapping(): - - - -AutoBoxing: android.os.HwBlob#wrapArray(byte[]): - -AutoBoxing: android.os.HwBlob#wrapArray(double[]): - -AutoBoxing: android.os.HwBlob#wrapArray(float[]): - -AutoBoxing: android.os.HwBlob#wrapArray(int[]): - -AutoBoxing: android.os.HwBlob#wrapArray(long[]): - -AutoBoxing: android.os.HwBlob#wrapArray(short[]): - -AutoBoxing: android.os.VintfObject#getTargetFrameworkCompatibilityMatrixVersion(): - - - -BannedThrow: android.app.ActivityTaskManager#removeStacksInWindowingModes(int[]): - -BannedThrow: android.app.ActivityTaskManager#removeStacksWithActivityTypes(int[]): - -BannedThrow: android.app.ActivityTaskManager#setTaskWindowingMode(int, int, boolean): - -BannedThrow: android.app.ActivityTaskManager#setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean): - -BannedThrow: android.media.audiofx.AudioEffect#getParameter(byte[], byte[]): - -BannedThrow: android.media.audiofx.AudioEffect#getParameter(int, byte[]): - -BannedThrow: android.media.audiofx.AudioEffect#getParameter(int, int[]): - -BannedThrow: android.media.audiofx.AudioEffect#getParameter(int, short[]): - -BannedThrow: android.media.audiofx.AudioEffect#getParameter(int[], short[]): - -BannedThrow: android.media.audiofx.AudioEffect#setParameter(byte[], byte[]): - -BannedThrow: android.media.audiofx.AudioEffect#setParameter(int, byte[]): - -BannedThrow: android.media.audiofx.AudioEffect#setParameter(int, int): - -BannedThrow: android.media.audiofx.AudioEffect#setParameter(int, short): - -BannedThrow: android.media.audiofx.AudioEffect#setParameter(int[], byte[]): - -BannedThrow: android.media.audiofx.AudioEffect#setParameter(int[], int[]): - -BannedThrow: android.media.audiopolicy.AudioMix.Builder#Builder(android.media.audiopolicy.AudioMixingRule): - -BannedThrow: android.media.audiopolicy.AudioMix.Builder#build(): - -BannedThrow: android.media.audiopolicy.AudioMix.Builder#setDevice(android.media.AudioDeviceInfo): - -BannedThrow: android.media.audiopolicy.AudioMix.Builder#setFormat(android.media.AudioFormat): - -BannedThrow: android.media.audiopolicy.AudioMix.Builder#setRouteFlags(int): - -BannedThrow: android.media.audiopolicy.AudioMixingRule.Builder#addMixRule(int, Object): - -BannedThrow: android.media.audiopolicy.AudioMixingRule.Builder#addRule(android.media.AudioAttributes, int): - -BannedThrow: android.media.audiopolicy.AudioMixingRule.Builder#excludeMixRule(int, Object): - -BannedThrow: android.media.audiopolicy.AudioMixingRule.Builder#excludeRule(android.media.AudioAttributes, int): - -BannedThrow: android.media.audiopolicy.AudioPolicy#createAudioRecordSink(android.media.audiopolicy.AudioMix): - -BannedThrow: android.media.audiopolicy.AudioPolicy#createAudioTrackSource(android.media.audiopolicy.AudioMix): - -BannedThrow: android.media.audiopolicy.AudioPolicy#setFocusDuckingBehavior(int): - -BannedThrow: android.media.audiopolicy.AudioPolicy.Builder#addMix(android.media.audiopolicy.AudioMix): - -BannedThrow: android.media.audiopolicy.AudioPolicy.Builder#setLooper(android.os.Looper): - -BannedThrow: android.os.HwBinder#getService(String, String): - -BannedThrow: android.os.HwBinder#getService(String, String, boolean): - -BannedThrow: android.os.Process#getThreadScheduler(int): - - - -BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#allowPrivilegedPlaybackCapture(boolean): - -BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#excludeMixRule(int, Object): - -BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#excludeRule(android.media.AudioAttributes, int): - -BuilderSetStyle: android.net.NetworkCapabilities.Builder#removeCapability(int): - -BuilderSetStyle: android.net.NetworkCapabilities.Builder#removeTransportType(int): - -BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateDnsslLifetime(long): - -BuilderSetStyle: android.net.metrics.RaEvent.Builder#updatePrefixPreferredLifetime(long): - -BuilderSetStyle: android.net.metrics.RaEvent.Builder#updatePrefixValidLifetime(long): - -BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRdnssLifetime(long): - -BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRouteInfoLifetime(long): - -BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRouterLifetime(long): - -BuilderSetStyle: android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc(): - -BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse(): - -BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#permitIncorrectContextUse(): - + Method parameter should be Collection<CharSequence> (or subclass) instead of raw array; was `java.lang.CharSequence[]` -CallbackInterface: android.app.prediction.AppPredictor.Callback: - -CallbackInterface: android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback: - -CallbackInterface: android.widget.Magnifier.Callback: - +AutoBoxing: android.os.VintfObject#getTargetFrameworkCompatibilityMatrixVersion(): + Must avoid boxed primitives (`java.lang.Long`) -CallbackMethodName: android.os.RemoteCallback: - +BuilderSetStyle: android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc(): + Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.ThreadPolicy.Builder.detectExplicitGc() +BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#permitIncorrectContextUse(): + Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.permitIncorrectContextUse() ConcreteCollection: android.content.AutofillOptions#disabledActivities: - + Field type is concrete collection (`android.util.ArrayMap`); must be higher-level interface ConcreteCollection: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill: - + Field type is concrete collection (`android.util.ArraySet`); must be higher-level interface ConcreteCollection: android.content.ContentCaptureOptions#ContentCaptureOptions(int, int, int, int, int, android.util.ArraySet<android.content.ComponentName>) parameter #5: - + Parameter type is concrete collection (`android.util.ArraySet`); must be higher-level interface ConcreteCollection: android.content.ContentCaptureOptions#whitelistedComponents: - + Field type is concrete collection (`android.util.ArraySet`); must be higher-level interface ConcreteCollection: android.database.sqlite.SQLiteDebug.PagerStats#dbStats: - -ConcreteCollection: android.os.HwParcel#readBoolVector(): - -ConcreteCollection: android.os.HwParcel#readDoubleVector(): - -ConcreteCollection: android.os.HwParcel#readFloatVector(): - -ConcreteCollection: android.os.HwParcel#readInt16Vector(): - -ConcreteCollection: android.os.HwParcel#readInt32Vector(): - -ConcreteCollection: android.os.HwParcel#readInt64Vector(): - -ConcreteCollection: android.os.HwParcel#readInt8Vector(): - -ConcreteCollection: android.os.HwParcel#readNativeHandleVector(): - -ConcreteCollection: android.os.HwParcel#readStringVector(): - -ConcreteCollection: android.os.HwParcel#writeBoolVector(java.util.ArrayList<java.lang.Boolean>) parameter #0: - -ConcreteCollection: android.os.HwParcel#writeDoubleVector(java.util.ArrayList<java.lang.Double>) parameter #0: - -ConcreteCollection: android.os.HwParcel#writeFloatVector(java.util.ArrayList<java.lang.Float>) parameter #0: - -ConcreteCollection: android.os.HwParcel#writeInt16Vector(java.util.ArrayList<java.lang.Short>) parameter #0: - -ConcreteCollection: android.os.HwParcel#writeInt32Vector(java.util.ArrayList<java.lang.Integer>) parameter #0: - -ConcreteCollection: android.os.HwParcel#writeInt64Vector(java.util.ArrayList<java.lang.Long>) parameter #0: - -ConcreteCollection: android.os.HwParcel#writeInt8Vector(java.util.ArrayList<java.lang.Byte>) parameter #0: - -ConcreteCollection: android.os.HwParcel#writeNativeHandleVector(java.util.ArrayList<android.os.NativeHandle>) parameter #0: - -ConcreteCollection: android.os.HwParcel#writeStringVector(java.util.ArrayList<java.lang.String>) parameter #0: - + Field type is concrete collection (`java.util.ArrayList`); must be higher-level interface ConcreteCollection: android.service.autofill.CompositeUserData#getFieldClassificationAlgorithms(): - + Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface ConcreteCollection: android.service.autofill.CompositeUserData#getFieldClassificationArgs(): - + Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface ConcreteCollection: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>) parameter #2: - + Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface ConcreteCollection: android.service.autofill.UserData#getFieldClassificationAlgorithms(): - -ConcreteCollection: android.telephony.ims.ImsConferenceState#mParticipants: - + Return type is concrete collection (`android.util.ArrayMap`); must be higher-level interface ContextFirst: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1: - - - -ContextNameSuffix: android.telephony.mbms.vendor.MbmsGroupCallServiceBase: - + Context is distinct, so it must be the first argument (method `get`) EndsWithImpl: android.view.contentcapture.ViewNode.ViewStructureImpl: - + Don't expose your implementation details: `ViewStructureImpl` ends with `Impl` Enum: android.view.inspector.InspectableProperty.ValueType: - - - -EqualsAndHashCode: android.app.prediction.AppPredictionContext#equals(Object): - -EqualsAndHashCode: android.app.prediction.AppTarget#equals(Object): - -EqualsAndHashCode: android.app.prediction.AppTargetEvent#equals(Object): - -EqualsAndHashCode: android.net.apf.ApfCapabilities#equals(Object): - -EqualsAndHashCode: android.net.metrics.ApfProgramEvent#equals(Object): - -EqualsAndHashCode: android.net.metrics.ApfStats#equals(Object): - -EqualsAndHashCode: android.net.metrics.DhcpClientEvent#equals(Object): - -EqualsAndHashCode: android.net.metrics.IpManagerEvent#equals(Object): - -EqualsAndHashCode: android.net.metrics.IpReachabilityEvent#equals(Object): - -EqualsAndHashCode: android.net.metrics.NetworkEvent#equals(Object): - -EqualsAndHashCode: android.net.metrics.RaEvent#equals(Object): - -EqualsAndHashCode: android.net.metrics.ValidationProbeEvent#equals(Object): - -EqualsAndHashCode: android.os.IncidentManager.PendingReport#equals(Object): - + Enums are discouraged in Android APIs + + EqualsAndHashCode: android.os.StrictMode.ViolationInfo#hashCode(): - + Must override both equals and hashCode; missing one in android.os.StrictMode.ViolationInfo -ExecutorRegistration: android.content.pm.PackageManager#addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener): - -ExecutorRegistration: android.hardware.camera2.CameraDevice#createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler): - ExecutorRegistration: android.media.audiofx.AudioEffect#setParameterListener(android.media.audiofx.AudioEffect.OnParameterChangeListener): - -ExecutorRegistration: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener): - -ExecutorRegistration: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener): - -ExecutorRegistration: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback): - -ExecutorRegistration: android.os.IncidentManager#cancelAuthorization(android.os.IncidentManager.AuthListener): - -ExecutorRegistration: android.os.IncidentManager#requestAuthorization(int, String, int, android.os.IncidentManager.AuthListener): - -ExecutorRegistration: android.os.RemoteCallback#RemoteCallback(android.os.RemoteCallback.OnResultListener, android.os.Handler): - + Registration methods should have overload that accepts delivery Executor: `setParameterListener` ExecutorRegistration: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler): - + Registration methods should have overload that accepts delivery Executor: `countPermissionApps` ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler): - + Registration methods should have overload that accepts delivery Executor: `getAppPermissions` ExecutorRegistration: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback): - -ExecutorRegistration: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener): - -ExecutorRegistration: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener): - -ExecutorRegistration: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener): - -ExecutorRegistration: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener): - -ExecutorRegistration: android.telephony.mbms.vendor.MbmsDownloadServiceBase#initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback): - -ExecutorRegistration: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int): - -ExecutorRegistration: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback): - -ExecutorRegistration: android.telephony.mbms.vendor.MbmsStreamingServiceBase#initialize(android.telephony.mbms.MbmsStreamingSessionCallback, int): - -ExecutorRegistration: android.telephony.mbms.vendor.MbmsStreamingServiceBase#startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback): - + Registration methods should have overload that accepts delivery Executor: `setCallback` ExecutorRegistration: android.window.WindowOrganizer#applySyncTransaction(android.window.WindowContainerTransaction, android.window.WindowContainerTransactionCallback): - + Registration methods should have overload that accepts delivery Executor: `applySyncTransaction` ForbiddenSuperClass: android.app.AppDetailsActivity: - + AppDetailsActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. -GenericException: android.app.prediction.AppPredictor#finalize(): - GenericException: android.service.autofill.CharSequenceTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int): - + Methods must not throw generic exceptions (`java.lang.Exception`) GenericException: android.service.autofill.DateTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int): - + Methods must not throw generic exceptions (`java.lang.Exception`) GenericException: android.service.autofill.ImageTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int): - -GenericException: android.service.autofill.augmented.FillWindow#finalize(): - - - -GetterOnBuilder: android.hardware.display.BrightnessConfiguration.Builder#getMaxCorrectionsByCategory(): - -GetterOnBuilder: android.hardware.display.BrightnessConfiguration.Builder#getMaxCorrectionsByPackageName(): - + Methods must not throw generic exceptions (`java.lang.Exception`) -GetterSetterNames: android.app.NotificationChannel#isBlockableSystem(): - -GetterSetterNames: android.app.NotificationChannel#isImportanceLockedByCriticalDeviceFunction(): - -GetterSetterNames: android.app.NotificationChannel#isImportanceLockedByOEM(): - GetterSetterNames: android.location.GnssClock#setBiasNanos(double): - + Symmetric method for `hasBiasNanos` must be named `setHasBiasNanos`; was `setBiasNanos` GetterSetterNames: android.location.GnssClock#setBiasUncertaintyNanos(double): - + Symmetric method for `hasBiasUncertaintyNanos` must be named `setHasBiasUncertaintyNanos`; was `setBiasUncertaintyNanos` GetterSetterNames: android.location.GnssClock#setDriftNanosPerSecond(double): - + Symmetric method for `hasDriftNanosPerSecond` must be named `setHasDriftNanosPerSecond`; was `setDriftNanosPerSecond` GetterSetterNames: android.location.GnssClock#setDriftUncertaintyNanosPerSecond(double): - + Symmetric method for `hasDriftUncertaintyNanosPerSecond` must be named `setHasDriftUncertaintyNanosPerSecond`; was `setDriftUncertaintyNanosPerSecond` GetterSetterNames: android.location.GnssClock#setElapsedRealtimeNanos(long): - + Symmetric method for `hasElapsedRealtimeNanos` must be named `setHasElapsedRealtimeNanos`; was `setElapsedRealtimeNanos` GetterSetterNames: android.location.GnssClock#setElapsedRealtimeUncertaintyNanos(double): - + Symmetric method for `hasElapsedRealtimeUncertaintyNanos` must be named `setHasElapsedRealtimeUncertaintyNanos`; was `setElapsedRealtimeUncertaintyNanos` GetterSetterNames: android.location.GnssClock#setFullBiasNanos(long): - + Symmetric method for `hasFullBiasNanos` must be named `setHasFullBiasNanos`; was `setFullBiasNanos` GetterSetterNames: android.location.GnssClock#setLeapSecond(int): - + Symmetric method for `hasLeapSecond` must be named `setHasLeapSecond`; was `setLeapSecond` GetterSetterNames: android.location.GnssClock#setReferenceCarrierFrequencyHzForIsb(double): - + Symmetric method for `hasReferenceCarrierFrequencyHzForIsb` must be named `setHasReferenceCarrierFrequencyHzForIsb`; was `setReferenceCarrierFrequencyHzForIsb` GetterSetterNames: android.location.GnssClock#setReferenceCodeTypeForIsb(String): - + Symmetric method for `hasReferenceCodeTypeForIsb` must be named `setHasReferenceCodeTypeForIsb`; was `setReferenceCodeTypeForIsb` GetterSetterNames: android.location.GnssClock#setReferenceConstellationTypeForIsb(int): - + Symmetric method for `hasReferenceConstellationTypeForIsb` must be named `setHasReferenceConstellationTypeForIsb`; was `setReferenceConstellationTypeForIsb` GetterSetterNames: android.location.GnssClock#setTimeUncertaintyNanos(double): - + Symmetric method for `hasTimeUncertaintyNanos` must be named `setHasTimeUncertaintyNanos`; was `setTimeUncertaintyNanos` GetterSetterNames: android.location.GnssMeasurement#setBasebandCn0DbHz(double): - + Symmetric method for `hasBasebandCn0DbHz` must be named `setHasBasebandCn0DbHz`; was `setBasebandCn0DbHz` GetterSetterNames: android.location.GnssMeasurement#setCarrierFrequencyHz(float): - + Symmetric method for `hasCarrierFrequencyHz` must be named `setHasCarrierFrequencyHz`; was `setCarrierFrequencyHz` GetterSetterNames: android.location.GnssMeasurement#setCodeType(String): - + Symmetric method for `hasCodeType` must be named `setHasCodeType`; was `setCodeType` GetterSetterNames: android.location.GnssMeasurement#setCorrelationVectors(java.util.Collection<android.location.CorrelationVector>): - + Symmetric method for `hasCorrelationVectors` must be named `setHasCorrelationVectors`; was `setCorrelationVectors` GetterSetterNames: android.location.GnssMeasurement#setFullInterSignalBiasNanos(double): - + Symmetric method for `hasFullInterSignalBiasNanos` must be named `setHasFullInterSignalBiasNanos`; was `setFullInterSignalBiasNanos` GetterSetterNames: android.location.GnssMeasurement#setFullInterSignalBiasUncertaintyNanos(double): - + Symmetric method for `hasFullInterSignalBiasUncertaintyNanos` must be named `setHasFullInterSignalBiasUncertaintyNanos`; was `setFullInterSignalBiasUncertaintyNanos` GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasNanos(double): - + Symmetric method for `hasSatelliteInterSignalBiasNanos` must be named `setHasSatelliteInterSignalBiasNanos`; was `setSatelliteInterSignalBiasNanos` GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasUncertaintyNanos(double): - + Symmetric method for `hasSatelliteInterSignalBiasUncertaintyNanos` must be named `setHasSatelliteInterSignalBiasUncertaintyNanos`; was `setSatelliteInterSignalBiasUncertaintyNanos` GetterSetterNames: android.location.GnssMeasurement#setSatellitePvt(android.location.SatellitePvt): - + Symmetric method for `hasSatellitePvt` must be named `setHasSatellitePvt`; was `setSatellitePvt` GetterSetterNames: android.location.GnssMeasurement#setSnrInDb(double): - -GetterSetterNames: android.location.LocationRequest#isLocationSettingsIgnored(): - -GetterSetterNames: android.location.LocationRequest#isLowPowerMode(): - + Symmetric method for `hasSnrInDb` must be named `setHasSnrInDb`; was `setSnrInDb` GetterSetterNames: android.net.NetworkPolicyManager#getRestrictBackground(): Symmetric method for `setRestrictBackground` must be named `isRestrictBackground`; was `getRestrictBackground` -GetterSetterNames: android.os.IncidentReportArgs#isAll(): - -GetterSetterNames: android.service.notification.NotificationStats#setDirectReplied(): - -GetterSetterNames: android.service.notification.NotificationStats#setExpanded(): - -GetterSetterNames: android.service.notification.NotificationStats#setSeen(): - -GetterSetterNames: android.service.notification.NotificationStats#setSnoozed(): - -GetterSetterNames: android.service.notification.NotificationStats#setViewedSettings(): - -GetterSetterNames: android.view.View#isAutofilled(): - -GetterSetterNames: android.view.View#isDefaultFocusHighlightEnabled(): - - - -IllegalStateException: android.media.audiopolicy.AudioMix.Builder#build(): - - - -IntentBuilderName: android.app.backup.BackupManager#getConfigurationIntent(String): - -IntentBuilderName: android.app.backup.BackupManager#getDataManagementIntent(String): - + + IntentBuilderName: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale): - + Methods creating an Intent should be named `create<Foo>Intent()`, was `getManageKeyphraseIntent` IntentName: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE: - + Intent action constant name must be ACTION_FOO: VOICE_INTERACTION_SERVICE IntentName: android.provider.Telephony.Sms.Intents#SMS_CARRIER_PROVISION_ACTION: - -IntentName: android.service.notification.Adjustment#KEY_CONTEXTUAL_ACTIONS: - + Intent action constant name must be ACTION_FOO: SMS_CARRIER_PROVISION_ACTION -InterfaceConstant: android.service.autofill.AutofillFieldClassificationService#SERVICE_INTERFACE: - -InterfaceConstant: android.service.autofill.augmented.AugmentedAutofillService#SERVICE_INTERFACE: - -InterfaceConstant: android.service.contentcapture.ContentCaptureService#SERVICE_INTERFACE: - -InterfaceConstant: android.service.notification.NotificationAssistantService#SERVICE_INTERFACE: - -InterfaceConstant: android.telecom.PhoneAccountSuggestionService#SERVICE_INTERFACE: - - - -InternalField: android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes: - Internal field sDefaultAttributes must not be exposed -InternalField: android.telephony.ims.ImsConferenceState#mParticipants: - +KotlinOperator: android.os.PackageTagsList#contains(android.os.PackageTagsList): + Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object) +KotlinOperator: android.util.SparseArrayMap#get(int, K): + Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object) -KotlinOperator: android.os.WorkSource#get(int): - -KotlinOperator: android.util.SparseArrayMap#get(int, K): - -KotlinOperator: android.util.SparseArrayMap#get(int, String): - - - -ListenerInterface: android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener: - -ListenerInterface: android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener: - -ListenerInterface: android.os.IncidentManager.AuthListener: - -ListenerInterface: android.telephony.ims.ImsCallSessionListener: - -ListenerInterface: android.telephony.ims.ImsUtListener: - - - -ListenerLast: android.hardware.camera2.CameraDevice#createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) parameter #4: - -ListenerLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper) parameter #2: - ListenerLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler) parameter #3: - + Listeners should always be at end of argument list (method `countPermissionApps`) ListenerLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler) parameter #2: - -ListenerLast: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) parameter #1: - -ListenerLast: android.telephony.mbms.vendor.MbmsStreamingServiceBase#initialize(android.telephony.mbms.MbmsStreamingSessionCallback, int) parameter #1: - + Listeners should always be at end of argument list (method `getAppPermissions`) ManagerConstructor: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context): - - - -ManagerLookup: android.telephony.ims.ImsMmTelManager#createForSubscriptionId(int): - -ManagerLookup: android.telephony.ims.ProvisioningManager#createForSubscriptionId(int): - - - -MethodNameTense: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToEnable(): - - - -MethodNameUnits: android.telephony.ims.ImsCallForwardInfo#getTimeSeconds(): - + Managers must always be obtained from Context; no direct constructors MinMaxConstant: android.os.UserHandle#MIN_SECONDARY_USER_ID: - + If min/max could change in future, make them dynamic methods: android.os.UserHandle#MIN_SECONDARY_USER_ID MinMaxConstant: android.view.autofill.AutofillManager#MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS: - - - -MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setFlags(int): - -MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setOpNames(java.util.List<java.lang.String>): - -MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setPackageName(String): - -MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setUid(int): - -MissingGetterMatchingBuilder: android.content.integrity.RuleSet.Builder#addRules(java.util.List<android.content.integrity.Rule>): - -MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection): - -MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#addCorrectionByPackageName(String, android.hardware.display.BrightnessCorrection): - -MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#setDescription(String): - -MissingGetterMatchingBuilder: android.hardware.lights.LightsRequest.Builder#setLight(android.hardware.lights.Light, android.hardware.lights.LightState): - + If min/max could change in future, make them dynamic methods: android.view.autofill.AutofillManager#MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS + + MissingGetterMatchingBuilder: android.media.VolumeShaper.Configuration.Builder#setOptionFlags(int): - -MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setDevice(android.media.AudioDeviceInfo): - -MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setFormat(android.media.AudioFormat): - -MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setRouteFlags(int): - -MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMixingRule.Builder#addMixRule(int, Object): - -MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMixingRule.Builder#addRule(android.media.AudioAttributes, int): - -MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#addMix(android.media.audiopolicy.AudioMix): - -MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener): - -MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener): - -MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback): - -MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsAudioFocusPolicy(boolean): - + android.media.VolumeShaper.Configuration does not declare a `getOptionFlags()` method matching method android.media.VolumeShaper.Configuration.Builder.setOptionFlags(int) MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsTestFocusPolicy(boolean): - -MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setLooper(android.os.Looper): - -MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setBytesRemaining(long): - -MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setExpiryTime(long): - -MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setRefreshTime(long): - -MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#addCapability(int): - -MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#setRequestorPackageName(String): - -MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#setRequestorUid(int): - -MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setShouldShowEntitlementUi(boolean): - -MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setStaticIpv4Addresses(android.net.LinkAddress, android.net.LinkAddress): - -MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setActualLifetime(long): - -MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setCurrentRas(int): - -MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setFilteredRas(int): - -MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setFlags(boolean, boolean): - -MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setLifetime(long): - -MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setProgramLength(int): - -MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setDroppedRas(int): - -MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setDurationMs(long): - -MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setMatchingRas(int): - -MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setMaxProgramSize(int): - -MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setParseErrors(int): - -MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdates(int): - -MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdatesAll(int): - -MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdatesAllowingMulticast(int): - -MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setReceivedRas(int): - -MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setZeroLifetimeRas(int): - -MissingGetterMatchingBuilder: android.net.metrics.DhcpClientEvent.Builder#setDurationMs(int): - -MissingGetterMatchingBuilder: android.net.metrics.DhcpClientEvent.Builder#setMsg(String): - -MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setDurationMs(long): - -MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setProbeType(int, boolean): - -MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setReturnCode(int): - + android.media.audiopolicy.AudioPolicy does not declare a `isIsTestFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsTestFocusPolicy(boolean) MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUniqueIdIncluded(boolean): - -MissingGetterMatchingBuilder: android.service.autofill.Dataset.Builder#setFieldInlinePresentation(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.service.autofill.InlinePresentation): - -MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setClientState(android.os.Bundle): - -MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setFillWindow(android.service.autofill.augmented.FillWindow): - -MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setInlineSuggestions(java.util.List<android.service.autofill.Dataset>): - -MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean): - + android.security.keystore.KeyGenParameterSpec does not declare a `isUniqueIdIncluded()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUniqueIdIncluded(boolean) MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setIsAdhocConferenceCall(boolean): - + android.telecom.ConnectionRequest does not declare a `isIsAdhocConferenceCall()` method matching method android.telecom.ConnectionRequest.Builder.setIsAdhocConferenceCall(boolean) MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeFromInCall(android.os.ParcelFileDescriptor): - + android.telecom.ConnectionRequest does not declare a `getRttPipeFromInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeFromInCall(android.os.ParcelFileDescriptor) MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeToInCall(android.os.ParcelFileDescriptor): - + android.telecom.ConnectionRequest does not declare a `getRttPipeToInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeToInCall(android.os.ParcelFileDescriptor) MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setShouldShowIncomingCallUi(boolean): - -MissingGetterMatchingBuilder: android.telecom.PhoneAccount.Builder#setGroupId(String): - -MissingGetterMatchingBuilder: android.telephony.NetworkRegistrationInfo.Builder#setEmergencyOnly(boolean): - -MissingGetterMatchingBuilder: android.telephony.ims.ImsSsData.Builder#setCallForwardingInfo(java.util.List<android.telephony.ims.ImsCallForwardInfo>): - -MissingGetterMatchingBuilder: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#addFeature(int, int): - -MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String): - + android.telecom.ConnectionRequest does not declare a `shouldShowIncomingCallUi()` method matching method android.telecom.ConnectionRequest.Builder.setShouldShowIncomingCallUi(boolean) MissingGetterMatchingBuilder: android.view.Display.Mode.Builder#setResolution(int, int): android.view.Display.Mode does not declare a `getResolution()` method matching method android.view.Display.Mode.Builder.setResolution(int,int) MissingNullability: android.app.Activity#onMovedToDisplay(int, android.content.res.Configuration) parameter #1: - -MissingNullability: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int) parameter #0: - + Missing nullability on parameter `config` in method `onMovedToDisplay` MissingNullability: android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName) parameter #0: - -MissingNullability: android.app.ActivityManager#forceStopPackage(String) parameter #0: - -MissingNullability: android.app.ActivityManager#getPackageImportance(String) parameter #0: - + Missing nullability on parameter `activity` in method `alwaysShowUnsupportedCompileSdkWarning` MissingNullability: android.app.ActivityManager#holdLock(android.os.IBinder, int) parameter #0: - -MissingNullability: android.app.ActivityManager#removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener) parameter #0: - + Missing nullability on parameter `token` in method `holdLock` MissingNullability: android.app.ActivityManager#scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int) parameter #0: - + Missing nullability on parameter `packages` in method `scheduleApplicationInfoChanged` MissingNullability: android.app.ActivityManager.TaskDescription#getIconFilename(): - + Missing nullability on method `getIconFilename` return MissingNullability: android.app.ActivityTaskManager#clearLaunchParamsForPackages(java.util.List<java.lang.String>) parameter #0: - -MissingNullability: android.app.ActivityTaskManager#listAllStacks(): - -MissingNullability: android.app.ActivityTaskManager#moveTopActivityToPinnedStack(int, android.graphics.Rect) parameter #1: - -MissingNullability: android.app.ActivityTaskManager#removeStacksInWindowingModes(int[]) parameter #0: - -MissingNullability: android.app.ActivityTaskManager#removeStacksWithActivityTypes(int[]) parameter #0: - -MissingNullability: android.app.ActivityTaskManager#resizeDockedStack(android.graphics.Rect, android.graphics.Rect) parameter #0: - -MissingNullability: android.app.ActivityTaskManager#resizeDockedStack(android.graphics.Rect, android.graphics.Rect) parameter #1: - -MissingNullability: android.app.ActivityTaskManager#resizePinnedStack(int, android.graphics.Rect, boolean) parameter #1: - + Missing nullability on parameter `packageNames` in method `clearLaunchParamsForPackages` MissingNullability: android.app.ActivityTaskManager#resizeTask(int, android.graphics.Rect) parameter #1: - -MissingNullability: android.app.ActivityTaskManager#setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) parameter #4: - + Missing nullability on parameter `bounds` in method `resizeTask` MissingNullability: android.app.ActivityTaskManager#supportsMultiWindow(android.content.Context) parameter #0: - + Missing nullability on parameter `context` in method `supportsMultiWindow` MissingNullability: android.app.ActivityTaskManager#supportsSplitScreenMultiWindow(android.content.Context) parameter #0: - + Missing nullability on parameter `context` in method `supportsSplitScreenMultiWindow` MissingNullability: android.app.AppDetailsActivity#onCreate(android.os.Bundle) parameter #0: - -MissingNullability: android.app.AppOpsManager#getOpStrs(): - + Missing nullability on parameter `savedInstanceState` in method `onCreate` MissingNullability: android.app.AppOpsManager#isOperationActive(int, int, String) parameter #2: - + Missing nullability on parameter `packageName` in method `isOperationActive` MissingNullability: android.app.AppOpsManager#opToPermission(int): - + Missing nullability on method `opToPermission` return MissingNullability: android.app.AppOpsManager#permissionToOpCode(String) parameter #0: - -MissingNullability: android.app.AppOpsManager#setMode(String, int, String, int) parameter #0: - -MissingNullability: android.app.AppOpsManager#setMode(String, int, String, int) parameter #2: - + Missing nullability on parameter `permission` in method `permissionToOpCode` MissingNullability: android.app.AppOpsManager#setMode(int, int, String, int) parameter #2: - -MissingNullability: android.app.AppOpsManager#setUidMode(String, int, int) parameter #0: - -MissingNullability: android.app.AppOpsManager.HistoricalOp#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.app.AppOpsManager.HistoricalOps#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.app.AppOpsManager.HistoricalUidOps#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.app.AppOpsManager.OpEntry#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `packageName` in method `setMode` MissingNullability: android.app.NotificationManager#allowAssistantAdjustment(String) parameter #0: - + Missing nullability on parameter `capability` in method `allowAssistantAdjustment` MissingNullability: android.app.NotificationManager#disallowAssistantAdjustment(String) parameter #0: - + Missing nullability on parameter `capability` in method `disallowAssistantAdjustment` MissingNullability: android.app.NotificationManager#getEffectsSuppressor(): - -MissingNullability: android.app.NotificationManager#matchesCallFilter(android.os.Bundle) parameter #0: - -MissingNullability: android.app.PictureInPictureParams#getActions(): - -MissingNullability: android.app.PictureInPictureParams#getSourceRectHint(): - + Missing nullability on method `getEffectsSuppressor` return MissingNullability: android.app.TimePickerDialog#getTimePicker(): - -MissingNullability: android.app.UiAutomation#executeShellCommandRw(String): - -MissingNullability: android.app.UiAutomation#executeShellCommandRw(String) parameter #0: - -MissingNullability: android.app.UiAutomation#grantRuntimePermission(String, String, android.os.UserHandle) parameter #0: - -MissingNullability: android.app.UiAutomation#grantRuntimePermission(String, String, android.os.UserHandle) parameter #1: - -MissingNullability: android.app.UiAutomation#grantRuntimePermission(String, String, android.os.UserHandle) parameter #2: - -MissingNullability: android.app.UiAutomation#revokeRuntimePermission(String, String, android.os.UserHandle) parameter #0: - -MissingNullability: android.app.UiAutomation#revokeRuntimePermission(String, String, android.os.UserHandle) parameter #1: - -MissingNullability: android.app.UiAutomation#revokeRuntimePermission(String, String, android.os.UserHandle) parameter #2: - -MissingNullability: android.app.WallpaperManager#setWallpaperComponent(android.content.ComponentName) parameter #0: - + Missing nullability on method `getTimePicker` return MissingNullability: android.app.WindowConfiguration#compareTo(android.app.WindowConfiguration) parameter #0: - + Missing nullability on parameter `that` in method `compareTo` MissingNullability: android.app.WindowConfiguration#getAppBounds(): - + Missing nullability on method `getAppBounds` return MissingNullability: android.app.WindowConfiguration#getBounds(): - + Missing nullability on method `getBounds` return MissingNullability: android.app.WindowConfiguration#setAppBounds(android.graphics.Rect) parameter #0: - + Missing nullability on parameter `rect` in method `setAppBounds` MissingNullability: android.app.WindowConfiguration#setBounds(android.graphics.Rect) parameter #0: - + Missing nullability on parameter `rect` in method `setBounds` MissingNullability: android.app.WindowConfiguration#setTo(android.app.WindowConfiguration) parameter #0: - + Missing nullability on parameter `other` in method `setTo` MissingNullability: android.app.WindowConfiguration#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `dest` in method `writeToParcel` MissingNullability: android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts(android.os.UserHandle): - + Missing nullability on method `getOwnerInstalledCaCerts` return MissingNullability: android.app.admin.SecurityLog.SecurityEvent#SecurityEvent(long, byte[]) parameter #1: - -MissingNullability: android.app.backup.BackupManager#getConfigurationIntent(String): - -MissingNullability: android.app.backup.BackupManager#getConfigurationIntent(String) parameter #0: - -MissingNullability: android.app.backup.BackupManager#getDataManagementIntent(String): - -MissingNullability: android.app.backup.BackupManager#getDataManagementIntent(String) parameter #0: - -MissingNullability: android.app.backup.BackupManager#getDestinationString(String): - -MissingNullability: android.app.backup.BackupManager#getDestinationString(String) parameter #0: - -MissingNullability: android.app.prediction.AppPredictionSessionId#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `data` in method `SecurityEvent` MissingNullability: android.app.prediction.AppPredictor#getSessionId(): - -MissingNullability: android.app.prediction.AppTarget#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.app.prediction.AppTargetEvent#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.app.prediction.AppTargetId#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on method `getSessionId` return MissingNullability: android.content.AutofillOptions#forWhitelistingItself(): - + Missing nullability on method `forWhitelistingItself` return MissingNullability: android.content.AutofillOptions#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `parcel` in method `writeToParcel` MissingNullability: android.content.ContentCaptureOptions#forWhitelistingItself(): - + Missing nullability on method `forWhitelistingItself` return MissingNullability: android.content.ContentCaptureOptions#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `parcel` in method `writeToParcel` MissingNullability: android.content.ContentResolver#getSyncAdapterPackagesForAuthorityAsUser(String, int): - + Missing nullability on method `getSyncAdapterPackagesForAuthorityAsUser` return MissingNullability: android.content.ContentResolver#getSyncAdapterPackagesForAuthorityAsUser(String, int) parameter #0: - -MissingNullability: android.content.Context#getDisplay(): - -MissingNullability: android.content.Context#getUser(): - -MissingNullability: android.content.ContextWrapper#getDisplay(): - -MissingNullability: android.content.ContextWrapper#setContentCaptureOptions(android.content.ContentCaptureOptions) parameter #0: - + Missing nullability on parameter `authority` in method `getSyncAdapterPackagesForAuthorityAsUser` MissingNullability: android.content.pm.ActivityInfo#isTranslucentOrFloating(android.content.res.TypedArray) parameter #0: - + Missing nullability on parameter `attributes` in method `isTranslucentOrFloating` MissingNullability: android.content.pm.LauncherApps#LauncherApps(android.content.Context) parameter #0: - -MissingNullability: android.content.pm.PackageInstaller.SessionParams#setGrantedRuntimePermissions(String[]) parameter #0: - + Missing nullability on parameter `context` in method `LauncherApps` MissingNullability: android.content.pm.PackageManager#getHoldLockToken(): - Missing nullability on method `BINDER` return + Missing nullability on method `getHoldLockToken` return MissingNullability: android.content.pm.PackageManager#getNamesForUids(int[]) parameter #0: - + Missing nullability on parameter `uids` in method `getNamesForUids` MissingNullability: android.content.pm.PackageManager#holdLock(android.os.IBinder, int) parameter #0: - + Missing nullability on parameter `token` in method `holdLock` MissingNullability: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context) parameter #0: - + Missing nullability on parameter `context` in method `ShortcutManager` MissingNullability: android.content.pm.UserInfo#UserInfo(android.content.pm.UserInfo) parameter #0: Missing nullability on parameter `orig` in method `UserInfo` MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #1: @@ -942,1469 +292,477 @@ MissingNullability: android.content.pm.UserInfo#userType: MissingNullability: android.content.pm.UserInfo#writeToParcel(android.os.Parcel, int) parameter #0: Missing nullability on parameter `dest` in method `writeToParcel` MissingNullability: android.content.res.AssetManager#getOverlayablesToString(String) parameter #0: - + Missing nullability on parameter `packageName` in method `getOverlayablesToString` MissingNullability: android.content.res.Configuration#windowConfiguration: - -MissingNullability: android.content.rollback.PackageRollbackInfo#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.content.rollback.RollbackInfo#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on field `windowConfiguration` in class `class android.content.res.Configuration` MissingNullability: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]) parameter #0: - + Missing nullability on parameter `printer` in method `dump` MissingNullability: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]) parameter #1: - + Missing nullability on parameter `args` in method `dump` MissingNullability: android.database.sqlite.SQLiteDebug#getDatabaseInfo(): - + Missing nullability on method `getDatabaseInfo` return MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#DbStats(String, long, long, int, int, int, int) parameter #0: - + Missing nullability on parameter `dbName` in method `DbStats` MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#cache: - + Missing nullability on field `cache` in class `class android.database.sqlite.SQLiteDebug.DbStats` MissingNullability: android.database.sqlite.SQLiteDebug.DbStats#dbName: - + Missing nullability on field `dbName` in class `class android.database.sqlite.SQLiteDebug.DbStats` MissingNullability: android.database.sqlite.SQLiteDebug.PagerStats#dbStats: - + Missing nullability on field `dbStats` in class `class android.database.sqlite.SQLiteDebug.PagerStats` MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #0: - + Missing nullability on parameter `db` in method `SQLiteDirectCursorDriver` MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #1: - + Missing nullability on parameter `sql` in method `SQLiteDirectCursorDriver` MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #2: - + Missing nullability on parameter `editTable` in method `SQLiteDirectCursorDriver` MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#SQLiteDirectCursorDriver(android.database.sqlite.SQLiteDatabase, String, String, android.os.CancellationSignal) parameter #3: - + Missing nullability on parameter `cancellationSignal` in method `SQLiteDirectCursorDriver` MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#cursorRequeried(android.database.Cursor) parameter #0: - + Missing nullability on parameter `cursor` in method `cursorRequeried` MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]): - + Missing nullability on method `query` return MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]) parameter #0: - + Missing nullability on parameter `factory` in method `query` MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]) parameter #1: - + Missing nullability on parameter `selectionArgs` in method `query` MissingNullability: android.database.sqlite.SQLiteDirectCursorDriver#setBindArguments(String[]) parameter #0: - + Missing nullability on parameter `bindArgs` in method `setBindArguments` MissingNullability: android.database.sqlite.SQLiteGlobal#getDefaultJournalMode(): - + Missing nullability on method `getDefaultJournalMode` return MissingNullability: android.database.sqlite.SQLiteGlobal#getDefaultSyncMode(): - + Missing nullability on method `getDefaultSyncMode` return MissingNullability: android.database.sqlite.SQLiteGlobal#getWALSyncMode(): - + Missing nullability on method `getWALSyncMode` return MissingNullability: android.graphics.ImageDecoder#createSource(android.content.res.Resources, java.io.InputStream, int) parameter #0: - -MissingNullability: android.graphics.ImageDecoder#createSource(android.content.res.Resources, java.io.InputStream, int) parameter #1: - + Missing nullability on parameter `res` in method `createSource` MissingNullability: android.graphics.drawable.AdaptiveIconDrawable#getSafeZone(): - + Missing nullability on method `getSafeZone` return MissingNullability: android.graphics.drawable.ColorDrawable#getXfermode(): - -MissingNullability: android.hardware.camera2.CameraDevice#createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) parameter #0: - + Missing nullability on method `getXfermode` return MissingNullability: android.hardware.camera2.CameraManager#getCameraIdListNoLazy(): - -MissingNullability: android.hardware.display.AmbientBrightnessDayStats#getBucketBoundaries(): - -MissingNullability: android.hardware.display.AmbientBrightnessDayStats#getLocalDate(): - -MissingNullability: android.hardware.display.AmbientBrightnessDayStats#getStats(): - -MissingNullability: android.hardware.display.AmbientBrightnessDayStats#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on method `getCameraIdListNoLazy` return MissingNullability: android.hardware.display.AmbientDisplayConfiguration#AmbientDisplayConfiguration(android.content.Context) parameter #0: - -MissingNullability: android.hardware.display.BrightnessChangeEvent#luxTimestamps: - -MissingNullability: android.hardware.display.BrightnessChangeEvent#luxValues: - -MissingNullability: android.hardware.display.BrightnessChangeEvent#packageName: - -MissingNullability: android.hardware.display.BrightnessChangeEvent#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.hardware.display.BrightnessConfiguration#getCurve(): - -MissingNullability: android.hardware.display.BrightnessConfiguration#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.hardware.display.BrightnessConfiguration.Builder#Builder(float[], float[]) parameter #0: - -MissingNullability: android.hardware.display.BrightnessConfiguration.Builder#Builder(float[], float[]) parameter #1: - -MissingNullability: android.hardware.display.BrightnessCorrection#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.hardware.display.DisplayManager#getAmbientBrightnessStats(): - -MissingNullability: android.hardware.display.DisplayManager#getBrightnessConfiguration(): - -MissingNullability: android.hardware.display.DisplayManager#getBrightnessEvents(): - -MissingNullability: android.hardware.display.DisplayManager#getStableDisplaySize(): - -MissingNullability: android.hardware.display.DisplayManager#setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration) parameter #0: - + Missing nullability on parameter `context` in method `AmbientDisplayConfiguration` MissingNullability: android.location.GnssClock#set(android.location.GnssClock) parameter #0: - + Missing nullability on parameter `clock` in method `set` MissingNullability: android.location.GnssMeasurement#set(android.location.GnssMeasurement) parameter #0: - -MissingNullability: android.location.GnssMeasurementsEvent#GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]) parameter #0: - -MissingNullability: android.location.GnssMeasurementsEvent#GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]) parameter #1: - + Missing nullability on parameter `measurement` in method `set` MissingNullability: android.location.GnssNavigationMessage#set(android.location.GnssNavigationMessage) parameter #0: - + Missing nullability on parameter `navigationMessage` in method `set` MissingNullability: android.location.GnssNavigationMessage#setData(byte[]) parameter #0: - -MissingNullability: android.location.LocationManager#getTestProviderCurrentRequests(String) parameter #0: - -MissingNullability: android.location.LocationRequest#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.media.AudioAttributes#SDK_USAGES: - Missing nullability on field `SDK_USAGES` in class `class android.media.AudioAttributes` + Missing nullability on parameter `value` in method `setData` MissingNullability: android.media.AudioAttributes#getSdkUsages(): Missing nullability on method `getSdkUsages` return -MissingNullability: android.media.AudioFocusInfo#writeToParcel(android.os.Parcel, int) parameter #0: - MissingNullability: android.media.AudioManager#getPublicStreamTypes(): Missing nullability on method `getPublicStreamTypes` return MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #3: - + Missing nullability on parameter `clientFormat` in method `AudioRecordingConfiguration` MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #4: - + Missing nullability on parameter `devFormat` in method `AudioRecordingConfiguration` MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #6: - + Missing nullability on parameter `packageName` in method `AudioRecordingConfiguration` MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10: - + Missing nullability on parameter `clientEffects` in method `AudioRecordingConfiguration` MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #11: - + Missing nullability on parameter `deviceEffects` in method `AudioRecordingConfiguration` MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #3: - + Missing nullability on parameter `clientFormat` in method `AudioRecordingConfiguration` MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #4: - + Missing nullability on parameter `devFormat` in method `AudioRecordingConfiguration` MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #6: - -MissingNullability: android.media.AudioSystem#streamToString(int): - Missing nullability on method `streamToString` return + Missing nullability on parameter `packageName` in method `AudioRecordingConfiguration` MissingNullability: android.media.PlaybackParams#setAudioStretchMode(int): - + Missing nullability on method `setAudioStretchMode` return MissingNullability: android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL: - + Missing nullability on field `EFFECT_TYPE_NULL` in class `class android.media.audiofx.AudioEffect` MissingNullability: android.media.audiofx.AudioEffect#byteArrayToInt(byte[]) parameter #0: - + Missing nullability on parameter `valueBuf` in method `byteArrayToInt` MissingNullability: android.media.audiofx.AudioEffect#byteArrayToShort(byte[]) parameter #0: - + Missing nullability on parameter `valueBuf` in method `byteArrayToShort` MissingNullability: android.media.audiofx.AudioEffect#getParameter(byte[], byte[]) parameter #0: - + Missing nullability on parameter `param` in method `getParameter` MissingNullability: android.media.audiofx.AudioEffect#getParameter(byte[], byte[]) parameter #1: - + Missing nullability on parameter `value` in method `getParameter` MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, byte[]) parameter #1: - + Missing nullability on parameter `value` in method `getParameter` MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, int[]) parameter #1: - + Missing nullability on parameter `value` in method `getParameter` MissingNullability: android.media.audiofx.AudioEffect#getParameter(int, short[]) parameter #1: - + Missing nullability on parameter `value` in method `getParameter` MissingNullability: android.media.audiofx.AudioEffect#getParameter(int[], short[]) parameter #0: - + Missing nullability on parameter `param` in method `getParameter` MissingNullability: android.media.audiofx.AudioEffect#getParameter(int[], short[]) parameter #1: - + Missing nullability on parameter `value` in method `getParameter` MissingNullability: android.media.audiofx.AudioEffect#intToByteArray(int): - + Missing nullability on method `intToByteArray` return MissingNullability: android.media.audiofx.AudioEffect#isEffectTypeAvailable(java.util.UUID) parameter #0: - + Missing nullability on parameter `type` in method `isEffectTypeAvailable` MissingNullability: android.media.audiofx.AudioEffect#setParameter(byte[], byte[]) parameter #0: - + Missing nullability on parameter `param` in method `setParameter` MissingNullability: android.media.audiofx.AudioEffect#setParameter(byte[], byte[]) parameter #1: - + Missing nullability on parameter `value` in method `setParameter` MissingNullability: android.media.audiofx.AudioEffect#setParameter(int, byte[]) parameter #1: - + Missing nullability on parameter `value` in method `setParameter` MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], byte[]) parameter #0: - + Missing nullability on parameter `param` in method `setParameter` MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], byte[]) parameter #1: - + Missing nullability on parameter `value` in method `setParameter` MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], int[]) parameter #0: - + Missing nullability on parameter `param` in method `setParameter` MissingNullability: android.media.audiofx.AudioEffect#setParameter(int[], int[]) parameter #1: - + Missing nullability on parameter `value` in method `setParameter` MissingNullability: android.media.audiofx.AudioEffect#setParameterListener(android.media.audiofx.AudioEffect.OnParameterChangeListener) parameter #0: - + Missing nullability on parameter `listener` in method `setParameterListener` MissingNullability: android.media.audiofx.AudioEffect#shortToByteArray(short): - + Missing nullability on method `shortToByteArray` return MissingNullability: android.media.audiofx.AudioEffect.Descriptor#Descriptor(android.os.Parcel) parameter #0: - + Missing nullability on parameter `in` in method `Descriptor` MissingNullability: android.media.audiofx.AudioEffect.Descriptor#writeToParcel(android.os.Parcel) parameter #0: - + Missing nullability on parameter `dest` in method `writeToParcel` MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #0: - + Missing nullability on parameter `effect` in method `onParameterChange` MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #2: - + Missing nullability on parameter `param` in method `onParameterChange` MissingNullability: android.media.audiofx.AudioEffect.OnParameterChangeListener#onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]) parameter #3: - -MissingNullability: android.media.audiopolicy.AudioMix.Builder#Builder(android.media.audiopolicy.AudioMixingRule) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioMix.Builder#build(): - -MissingNullability: android.media.audiopolicy.AudioMix.Builder#setDevice(android.media.AudioDeviceInfo): - -MissingNullability: android.media.audiopolicy.AudioMix.Builder#setFormat(android.media.AudioFormat): - -MissingNullability: android.media.audiopolicy.AudioMix.Builder#setFormat(android.media.AudioFormat) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioMix.Builder#setRouteFlags(int): - -MissingNullability: android.media.audiopolicy.AudioMixingRule.Builder#addMixRule(int, Object): - -MissingNullability: android.media.audiopolicy.AudioMixingRule.Builder#addMixRule(int, Object) parameter #1: - -MissingNullability: android.media.audiopolicy.AudioMixingRule.Builder#addRule(android.media.AudioAttributes, int): - -MissingNullability: android.media.audiopolicy.AudioMixingRule.Builder#addRule(android.media.AudioAttributes, int) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioMixingRule.Builder#build(): - -MissingNullability: android.media.audiopolicy.AudioMixingRule.Builder#excludeMixRule(int, Object): - -MissingNullability: android.media.audiopolicy.AudioMixingRule.Builder#excludeMixRule(int, Object) parameter #1: - -MissingNullability: android.media.audiopolicy.AudioMixingRule.Builder#excludeRule(android.media.AudioAttributes, int): - -MissingNullability: android.media.audiopolicy.AudioMixingRule.Builder#excludeRule(android.media.AudioAttributes, int) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioPolicy#createAudioRecordSink(android.media.audiopolicy.AudioMix): - -MissingNullability: android.media.audiopolicy.AudioPolicy#createAudioRecordSink(android.media.audiopolicy.AudioMix) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioPolicy#createAudioTrackSource(android.media.audiopolicy.AudioMix): - -MissingNullability: android.media.audiopolicy.AudioPolicy#createAudioTrackSource(android.media.audiopolicy.AudioMix) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioPolicy#setRegistration(String) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioPolicy#toLogFriendlyString(): - -MissingNullability: android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener#onAudioFocusAbandon(android.media.AudioFocusInfo) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener#onAudioFocusGrant(android.media.AudioFocusInfo, int) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener#onAudioFocusLoss(android.media.AudioFocusInfo, boolean) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener#onAudioFocusRequest(android.media.AudioFocusInfo, int) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener#onMixStateUpdate(android.media.audiopolicy.AudioMix) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioPolicy.Builder#Builder(android.content.Context) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener) parameter #0: - -MissingNullability: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener) parameter #0: - -MissingNullability: android.metrics.LogMaker#LogMaker(Object[]) parameter #0: - -MissingNullability: android.metrics.LogMaker#addTaggedData(int, Object): - -MissingNullability: android.metrics.LogMaker#addTaggedData(int, Object) parameter #1: - -MissingNullability: android.metrics.LogMaker#clearCategory(): - -MissingNullability: android.metrics.LogMaker#clearPackageName(): - -MissingNullability: android.metrics.LogMaker#clearSubtype(): - -MissingNullability: android.metrics.LogMaker#clearTaggedData(int): - -MissingNullability: android.metrics.LogMaker#clearType(): - -MissingNullability: android.metrics.LogMaker#deserialize(Object[]) parameter #0: - -MissingNullability: android.metrics.LogMaker#getCounterName(): - -MissingNullability: android.metrics.LogMaker#getPackageName(): - -MissingNullability: android.metrics.LogMaker#getTaggedData(int): - -MissingNullability: android.metrics.LogMaker#isSubsetOf(android.metrics.LogMaker) parameter #0: - -MissingNullability: android.metrics.LogMaker#isValidValue(Object) parameter #0: - -MissingNullability: android.metrics.LogMaker#serialize(): - -MissingNullability: android.metrics.LogMaker#setCategory(int): - -MissingNullability: android.metrics.LogMaker#setPackageName(String): - -MissingNullability: android.metrics.LogMaker#setPackageName(String) parameter #0: - -MissingNullability: android.metrics.LogMaker#setSubtype(int): - -MissingNullability: android.metrics.LogMaker#setType(int): - -MissingNullability: android.metrics.MetricsReader#next(): - -MissingNullability: android.net.NetworkCapabilities#getCapabilities(): - -MissingNullability: android.net.StaticIpConfiguration#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.net.TestNetworkInterface#CREATOR: - -MissingNullability: android.net.TestNetworkInterface#TestNetworkInterface(android.os.ParcelFileDescriptor, String) parameter #0: - -MissingNullability: android.net.TestNetworkInterface#TestNetworkInterface(android.os.ParcelFileDescriptor, String) parameter #1: - -MissingNullability: android.net.TestNetworkInterface#getFileDescriptor(): - -MissingNullability: android.net.TestNetworkInterface#getInterfaceName(): - -MissingNullability: android.net.TestNetworkInterface#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.net.TestNetworkManager#createTapInterface(): - -MissingNullability: android.net.TestNetworkManager#createTunInterface(android.net.LinkAddress[]): - -MissingNullability: android.net.apf.ApfCapabilities#CREATOR: - -MissingNullability: android.net.apf.ApfCapabilities#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.net.metrics.DhcpClientEvent.Builder#setMsg(String) parameter #0: - + Missing nullability on parameter `value` in method `onParameterChange` MissingNullability: android.os.Build#is64BitAbi(String) parameter #0: - + Missing nullability on parameter `abi` in method `is64BitAbi` MissingNullability: android.os.Build.VERSION#ACTIVE_CODENAMES: - + Missing nullability on field `ACTIVE_CODENAMES` in class `class android.os.Build.VERSION` MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...): - + Missing nullability on method `buildPath` return MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...) parameter #0: - + Missing nullability on parameter `base` in method `buildPath` MissingNullability: android.os.Environment#buildPath(java.io.File, java.lang.String...) parameter #1: - + Missing nullability on parameter `segments` in method `buildPath` MissingNullability: android.os.FileUtils#contains(java.io.File, java.io.File) parameter #0: - + Missing nullability on parameter `dir` in method `contains` MissingNullability: android.os.FileUtils#contains(java.io.File, java.io.File) parameter #1: - -MissingNullability: android.os.HwBinder#getService(String, String): - -MissingNullability: android.os.HwBinder#getService(String, String) parameter #0: - -MissingNullability: android.os.HwBinder#getService(String, String) parameter #1: - -MissingNullability: android.os.HwBinder#getService(String, String, boolean): - -MissingNullability: android.os.HwBinder#getService(String, String, boolean) parameter #0: - -MissingNullability: android.os.HwBinder#getService(String, String, boolean) parameter #1: - -MissingNullability: android.os.HwBinder#onTransact(int, android.os.HwParcel, android.os.HwParcel, int) parameter #1: - -MissingNullability: android.os.HwBinder#onTransact(int, android.os.HwParcel, android.os.HwParcel, int) parameter #2: - -MissingNullability: android.os.HwBinder#registerService(String) parameter #0: - -MissingNullability: android.os.HwBinder#transact(int, android.os.HwParcel, android.os.HwParcel, int) parameter #1: - -MissingNullability: android.os.HwBinder#transact(int, android.os.HwParcel, android.os.HwParcel, int) parameter #2: - -MissingNullability: android.os.HwBlob#copyToBoolArray(long, boolean[], int) parameter #1: - -MissingNullability: android.os.HwBlob#copyToDoubleArray(long, double[], int) parameter #1: - -MissingNullability: android.os.HwBlob#copyToFloatArray(long, float[], int) parameter #1: - -MissingNullability: android.os.HwBlob#copyToInt16Array(long, short[], int) parameter #1: - -MissingNullability: android.os.HwBlob#copyToInt32Array(long, int[], int) parameter #1: - -MissingNullability: android.os.HwBlob#copyToInt64Array(long, long[], int) parameter #1: - -MissingNullability: android.os.HwBlob#copyToInt8Array(long, byte[], int) parameter #1: - -MissingNullability: android.os.HwBlob#getString(long): - -MissingNullability: android.os.HwBlob#putBlob(long, android.os.HwBlob) parameter #1: - -MissingNullability: android.os.HwBlob#putBoolArray(long, boolean[]) parameter #1: - -MissingNullability: android.os.HwBlob#putDoubleArray(long, double[]) parameter #1: - -MissingNullability: android.os.HwBlob#putFloatArray(long, float[]) parameter #1: - -MissingNullability: android.os.HwBlob#putInt16Array(long, short[]) parameter #1: - -MissingNullability: android.os.HwBlob#putInt32Array(long, int[]) parameter #1: - -MissingNullability: android.os.HwBlob#putInt64Array(long, long[]) parameter #1: - -MissingNullability: android.os.HwBlob#putInt8Array(long, byte[]) parameter #1: - -MissingNullability: android.os.HwBlob#putString(long, String) parameter #1: - -MissingNullability: android.os.HwBlob#wrapArray(boolean[]): - -MissingNullability: android.os.HwBlob#wrapArray(byte[]): - -MissingNullability: android.os.HwBlob#wrapArray(double[]): - -MissingNullability: android.os.HwBlob#wrapArray(float[]): - -MissingNullability: android.os.HwBlob#wrapArray(int[]): - -MissingNullability: android.os.HwBlob#wrapArray(long[]): - -MissingNullability: android.os.HwBlob#wrapArray(short[]): - -MissingNullability: android.os.HwParcel#enforceInterface(String) parameter #0: - -MissingNullability: android.os.HwParcel#readBoolVector(): - -MissingNullability: android.os.HwParcel#readBuffer(long): - -MissingNullability: android.os.HwParcel#readDoubleVector(): - -MissingNullability: android.os.HwParcel#readEmbeddedBuffer(long, long, long, boolean): - -MissingNullability: android.os.HwParcel#readFloatVector(): - -MissingNullability: android.os.HwParcel#readInt16Vector(): - -MissingNullability: android.os.HwParcel#readInt32Vector(): - -MissingNullability: android.os.HwParcel#readInt64Vector(): - -MissingNullability: android.os.HwParcel#readInt8Vector(): - -MissingNullability: android.os.HwParcel#readString(): - -MissingNullability: android.os.HwParcel#readStringVector(): - -MissingNullability: android.os.HwParcel#readStrongBinder(): - -MissingNullability: android.os.HwParcel#writeBoolVector(java.util.ArrayList<java.lang.Boolean>) parameter #0: - -MissingNullability: android.os.HwParcel#writeBuffer(android.os.HwBlob) parameter #0: - -MissingNullability: android.os.HwParcel#writeDoubleVector(java.util.ArrayList<java.lang.Double>) parameter #0: - -MissingNullability: android.os.HwParcel#writeFloatVector(java.util.ArrayList<java.lang.Float>) parameter #0: - -MissingNullability: android.os.HwParcel#writeInt16Vector(java.util.ArrayList<java.lang.Short>) parameter #0: - -MissingNullability: android.os.HwParcel#writeInt32Vector(java.util.ArrayList<java.lang.Integer>) parameter #0: - -MissingNullability: android.os.HwParcel#writeInt64Vector(java.util.ArrayList<java.lang.Long>) parameter #0: - -MissingNullability: android.os.HwParcel#writeInt8Vector(java.util.ArrayList<java.lang.Byte>) parameter #0: - -MissingNullability: android.os.HwParcel#writeInterfaceToken(String) parameter #0: - -MissingNullability: android.os.HwParcel#writeString(String) parameter #0: - -MissingNullability: android.os.HwParcel#writeStringVector(java.util.ArrayList<java.lang.String>) parameter #0: - -MissingNullability: android.os.HwParcel#writeStrongBinder(android.os.IHwBinder) parameter #0: - -MissingNullability: android.os.IHwBinder#linkToDeath(android.os.IHwBinder.DeathRecipient, long) parameter #0: - -MissingNullability: android.os.IHwBinder#queryLocalInterface(String): - -MissingNullability: android.os.IHwBinder#queryLocalInterface(String) parameter #0: - -MissingNullability: android.os.IHwBinder#transact(int, android.os.HwParcel, android.os.HwParcel, int) parameter #1: - -MissingNullability: android.os.IHwBinder#transact(int, android.os.HwParcel, android.os.HwParcel, int) parameter #2: - -MissingNullability: android.os.IHwBinder#unlinkToDeath(android.os.IHwBinder.DeathRecipient) parameter #0: - -MissingNullability: android.os.IHwInterface#asBinder(): - -MissingNullability: android.os.IncidentManager#approveReport(android.net.Uri) parameter #0: - -MissingNullability: android.os.IncidentManager#cancelAuthorization(android.os.IncidentManager.AuthListener) parameter #0: - -MissingNullability: android.os.IncidentManager#deleteIncidentReports(android.net.Uri) parameter #0: - -MissingNullability: android.os.IncidentManager#denyReport(android.net.Uri) parameter #0: - -MissingNullability: android.os.IncidentManager#getIncidentReport(android.net.Uri) parameter #0: - -MissingNullability: android.os.IncidentManager#getIncidentReportList(String) parameter #0: - -MissingNullability: android.os.IncidentManager#getPendingReports(): - -MissingNullability: android.os.IncidentManager#reportIncident(android.os.IncidentReportArgs) parameter #0: - -MissingNullability: android.os.IncidentManager#requestAuthorization(int, String, int, android.os.IncidentManager.AuthListener) parameter #1: - -MissingNullability: android.os.IncidentManager#requestAuthorization(int, String, int, android.os.IncidentManager.AuthListener) parameter #3: - -MissingNullability: android.os.IncidentManager.IncidentReport#IncidentReport(android.os.Parcel) parameter #0: - -MissingNullability: android.os.IncidentManager.IncidentReport#getInputStream(): - -MissingNullability: android.os.IncidentManager.IncidentReport#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.os.IncidentReportArgs#IncidentReportArgs(android.os.Parcel) parameter #0: - -MissingNullability: android.os.IncidentReportArgs#addHeader(byte[]) parameter #0: - -MissingNullability: android.os.IncidentReportArgs#readFromParcel(android.os.Parcel) parameter #0: - -MissingNullability: android.os.IncidentReportArgs#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `file` in method `contains` MissingNullability: android.os.ParcelFileDescriptor#getFile(java.io.FileDescriptor): - + Missing nullability on method `getFile` return MissingNullability: android.os.ParcelFileDescriptor#getFile(java.io.FileDescriptor) parameter #0: - -MissingNullability: android.os.RemoteCallback#RemoteCallback(android.os.RemoteCallback.OnResultListener) parameter #0: - -MissingNullability: android.os.RemoteCallback#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `fd` in method `getFile` MissingNullability: android.os.StrictMode#setViolationLogger(android.os.StrictMode.ViolationLogger) parameter #0: - + Missing nullability on parameter `listener` in method `setViolationLogger` MissingNullability: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel) parameter #0: - + Missing nullability on parameter `in` in method `ViolationInfo` MissingNullability: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel, boolean) parameter #0: - + Missing nullability on parameter `in` in method `ViolationInfo` MissingNullability: android.os.StrictMode.ViolationInfo#broadcastIntentAction: - + Missing nullability on field `broadcastIntentAction` in class `class android.os.StrictMode.ViolationInfo` MissingNullability: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String) parameter #0: - + Missing nullability on parameter `pw` in method `dump` MissingNullability: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String) parameter #1: - + Missing nullability on parameter `prefix` in method `dump` MissingNullability: android.os.StrictMode.ViolationInfo#getStackTrace(): - + Missing nullability on method `getStackTrace` return MissingNullability: android.os.StrictMode.ViolationInfo#getViolationClass(): - + Missing nullability on method `getViolationClass` return MissingNullability: android.os.StrictMode.ViolationInfo#getViolationDetails(): - + Missing nullability on method `getViolationDetails` return MissingNullability: android.os.StrictMode.ViolationInfo#tags: - + Missing nullability on field `tags` in class `class android.os.StrictMode.ViolationInfo` MissingNullability: android.os.StrictMode.ViolationInfo#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `dest` in method `writeToParcel` MissingNullability: android.os.StrictMode.ViolationLogger#log(android.os.StrictMode.ViolationInfo) parameter #0: - -MissingNullability: android.os.UserHandle#of(int): - + Missing nullability on parameter `info` in method `log` MissingNullability: android.os.VibrationEffect#RINGTONES: - + Missing nullability on field `RINGTONES` in class `class android.os.VibrationEffect` MissingNullability: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #0: - + Missing nullability on parameter `uri` in method `get` MissingNullability: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1: - + Missing nullability on parameter `context` in method `get` MissingNullability: android.os.VibrationEffect#get(int): - + Missing nullability on method `get` return MissingNullability: android.os.VibrationEffect#get(int, boolean): - + Missing nullability on method `get` return MissingNullability: android.os.VintfObject#getHalNamesAndVersions(): - + Missing nullability on method `getHalNamesAndVersions` return MissingNullability: android.os.VintfObject#getSepolicyVersion(): - + Missing nullability on method `getSepolicyVersion` return MissingNullability: android.os.VintfObject#getTargetFrameworkCompatibilityMatrixVersion(): - + Missing nullability on method `getTargetFrameworkCompatibilityMatrixVersion` return MissingNullability: android.os.VintfObject#getVndkSnapshots(): - + Missing nullability on method `getVndkSnapshots` return MissingNullability: android.os.VintfObject#report(): - + Missing nullability on method `report` return MissingNullability: android.os.VintfRuntimeInfo#getCpuInfo(): - + Missing nullability on method `getCpuInfo` return MissingNullability: android.os.VintfRuntimeInfo#getHardwareId(): - + Missing nullability on method `getHardwareId` return MissingNullability: android.os.VintfRuntimeInfo#getKernelVersion(): - + Missing nullability on method `getKernelVersion` return MissingNullability: android.os.VintfRuntimeInfo#getNodeName(): - + Missing nullability on method `getNodeName` return MissingNullability: android.os.VintfRuntimeInfo#getOsName(): - + Missing nullability on method `getOsName` return MissingNullability: android.os.VintfRuntimeInfo#getOsRelease(): - + Missing nullability on method `getOsRelease` return MissingNullability: android.os.VintfRuntimeInfo#getOsVersion(): - + Missing nullability on method `getOsVersion` return MissingNullability: android.os.WorkSource#add(int, String) parameter #1: - -MissingNullability: android.os.WorkSource#addReturningNewbs(android.os.WorkSource) parameter #0: - -MissingNullability: android.os.WorkSource#getName(int): - -MissingNullability: android.os.WorkSource#setReturningDiffs(android.os.WorkSource) parameter #0: - + Missing nullability on parameter `name` in method `add` MissingNullability: android.os.health.HealthKeys.Constants#Constants(Class) parameter #0: - + Missing nullability on parameter `clazz` in method `Constants` MissingNullability: android.os.health.HealthKeys.Constants#getDataType(): - + Missing nullability on method `getDataType` return MissingNullability: android.os.health.HealthKeys.Constants#getKeys(int): - + Missing nullability on method `getKeys` return MissingNullability: android.os.health.HealthStats#HealthStats(android.os.Parcel) parameter #0: - + Missing nullability on parameter `in` in method `HealthStats` MissingNullability: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.Parcel) parameter #0: - + Missing nullability on parameter `in` in method `HealthStatsParceler` MissingNullability: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.health.HealthStatsWriter) parameter #0: - + Missing nullability on parameter `writer` in method `HealthStatsParceler` MissingNullability: android.os.health.HealthStatsParceler#getHealthStats(): - + Missing nullability on method `getHealthStats` return MissingNullability: android.os.health.HealthStatsParceler#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `out` in method `writeToParcel` MissingNullability: android.os.health.HealthStatsWriter#HealthStatsWriter(android.os.health.HealthKeys.Constants) parameter #0: - + Missing nullability on parameter `constants` in method `HealthStatsWriter` MissingNullability: android.os.health.HealthStatsWriter#addMeasurements(int, String, long) parameter #1: - + Missing nullability on parameter `name` in method `addMeasurements` MissingNullability: android.os.health.HealthStatsWriter#addStats(int, String, android.os.health.HealthStatsWriter) parameter #1: - + Missing nullability on parameter `name` in method `addStats` MissingNullability: android.os.health.HealthStatsWriter#addStats(int, String, android.os.health.HealthStatsWriter) parameter #2: - + Missing nullability on parameter `value` in method `addStats` MissingNullability: android.os.health.HealthStatsWriter#addTimers(int, String, android.os.health.TimerStat) parameter #1: - + Missing nullability on parameter `name` in method `addTimers` MissingNullability: android.os.health.HealthStatsWriter#addTimers(int, String, android.os.health.TimerStat) parameter #2: - + Missing nullability on parameter `value` in method `addTimers` MissingNullability: android.os.health.HealthStatsWriter#flattenToParcel(android.os.Parcel) parameter #0: - + Missing nullability on parameter `out` in method `flattenToParcel` MissingNullability: android.os.storage.StorageVolume#getPath(): - -MissingNullability: android.permission.RuntimePermissionPresentationInfo#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on method `getPath` return MissingNullability: android.provider.CalendarContract.Calendars#SYNC_WRITABLE_COLUMNS: - + Missing nullability on field `SYNC_WRITABLE_COLUMNS` in class `class android.provider.CalendarContract.Calendars` MissingNullability: android.provider.CalendarContract.Events#SYNC_WRITABLE_COLUMNS: - -MissingNullability: android.provider.ContactsContract.CommonDataKinds.Phone#ENTERPRISE_CONTENT_URI: - + Missing nullability on field `SYNC_WRITABLE_COLUMNS` in class `class android.provider.CalendarContract.Events` MissingNullability: android.provider.ContactsContract.RawContactsEntity#CORP_CONTENT_URI: - -MissingNullability: android.provider.DeviceConfig#getProperty(String, String): - -MissingNullability: android.provider.DeviceConfig#getString(String, String, String): - -MissingNullability: android.provider.MediaStore#deleteContributedMedia(android.content.Context, String, android.os.UserHandle) parameter #0: - -MissingNullability: android.provider.MediaStore#deleteContributedMedia(android.content.Context, String, android.os.UserHandle) parameter #1: - -MissingNullability: android.provider.MediaStore#deleteContributedMedia(android.content.Context, String, android.os.UserHandle) parameter #2: - -MissingNullability: android.provider.MediaStore#getContributedMediaSize(android.content.Context, String, android.os.UserHandle) parameter #0: - -MissingNullability: android.provider.MediaStore#getContributedMediaSize(android.content.Context, String, android.os.UserHandle) parameter #1: - -MissingNullability: android.provider.MediaStore#getContributedMediaSize(android.content.Context, String, android.os.UserHandle) parameter #2: - -MissingNullability: android.provider.MediaStore#scanFile(android.content.Context, java.io.File): - -MissingNullability: android.provider.MediaStore#scanFile(android.content.Context, java.io.File) parameter #0: - -MissingNullability: android.provider.MediaStore#scanFile(android.content.Context, java.io.File) parameter #1: - -MissingNullability: android.provider.MediaStore#scanFileFromShell(android.content.Context, java.io.File): - -MissingNullability: android.provider.MediaStore#scanFileFromShell(android.content.Context, java.io.File) parameter #0: - -MissingNullability: android.provider.MediaStore#scanFileFromShell(android.content.Context, java.io.File) parameter #1: - -MissingNullability: android.provider.MediaStore#scanVolume(android.content.Context, java.io.File) parameter #0: - -MissingNullability: android.provider.MediaStore#scanVolume(android.content.Context, java.io.File) parameter #1: - -MissingNullability: android.provider.MediaStore#waitForIdle(android.content.Context) parameter #0: - -MissingNullability: android.security.KeyStoreException#KeyStoreException(int, String) parameter #1: - -MissingNullability: android.security.keystore.AttestationUtils#attestDeviceIds(android.content.Context, int[], byte[]) parameter #0: - + Missing nullability on field `CORP_CONTENT_URI` in class `class android.provider.ContactsContract.RawContactsEntity` MissingNullability: android.security.keystore.KeyProtection.Builder#setBoundToSpecificSecureUserId(long): - -MissingNullability: android.service.autofill.AutofillFieldClassificationService#onBind(android.content.Intent): - -MissingNullability: android.service.autofill.AutofillFieldClassificationService#onBind(android.content.Intent) parameter #0: - + Missing nullability on method `setBoundToSpecificSecureUserId` return MissingNullability: android.service.autofill.CompositeUserData#getCategoryIds(): - + Missing nullability on method `getCategoryIds` return MissingNullability: android.service.autofill.CompositeUserData#getDefaultFieldClassificationArgs(): - + Missing nullability on method `getDefaultFieldClassificationArgs` return MissingNullability: android.service.autofill.CompositeUserData#getFieldClassificationAlgorithms(): - + Missing nullability on method `getFieldClassificationAlgorithms` return MissingNullability: android.service.autofill.CompositeUserData#getFieldClassificationArgs(): - + Missing nullability on method `getFieldClassificationArgs` return MissingNullability: android.service.autofill.CompositeUserData#getValues(): - + Missing nullability on method `getValues` return MissingNullability: android.service.autofill.CompositeUserData#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `parcel` in method `writeToParcel` MissingNullability: android.service.autofill.UserData#getFieldClassificationAlgorithms(): - -MissingNullability: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0: - -MissingNullability: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1: - -MissingNullability: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #2: - -MissingNullability: android.service.autofill.augmented.AugmentedAutofillService#onUnbind(android.content.Intent) parameter #0: - -MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0: - -MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1: - -MissingNullability: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #2: - -MissingNullability: android.service.notification.Adjustment#Adjustment(String, String, android.os.Bundle, CharSequence, int) parameter #0: - -MissingNullability: android.service.notification.Adjustment#Adjustment(String, String, android.os.Bundle, CharSequence, int) parameter #1: - -MissingNullability: android.service.notification.Adjustment#Adjustment(String, String, android.os.Bundle, CharSequence, int) parameter #2: - -MissingNullability: android.service.notification.Adjustment#Adjustment(String, String, android.os.Bundle, CharSequence, int) parameter #3: - -MissingNullability: android.service.notification.Adjustment#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context) parameter #0: - -MissingNullability: android.service.notification.NotificationStats#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.service.notification.SnoozeCriterion#SnoozeCriterion(String, CharSequence, CharSequence) parameter #0: - -MissingNullability: android.service.notification.SnoozeCriterion#SnoozeCriterion(String, CharSequence, CharSequence) parameter #1: - -MissingNullability: android.service.notification.SnoozeCriterion#SnoozeCriterion(String, CharSequence, CharSequence) parameter #2: - -MissingNullability: android.service.notification.SnoozeCriterion#SnoozeCriterion(android.os.Parcel) parameter #0: - -MissingNullability: android.service.notification.SnoozeCriterion#getConfirmation(): - -MissingNullability: android.service.notification.SnoozeCriterion#getExplanation(): - -MissingNullability: android.service.notification.SnoozeCriterion#getId(): - -MissingNullability: android.service.notification.SnoozeCriterion#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on method `getFieldClassificationAlgorithms` return MissingNullability: android.telecom.Call.Details#getTelecomCallId(): - -MissingNullability: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallFurther(boolean): - -MissingNullability: android.telecom.Conference#getPrimaryConnection(): - -MissingNullability: android.telecom.PhoneAccountSuggestionService#onBind(android.content.Intent): - -MissingNullability: android.telecom.PhoneAccountSuggestionService#onBind(android.content.Intent) parameter #0: - -MissingNullability: android.telephony.CallQuality#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.DataSpecificRegistrationInfo#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.LteVopsSupportInfo#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.NetworkRegistrationInfo#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on method `getTelecomCallId` return MissingNullability: android.telephony.ServiceState#addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo) parameter #0: - + Missing nullability on parameter `nri` in method `addNetworkRegistrationInfo` MissingNullability: android.telephony.ServiceState#setCellBandwidths(int[]) parameter #0: - + Missing nullability on parameter `bandwidths` in method `setCellBandwidths` MissingNullability: android.telephony.SmsManager#checkSmsShortCodeDestination(String, String) parameter #0: - + Missing nullability on parameter `destAddress` in method `checkSmsShortCodeDestination` MissingNullability: android.telephony.SmsManager#checkSmsShortCodeDestination(String, String) parameter #1: - -MissingNullability: android.telephony.TelephonyManager#checkCarrierPrivilegesForPackage(String) parameter #0: - -MissingNullability: android.telephony.TelephonyManager#getCarrierPackageNamesForIntent(android.content.Intent): - -MissingNullability: android.telephony.TelephonyManager#getCarrierPackageNamesForIntent(android.content.Intent) parameter #0: - + Missing nullability on parameter `countryIso` in method `checkSmsShortCodeDestination` MissingNullability: android.telephony.TelephonyManager#getLine1AlphaTag(): - + Missing nullability on method `getLine1AlphaTag` return MissingNullability: android.telephony.TelephonyManager#getRadioHalVersion(): - -MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String) parameter #0: - -MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String) parameter #1: - -MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String) parameter #2: - -MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String) parameter #3: - -MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String) parameter #4: - -MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String) parameter #5: - -MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String) parameter #6: - + Missing nullability on method `getRadioHalVersion` return MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #0: - + Missing nullability on parameter `mccmnc` in method `setCarrierTestOverride` MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #1: - + Missing nullability on parameter `imsi` in method `setCarrierTestOverride` MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #2: - + Missing nullability on parameter `iccid` in method `setCarrierTestOverride` MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #3: - + Missing nullability on parameter `gid1` in method `setCarrierTestOverride` MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #4: - + Missing nullability on parameter `gid2` in method `setCarrierTestOverride` MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #5: - + Missing nullability on parameter `plmn` in method `setCarrierTestOverride` MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #6: - + Missing nullability on parameter `spn` in method `setCarrierTestOverride` MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #7: - + Missing nullability on parameter `carrierPriviledgeRules` in method `setCarrierTestOverride` MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #8: - -MissingNullability: android.telephony.ims.ImsCallForwardInfo#getNumber(): - -MissingNullability: android.telephony.ims.ImsCallForwardInfo#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile) parameter #2: - -MissingNullability: android.telephony.ims.ImsCallProfile#ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile) parameter #3: - -MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String): - -MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String): - -MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String) parameter #1: - -MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraBoolean(String) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraBoolean(String, boolean) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraInt(String) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraInt(String, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtras(): - -MissingNullability: android.telephony.ims.ImsCallProfile#getMediaProfile(): - -MissingNullability: android.telephony.ims.ImsCallProfile#getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtra(String, String) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtra(String, String) parameter #1: - -MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtraBoolean(String, boolean) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtraInt(String, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#updateCallExtras(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#updateCallType(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#updateMediaProfile(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallProfile#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo) parameter #2: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo) parameter #2: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHeld(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHoldFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHoldReceived(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInitiated(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionProgressing(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRemoveParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumeFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumeReceived(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumed(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRttMessageReceived(String) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionTerminated(android.telephony.ims.ImsReasonInfo) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdateReceived(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdated(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUssdMessageReceived(int, String) parameter #1: - -MissingNullability: android.telephony.ims.ImsConferenceState#getConnectionStateForStatus(String) parameter #0: - -MissingNullability: android.telephony.ims.ImsConferenceState#mParticipants: - -MissingNullability: android.telephony.ims.ImsConferenceState#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsExternalCallState#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsReasonInfo#ImsReasonInfo(int, int, String) parameter #2: - -MissingNullability: android.telephony.ims.ImsReasonInfo#getExtraMessage(): - -MissingNullability: android.telephony.ims.ImsReasonInfo#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsService#createMmTelFeature(int): - -MissingNullability: android.telephony.ims.ImsService#createRcsFeature(int): - -MissingNullability: android.telephony.ims.ImsService#getConfig(int): - -MissingNullability: android.telephony.ims.ImsService#getRegistration(int): - -MissingNullability: android.telephony.ims.ImsService#onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) parameter #0: - -MissingNullability: android.telephony.ims.ImsService#querySupportedImsFeatures(): - -MissingNullability: android.telephony.ims.ImsSsData#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsSsInfo#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsStreamMediaProfile#copyFrom(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsStreamMediaProfile#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsSuppServiceNotification#ImsSuppServiceNotification(int, int, int, int, String, String[]) parameter #4: - -MissingNullability: android.telephony.ims.ImsSuppServiceNotification#ImsSuppServiceNotification(int, int, int, int, String, String[]) parameter #5: - -MissingNullability: android.telephony.ims.ImsSuppServiceNotification#history: - -MissingNullability: android.telephony.ims.ImsSuppServiceNotification#number: - -MissingNullability: android.telephony.ims.ImsSuppServiceNotification#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsUtListener#onSupplementaryServiceIndication(android.telephony.ims.ImsSsData) parameter #0: - -MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - -MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]) parameter #1: - -MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1: - -MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationQueried(int, android.os.Bundle) parameter #1: - -MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationQueryFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1: - -MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationUpdateFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities) parameter #0: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #1: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyResponse(android.telecom.VideoProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetCamera(String) parameter #0: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetCamera(String, int) parameter #0: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetDisplaySurface(android.view.Surface) parameter #0: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetPauseImage(android.net.Uri) parameter #0: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetPreviewSurface(android.view.Surface) parameter #0: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyRequest(android.telecom.VideoProfile) parameter #0: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #1: - -MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #2: - -MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToDisable(): - -MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToEnable(): - -MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.ims.feature.ImsFeature#changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy) parameter #0: - -MissingNullability: android.telephony.ims.feature.ImsFeature#changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy) parameter #1: - -MissingNullability: android.telephony.ims.feature.MmTelFeature#queryCapabilityStatus(): - -MissingNullability: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#accept(int, android.telephony.ims.ImsStreamMediaProfile) parameter #1: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#deflect(String) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#extendToConference(String[]) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getCallId(): - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getCallProfile(): - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getImsVideoCallProvider(): - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getLocalCallProfile(): - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getProperty(String): - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getProperty(String) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getRemoteCallProfile(): - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#hold(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#inviteParticipants(String[]) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#removeParticipants(String[]) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#resume(android.telephony.ims.ImsStreamMediaProfile) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendDtmf(char, android.os.Message) parameter #1: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendRttMessage(String) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendRttModifyRequest(android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendUssd(String) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#start(String, android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#start(String, android.telephony.ims.ImsCallProfile) parameter #1: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#startConference(String[], android.telephony.ims.ImsCallProfile) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#startConference(String[], android.telephony.ims.ImsCallProfile) parameter #1: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#update(int, android.telephony.ims.ImsStreamMediaProfile) parameter #1: - -MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase.State#toString(int): - -MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#getConfigString(int): - -MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#notifyProvisionedValueChanged(int, String) parameter #1: - -MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#setConfig(int, String) parameter #1: - -MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration#getServiceFeatures(): - -MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#addFeature(int, int): - -MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#build(): - -MissingNullability: android.telephony.ims.stub.ImsMultiEndpointImplBase#onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onDeregistered(android.telephony.ims.ImsReasonInfo) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1: - -MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#getSmsFormat(): - -MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsReceived(int, String, byte[]) parameter #1: - -MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsReceived(int, String, byte[]) parameter #2: - -MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #1: - -MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #2: - -MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, int, String, byte[]) parameter #2: - -MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, int, String, byte[]) parameter #3: - -MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #2: - -MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #3: - -MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #5: - -MissingNullability: android.telephony.ims.stub.ImsUtImplBase#queryCallForward(int, String) parameter #1: - -MissingNullability: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsUtImplBase#transact(android.os.Bundle) parameter #0: - -MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallBarring(int, int, String[]) parameter #2: - -MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallBarringForServiceClass(int, int, String[], int) parameter #2: - -MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallForward(int, int, String, int, int) parameter #2: - -MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String): - -MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String) parameter #0: - -MissingNullability: android.telephony.mbms.FileInfo#FileInfo(android.net.Uri, String) parameter #0: - -MissingNullability: android.telephony.mbms.FileInfo#FileInfo(android.net.Uri, String) parameter #1: - -MissingNullability: android.telephony.mbms.FileServiceInfo#FileServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>) parameter #0: - -MissingNullability: android.telephony.mbms.FileServiceInfo#FileServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>) parameter #1: - -MissingNullability: android.telephony.mbms.FileServiceInfo#FileServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>) parameter #2: - -MissingNullability: android.telephony.mbms.FileServiceInfo#FileServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>) parameter #3: - -MissingNullability: android.telephony.mbms.FileServiceInfo#FileServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>) parameter #4: - -MissingNullability: android.telephony.mbms.FileServiceInfo#FileServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>) parameter #5: - -MissingNullability: android.telephony.mbms.FileServiceInfo#FileServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>) parameter #6: - -MissingNullability: android.telephony.mbms.StreamingServiceInfo#StreamingServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date) parameter #0: - -MissingNullability: android.telephony.mbms.StreamingServiceInfo#StreamingServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date) parameter #1: - -MissingNullability: android.telephony.mbms.StreamingServiceInfo#StreamingServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date) parameter #2: - -MissingNullability: android.telephony.mbms.StreamingServiceInfo#StreamingServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date) parameter #3: - -MissingNullability: android.telephony.mbms.StreamingServiceInfo#StreamingServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date) parameter #4: - -MissingNullability: android.telephony.mbms.StreamingServiceInfo#StreamingServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date) parameter #5: - -MissingNullability: android.telephony.mbms.UriPathPair#getContentUri(): - -MissingNullability: android.telephony.mbms.UriPathPair#getFilePathUri(): - -MissingNullability: android.telephony.mbms.UriPathPair#writeToParcel(android.os.Parcel, int) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#asBinder(): - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#cancelDownload(android.telephony.mbms.DownloadRequest) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#download(android.telephony.mbms.DownloadRequest) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#onTransact(int, android.os.Parcel, android.os.Parcel, int) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#onTransact(int, android.os.Parcel, android.os.Parcel, int) parameter #2: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#requestUpdateFileServices(int, java.util.List<java.lang.String>) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.MbmsDownloadServiceBase#setTempFileRootDirectory(int, String) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#onBind(android.content.Intent): - -MissingNullability: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#onBind(android.content.Intent) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.MbmsStreamingServiceBase#asBinder(): - -MissingNullability: android.telephony.mbms.vendor.MbmsStreamingServiceBase#getPlaybackUri(int, String) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsStreamingServiceBase#initialize(android.telephony.mbms.MbmsStreamingSessionCallback, int) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.MbmsStreamingServiceBase#onTransact(int, android.os.Parcel, android.os.Parcel, int) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsStreamingServiceBase#onTransact(int, android.os.Parcel, android.os.Parcel, int) parameter #2: - -MissingNullability: android.telephony.mbms.vendor.MbmsStreamingServiceBase#requestUpdateStreamingServices(int, java.util.List<java.lang.String>) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsStreamingServiceBase#startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.MbmsStreamingServiceBase#startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback) parameter #2: - -MissingNullability: android.telephony.mbms.vendor.MbmsStreamingServiceBase#stopStreaming(int, String) parameter #1: - -MissingNullability: android.telephony.mbms.vendor.VendorUtils#getAppReceiverFromPackageName(android.content.Context, String): - -MissingNullability: android.telephony.mbms.vendor.VendorUtils#getAppReceiverFromPackageName(android.content.Context, String) parameter #0: - -MissingNullability: android.telephony.mbms.vendor.VendorUtils#getAppReceiverFromPackageName(android.content.Context, String) parameter #1: - + Missing nullability on parameter `apn` in method `setCarrierTestOverride` MissingNullability: android.text.Selection.MemoryTextWatcher#afterTextChanged(android.text.Editable) parameter #0: - + Missing nullability on parameter `s` in method `afterTextChanged` MissingNullability: android.text.Selection.MemoryTextWatcher#beforeTextChanged(CharSequence, int, int, int) parameter #0: - + Missing nullability on parameter `s` in method `beforeTextChanged` MissingNullability: android.text.Selection.MemoryTextWatcher#onTextChanged(CharSequence, int, int, int) parameter #0: - + Missing nullability on parameter `s` in method `onTextChanged` MissingNullability: android.transition.TransitionManager#getTransition(android.transition.Scene): - + Missing nullability on method `getTransition` return MissingNullability: android.transition.TransitionManager#getTransition(android.transition.Scene) parameter #0: - + Missing nullability on parameter `scene` in method `getTransition` MissingNullability: android.util.FeatureFlagUtils#getAllFeatureFlags(): - + Missing nullability on method `getAllFeatureFlags` return MissingNullability: android.util.FeatureFlagUtils#isEnabled(android.content.Context, String) parameter #0: - + Missing nullability on parameter `context` in method `isEnabled` MissingNullability: android.util.FeatureFlagUtils#isEnabled(android.content.Context, String) parameter #1: - + Missing nullability on parameter `feature` in method `isEnabled` MissingNullability: android.util.FeatureFlagUtils#setEnabled(android.content.Context, String, boolean) parameter #0: - + Missing nullability on parameter `context` in method `setEnabled` MissingNullability: android.util.FeatureFlagUtils#setEnabled(android.content.Context, String, boolean) parameter #1: - + Missing nullability on parameter `feature` in method `setEnabled` MissingNullability: android.util.TimeUtils#formatDuration(long): - + Missing nullability on method `formatDuration` return MissingNullability: android.util.proto.EncodedBuffer#dumpBuffers(String) parameter #0: - + Missing nullability on parameter `tag` in method `dumpBuffers` MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #0: - + Missing nullability on parameter `tag` in method `dumpByteString` MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #1: - + Missing nullability on parameter `prefix` in method `dumpByteString` MissingNullability: android.util.proto.EncodedBuffer#dumpByteString(String, String, byte[]) parameter #2: - + Missing nullability on parameter `buf` in method `dumpByteString` MissingNullability: android.util.proto.EncodedBuffer#getBytes(int): - + Missing nullability on method `getBytes` return MissingNullability: android.util.proto.EncodedBuffer#getDebugString(): - + Missing nullability on method `getDebugString` return MissingNullability: android.util.proto.EncodedBuffer#writeRawBuffer(byte[]) parameter #0: - + Missing nullability on parameter `val` in method `writeRawBuffer` MissingNullability: android.util.proto.EncodedBuffer#writeRawBuffer(byte[], int, int) parameter #0: - -MissingNullability: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.FileDescriptor) parameter #0: - -MissingNullability: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.OutputStream) parameter #0: - -MissingNullability: android.util.proto.ProtoOutputStream#dump(String) parameter #0: - -MissingNullability: android.util.proto.ProtoOutputStream#getBytes(): - -MissingNullability: android.util.proto.ProtoOutputStream#write(long, String) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#write(long, byte[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writeBytes(long, byte[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writeObject(long, byte[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedBool(long, boolean[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedDouble(long, double[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedEnum(long, int[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedFixed32(long, int[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedFixed64(long, long[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedFloat(long, float[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedInt32(long, int[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedInt64(long, long[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedSFixed32(long, int[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedSFixed64(long, long[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedSInt32(long, int[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedSInt64(long, long[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedUInt32(long, int[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writePackedUInt64(long, long[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writeRepeatedBytes(long, byte[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writeRepeatedObject(long, byte[]) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writeRepeatedString(long, String) parameter #1: - -MissingNullability: android.util.proto.ProtoOutputStream#writeString(long, String) parameter #1: - + Missing nullability on parameter `val` in method `writeRawBuffer` MissingNullability: android.util.proto.ProtoParseException#ProtoParseException(String) parameter #0: - -MissingNullability: android.util.proto.ProtoStream#FIELD_TYPE_NAMES: - -MissingNullability: android.util.proto.ProtoStream#getFieldCountString(long): - -MissingNullability: android.util.proto.ProtoStream#getFieldIdString(long): - -MissingNullability: android.util.proto.ProtoStream#getFieldTypeString(long): - -MissingNullability: android.util.proto.ProtoStream#getWireTypeString(int): - -MissingNullability: android.util.proto.ProtoStream#token2String(long): - + Missing nullability on parameter `msg` in method `ProtoParseException` MissingNullability: android.util.proto.WireTypeMismatchException#WireTypeMismatchException(String) parameter #0: - + Missing nullability on parameter `msg` in method `WireTypeMismatchException` MissingNullability: android.view.Choreographer#postCallback(int, Runnable, Object) parameter #1: - + Missing nullability on parameter `action` in method `postCallback` MissingNullability: android.view.Choreographer#postCallback(int, Runnable, Object) parameter #2: - + Missing nullability on parameter `token` in method `postCallback` MissingNullability: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long) parameter #1: - + Missing nullability on parameter `action` in method `postCallbackDelayed` MissingNullability: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long) parameter #2: - + Missing nullability on parameter `token` in method `postCallbackDelayed` MissingNullability: android.view.Choreographer#removeCallbacks(int, Runnable, Object) parameter #1: - + Missing nullability on parameter `action` in method `removeCallbacks` MissingNullability: android.view.Choreographer#removeCallbacks(int, Runnable, Object) parameter #2: - + Missing nullability on parameter `token` in method `removeCallbacks` MissingNullability: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0: - + Missing nullability on parameter `views` in method `sort` MissingNullability: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #3: - + Missing nullability on parameter `root` in method `sort` MissingNullability: android.view.KeyEvent#actionToString(int): - -MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #0: - -MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #1: - -MissingNullability: android.view.SurfaceControlViewHost#SurfaceControlViewHost(android.content.Context, android.view.Display, android.view.SurfaceControl) parameter #2: - -MissingNullability: android.view.SurfaceControlViewHost#addView(android.view.View, android.view.WindowManager.LayoutParams) parameter #0: - -MissingNullability: android.view.SurfaceControlViewHost#addView(android.view.View, android.view.WindowManager.LayoutParams) parameter #1: - + Missing nullability on method `actionToString` return MissingNullability: android.view.SurfaceControlViewHost#relayout(android.view.WindowManager.LayoutParams) parameter #0: - + Missing nullability on parameter `attrs` in method `relayout` MissingNullability: android.view.View#getTooltipView(): - + Missing nullability on method `getTooltipView` return MissingNullability: android.view.View#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #0: - + Missing nullability on parameter `background` in method `isDefaultFocusHighlightNeeded` MissingNullability: android.view.View#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #1: - + Missing nullability on parameter `foreground` in method `isDefaultFocusHighlightNeeded` MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #0: - + Missing nullability on parameter `tree` in method `startRenderingCommandsCapture` MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #1: - + Missing nullability on parameter `executor` in method `startRenderingCommandsCapture` MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.concurrent.Callable<java.io.OutputStream>) parameter #2: - -MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.function.Function<android.graphics.Picture,java.lang.Boolean>) parameter #0: - -MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.function.Function<android.graphics.Picture,java.lang.Boolean>) parameter #1: - -MissingNullability: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.function.Function<android.graphics.Picture,java.lang.Boolean>) parameter #2: - + Missing nullability on parameter `callback` in method `startRenderingCommandsCapture` MissingNullability: android.view.WindowManager#holdLock(android.os.IBinder, int) parameter #0: - + Missing nullability on parameter `token` in method `holdLock` MissingNullability: android.view.WindowManager.LayoutParams#accessibilityTitle: - -MissingNullability: android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener#onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager) parameter #0: - -MissingNullability: android.view.accessibility.AccessibilityNodeInfo#setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger) parameter #0: - + Missing nullability on field `accessibilityTitle` in class `class android.view.WindowManager.LayoutParams` MissingNullability: android.view.accessibility.AccessibilityNodeInfo#writeToParcelNoRecycle(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `parcel` in method `writeToParcelNoRecycle` MissingNullability: android.view.accessibility.AccessibilityWindowInfo#setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger) parameter #0: - -MissingNullability: android.view.contentcapture.ContentCaptureEvent#writeToParcel(android.os.Parcel, int) parameter #0: - + Missing nullability on parameter `counter` in method `setNumInstancesInUseCounter` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#asyncNewChild(int): - + Missing nullability on method `asyncNewChild` return MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getAutofillId(): - + Missing nullability on method `getAutofillId` return MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getExtras(): - + Missing nullability on method `getExtras` return MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getHint(): - + Missing nullability on method `getHint` return MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getNode(): - + Missing nullability on method `getNode` return MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getTempRect(): - + Missing nullability on method `getTempRect` return MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#getText(): - + Missing nullability on method `getText` return MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newChild(int): - + Missing nullability on method `newChild` return MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newHtmlInfoBuilder(String): - + Missing nullability on method `newHtmlInfoBuilder` return MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#newHtmlInfoBuilder(String) parameter #0: - + Missing nullability on parameter `tagName` in method `newHtmlInfoBuilder` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillHints(String[]) parameter #0: - + Missing nullability on parameter `hints` in method `setAutofillHints` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillId(android.view.autofill.AutofillId) parameter #0: - + Missing nullability on parameter `id` in method `setAutofillId` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillId(android.view.autofill.AutofillId, int) parameter #0: - + Missing nullability on parameter `parentId` in method `setAutofillId` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillOptions(CharSequence[]) parameter #0: - + Missing nullability on parameter `options` in method `setAutofillOptions` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setAutofillValue(android.view.autofill.AutofillValue) parameter #0: - + Missing nullability on parameter `value` in method `setAutofillValue` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setClassName(String) parameter #0: - + Missing nullability on parameter `className` in method `setClassName` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setContentDescription(CharSequence) parameter #0: - + Missing nullability on parameter `contentDescription` in method `setContentDescription` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHint(CharSequence) parameter #0: - + Missing nullability on parameter `hint` in method `setHint` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHintIdEntry(String) parameter #0: - + Missing nullability on parameter `entryName` in method `setHintIdEntry` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setHtmlInfo(android.view.ViewStructure.HtmlInfo) parameter #0: - + Missing nullability on parameter `htmlInfo` in method `setHtmlInfo` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #1: - + Missing nullability on parameter `packageName` in method `setId` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #2: - + Missing nullability on parameter `typeName` in method `setId` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setId(int, String, String, String) parameter #3: - + Missing nullability on parameter `entryName` in method `setId` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setLocaleList(android.os.LocaleList) parameter #0: - + Missing nullability on parameter `localeList` in method `setLocaleList` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setText(CharSequence) parameter #0: - + Missing nullability on parameter `text` in method `setText` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setText(CharSequence, int, int) parameter #0: - + Missing nullability on parameter `text` in method `setText` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTextLines(int[], int[]) parameter #0: - + Missing nullability on parameter `charOffsets` in method `setTextLines` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTextLines(int[], int[]) parameter #1: - + Missing nullability on parameter `baselines` in method `setTextLines` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setTransformation(android.graphics.Matrix) parameter #0: - + Missing nullability on parameter `matrix` in method `setTransformation` MissingNullability: android.view.contentcapture.ViewNode.ViewStructureImpl#setWebDomain(String) parameter #0: - + Missing nullability on parameter `domain` in method `setWebDomain` MissingNullability: android.widget.CalendarView#getBoundsForDate(long, android.graphics.Rect) parameter #1: - + Missing nullability on parameter `outBounds` in method `getBoundsForDate` MissingNullability: android.widget.ImageView#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #0: - + Missing nullability on parameter `background` in method `isDefaultFocusHighlightNeeded` MissingNullability: android.widget.ImageView#isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable) parameter #1: - + Missing nullability on parameter `foreground` in method `isDefaultFocusHighlightNeeded` MissingNullability: android.widget.Magnifier#getMagnifierDefaultSize(): - + Missing nullability on method `getMagnifierDefaultSize` return MissingNullability: android.widget.Magnifier#setOnOperationCompleteCallback(android.widget.Magnifier.Callback) parameter #0: - + Missing nullability on parameter `callback` in method `setOnOperationCompleteCallback` MissingNullability: android.widget.NumberPicker#getDisplayedValueForCurrentSelection(): - + Missing nullability on method `getDisplayedValueForCurrentSelection` return MissingNullability: android.widget.PopupMenu#getMenuListView(): - + Missing nullability on method `getMenuListView` return MissingNullability: android.widget.TimePicker#getAmView(): - + Missing nullability on method `getAmView` return MissingNullability: android.widget.TimePicker#getHourView(): - + Missing nullability on method `getHourView` return MissingNullability: android.widget.TimePicker#getMinuteView(): - + Missing nullability on method `getMinuteView` return MissingNullability: android.widget.TimePicker#getPmView(): - + Missing nullability on method `getPmView` return MutableBareField: android.content.AutofillOptions#appDisabledExpiration: - + Bare field appDisabledExpiration must be marked final, or moved behind accessors if mutable MutableBareField: android.content.AutofillOptions#augmentedAutofillEnabled: - + Bare field augmentedAutofillEnabled must be marked final, or moved behind accessors if mutable MutableBareField: android.content.AutofillOptions#disabledActivities: - + Bare field disabledActivities must be marked final, or moved behind accessors if mutable MutableBareField: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill: - + Bare field whitelistedActivitiesForAugmentedAutofill must be marked final, or moved behind accessors if mutable MutableBareField: android.content.pm.UserInfo#convertedFromPreCreated: Bare field convertedFromPreCreated must be marked final, or moved behind accessors if mutable MutableBareField: android.content.pm.UserInfo#creationTime: @@ -2438,588 +796,230 @@ MutableBareField: android.content.pm.UserInfo#serialNumber: MutableBareField: android.content.pm.UserInfo#userType: Bare field userType must be marked final, or moved behind accessors if mutable MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#cache: - + Bare field cache must be marked final, or moved behind accessors if mutable MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbName: - + Bare field dbName must be marked final, or moved behind accessors if mutable MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbSize: - + Bare field dbSize must be marked final, or moved behind accessors if mutable MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#lookaside: - + Bare field lookaside must be marked final, or moved behind accessors if mutable MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#pageSize: - + Bare field pageSize must be marked final, or moved behind accessors if mutable MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#dbStats: - + Bare field dbStats must be marked final, or moved behind accessors if mutable MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#largestMemAlloc: - + Bare field largestMemAlloc must be marked final, or moved behind accessors if mutable MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#memoryUsed: - + Bare field memoryUsed must be marked final, or moved behind accessors if mutable MutableBareField: android.database.sqlite.SQLiteDebug.PagerStats#pageCacheOverflow: - + Bare field pageCacheOverflow must be marked final, or moved behind accessors if mutable MutableBareField: android.os.StrictMode.ViolationInfo#broadcastIntentAction: - + Bare field broadcastIntentAction must be marked final, or moved behind accessors if mutable MutableBareField: android.os.StrictMode.ViolationInfo#durationMillis: - + Bare field durationMillis must be marked final, or moved behind accessors if mutable MutableBareField: android.os.StrictMode.ViolationInfo#numAnimationsRunning: - + Bare field numAnimationsRunning must be marked final, or moved behind accessors if mutable MutableBareField: android.os.StrictMode.ViolationInfo#numInstances: - + Bare field numInstances must be marked final, or moved behind accessors if mutable MutableBareField: android.os.StrictMode.ViolationInfo#tags: - + Bare field tags must be marked final, or moved behind accessors if mutable MutableBareField: android.os.StrictMode.ViolationInfo#violationNumThisLoop: - + Bare field violationNumThisLoop must be marked final, or moved behind accessors if mutable MutableBareField: android.os.StrictMode.ViolationInfo#violationUptimeMillis: - + Bare field violationUptimeMillis must be marked final, or moved behind accessors if mutable NoByteOrShort: android.media.audiofx.AudioEffect#byteArrayToShort(byte[]): - + Should avoid odd sized primitives; use `int` instead of `short` in method android.media.audiofx.AudioEffect.byteArrayToShort(byte[]) NoByteOrShort: android.media.audiofx.AudioEffect#setParameter(int, short) parameter #1: - + Should avoid odd sized primitives; use `int` instead of `short` in parameter value in android.media.audiofx.AudioEffect.setParameter(int param, short value) NoByteOrShort: android.media.audiofx.AudioEffect#shortToByteArray(short) parameter #0: - -NoByteOrShort: android.os.HwBlob#getInt16(long): - -NoByteOrShort: android.os.HwBlob#getInt8(long): - -NoByteOrShort: android.os.HwBlob#putInt16(long, short) parameter #1: - -NoByteOrShort: android.os.HwBlob#putInt8(long, byte) parameter #1: - -NoByteOrShort: android.os.HwParcel#readInt16(): - -NoByteOrShort: android.os.HwParcel#readInt8(): - -NoByteOrShort: android.os.HwParcel#writeInt16(short) parameter #0: - -NoByteOrShort: android.os.HwParcel#writeInt8(byte) parameter #0: - + Should avoid odd sized primitives; use `int` instead of `short` in parameter value in android.media.audiofx.AudioEffect.shortToByteArray(short value) NoByteOrShort: android.util.proto.EncodedBuffer#readRawByte(): - + Should avoid odd sized primitives; use `int` instead of `byte` in method android.util.proto.EncodedBuffer.readRawByte() NoByteOrShort: android.util.proto.EncodedBuffer#writeRawByte(byte) parameter #0: - - - -NoClone: android.net.util.SocketUtils#bindSocketToInterface(java.io.FileDescriptor, String) parameter #0: - -NoClone: android.net.util.SocketUtils#closeSocket(java.io.FileDescriptor) parameter #0: - -NoClone: android.os.NativeHandle#NativeHandle(java.io.FileDescriptor, boolean) parameter #0: - -NoClone: android.os.NativeHandle#getFileDescriptor(): - -NoClone: android.os.ParcelFileDescriptor#getFile(java.io.FileDescriptor) parameter #0: - -NoClone: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0: - -NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0: - -NoClone: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.FileDescriptor) parameter #0: - + Should avoid odd sized primitives; use `int` instead of `byte` in parameter val in android.util.proto.EncodedBuffer.writeRawByte(byte val) NoSettingsProvider: android.provider.Settings.Global#APP_OPS_CONSTANTS: - -NoSettingsProvider: android.provider.Settings.Global#AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES: - + New setting keys are not allowed (Field: APP_OPS_CONSTANTS); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Global#AUTOMATIC_POWER_SAVE_MODE: - + New setting keys are not allowed (Field: AUTOMATIC_POWER_SAVE_MODE); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Global#BATTERY_SAVER_CONSTANTS: - + New setting keys are not allowed (Field: BATTERY_SAVER_CONSTANTS); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD: - + New setting keys are not allowed (Field: DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_ENABLED: - + New setting keys are not allowed (Field: DYNAMIC_POWER_SAVINGS_ENABLED); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_BLACKLIST_EXEMPTIONS: - + New setting keys are not allowed (Field: HIDDEN_API_BLACKLIST_EXEMPTIONS); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_POLICY: - + New setting keys are not allowed (Field: HIDDEN_API_POLICY); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Global#HIDE_ERROR_DIALOGS: - -NoSettingsProvider: android.provider.Settings.Global#LOCATION_GLOBAL_KILL_SWITCH: - -NoSettingsProvider: android.provider.Settings.Global#LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST: - + New setting keys are not allowed (Field: HIDE_ERROR_DIALOGS); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE: - + New setting keys are not allowed (Field: LOW_POWER_MODE); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Global#LOW_POWER_MODE_STICKY: - -NoSettingsProvider: android.provider.Settings.Global#NOTIFICATION_BUBBLES: - + New setting keys are not allowed (Field: LOW_POWER_MODE_STICKY); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Global#OVERLAY_DISPLAY_DEVICES: - -NoSettingsProvider: android.provider.Settings.Global#TETHER_OFFLOAD_DISABLED: - -NoSettingsProvider: android.provider.Settings.Global#USE_OPEN_WIFI_PACKAGE: - + New setting keys are not allowed (Field: OVERLAY_DISPLAY_DEVICES); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED: - + New setting keys are not allowed (Field: ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY: - + New setting keys are not allowed (Field: ACCESSIBILITY_MAGNIFICATION_CAPABILITY); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE: - -NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_ALL: - -NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN: - -NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW: - + New setting keys are not allowed (Field: ACCESSIBILITY_MAGNIFICATION_MODE); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE: - -NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_FEATURE_FIELD_CLASSIFICATION: - + New setting keys are not allowed (Field: ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_SERVICE: - -NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT: - -NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE: - -NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE: - -NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MAX_VALUE_LENGTH: - -NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_USER_DATA_MIN_VALUE_LENGTH: - + New setting keys are not allowed (Field: AUTOFILL_SERVICE); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#CONTENT_CAPTURE_ENABLED: - + New setting keys are not allowed (Field: CONTENT_CAPTURE_ENABLED); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#DISABLED_PRINT_SERVICES: - -NoSettingsProvider: android.provider.Settings.Secure#DOZE_ALWAYS_ON: - + New setting keys are not allowed (Field: DISABLED_PRINT_SERVICES); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#ENABLED_VR_LISTENERS: - + New setting keys are not allowed (Field: ENABLED_VR_LISTENERS); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#IMMERSIVE_MODE_CONFIRMATIONS: - -NoSettingsProvider: android.provider.Settings.Secure#LOCATION_ACCESS_CHECK_DELAY_MILLIS: - -NoSettingsProvider: android.provider.Settings.Secure#LOCATION_ACCESS_CHECK_INTERVAL_MILLIS: - -NoSettingsProvider: android.provider.Settings.Secure#LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS: - -NoSettingsProvider: android.provider.Settings.Secure#LOCK_SCREEN_SHOW_NOTIFICATIONS: - -NoSettingsProvider: android.provider.Settings.Secure#NFC_PAYMENT_DEFAULT_COMPONENT: - + New setting keys are not allowed (Field: IMMERSIVE_MODE_CONFIRMATIONS); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#NOTIFICATION_BADGING: - + New setting keys are not allowed (Field: NOTIFICATION_BADGING); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#POWER_MENU_LOCKED_SHOW_CONTENT: - + New setting keys are not allowed (Field: POWER_MENU_LOCKED_SHOW_CONTENT); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#SYNC_PARENT_SOUNDS: - -NoSettingsProvider: android.provider.Settings.Secure#USER_SETUP_COMPLETE: - + New setting keys are not allowed (Field: SYNC_PARENT_SOUNDS); use getters/setters in relevant manager class NoSettingsProvider: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE: - + New setting keys are not allowed (Field: VOICE_INTERACTION_SERVICE); use getters/setters in relevant manager class -NotCloseable: android.app.prediction.AppPredictor: - -NotCloseable: android.net.EthernetManager.TetheredInterfaceRequest: - -NotCloseable: android.os.HwParcel: - -NotCloseable: android.telephony.ims.stub.ImsUtImplBase: - - - -NullableCollection: android.os.UserManager#createProfileForUser(String, String, int, int, String[]) parameter #4: - Type of parameter disallowedPackages in android.os.UserManager.createProfileForUser(String name, String userType, int flags, int userId, String[] disallowedPackages) is a nullable collection (`java.lang.String[]`); must be non-null - - -OnNameExpected: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.PrintWriter, String[]): - -OnNameExpected: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]): - OnNameExpected: android.service.notification.ConditionProviderService#isBound(): - -OnNameExpected: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context): - -OnNameExpected: android.service.quicksettings.TileService#isQuickSettingsSupported(): - + If implemented by developer, should follow the on<Something> style; otherwise consider marking final OnNameExpected: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback): - -OnNameExpected: android.telephony.ims.ImsService#createMmTelFeature(int): - -OnNameExpected: android.telephony.ims.ImsService#createRcsFeature(int): - -OnNameExpected: android.telephony.ims.ImsService#disableIms(int): - -OnNameExpected: android.telephony.ims.ImsService#enableIms(int): - -OnNameExpected: android.telephony.ims.ImsService#getConfig(int): - -OnNameExpected: android.telephony.ims.ImsService#getRegistration(int): - -OnNameExpected: android.telephony.ims.ImsService#querySupportedImsFeatures(): - -OnNameExpected: android.telephony.ims.ImsService#readyForFeatureCreation(): - -OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#dispose(int): - -OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int): - -OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#startGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>, android.telephony.mbms.GroupCallCallback): - -OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#stopGroupCall(int, long): - -OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#updateGroupCall(int, long, java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>): - - - -OptionalBuilderConstructorArgument: android.app.prediction.AppTargetEvent.Builder#Builder(android.app.prediction.AppTarget, int) parameter #0: - -OptionalBuilderConstructorArgument: android.net.CaptivePortalData.Builder#Builder(android.net.CaptivePortalData) parameter #0: - -OptionalBuilderConstructorArgument: android.os.VibrationAttributes.Builder#Builder(android.media.AudioAttributes, android.os.VibrationEffect) parameter #1: - + If implemented by developer, should follow the on<Something> style; otherwise consider marking final PackageLayering: android.util.FeatureFlagUtils: - + Method parameter type `android.content.Context` violates package layering: nothing in `package android.util` should depend on `package android.content` -ParcelConstructor: android.os.IncidentManager.IncidentReport#IncidentReport(android.os.Parcel): - -ParcelConstructor: android.os.IncidentReportArgs#IncidentReportArgs(android.os.Parcel): - ParcelConstructor: android.os.StrictMode.ViolationInfo#ViolationInfo(android.os.Parcel): - + Parcelable inflation is exposed through CREATOR, not raw constructors, in android.os.StrictMode.ViolationInfo ParcelConstructor: android.os.health.HealthStatsParceler#HealthStatsParceler(android.os.Parcel): - -ParcelConstructor: android.service.notification.SnoozeCriterion#SnoozeCriterion(android.os.Parcel): - + Parcelable inflation is exposed through CREATOR, not raw constructors, in android.os.health.HealthStatsParceler ParcelCreator: android.app.WindowConfiguration: - -ParcelCreator: android.net.metrics.ApfProgramEvent: - -ParcelCreator: android.net.metrics.ApfStats: - -ParcelCreator: android.net.metrics.DhcpClientEvent: - -ParcelCreator: android.net.metrics.DhcpErrorEvent: - -ParcelCreator: android.net.metrics.IpConnectivityLog.Event: - -ParcelCreator: android.net.metrics.IpManagerEvent: - -ParcelCreator: android.net.metrics.IpReachabilityEvent: - -ParcelCreator: android.net.metrics.NetworkEvent: - -ParcelCreator: android.net.metrics.RaEvent: - -ParcelCreator: android.net.metrics.ValidationProbeEvent: - + Parcelable requires a `CREATOR` field; missing in android.app.WindowConfiguration ParcelCreator: android.service.autofill.InternalOnClickAction: - + Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalOnClickAction ParcelCreator: android.service.autofill.InternalSanitizer: - + Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalSanitizer ParcelCreator: android.service.autofill.InternalTransformation: - + Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalTransformation ParcelCreator: android.service.autofill.InternalValidator: - + Parcelable requires a `CREATOR` field; missing in android.service.autofill.InternalValidator ParcelNotFinal: android.app.WindowConfiguration: - + Parcelable classes must be final: android.app.WindowConfiguration is not final ParcelNotFinal: android.content.pm.UserInfo: Parcelable classes must be final: android.content.pm.UserInfo is not final -ParcelNotFinal: android.net.metrics.IpConnectivityLog.Event: - -ParcelNotFinal: android.os.IncidentManager.IncidentReport: - ParcelNotFinal: android.os.health.HealthStatsParceler: - + Parcelable classes must be final: android.os.health.HealthStatsParceler is not final ParcelNotFinal: android.service.autofill.InternalOnClickAction: - + Parcelable classes must be final: android.service.autofill.InternalOnClickAction is not final ParcelNotFinal: android.service.autofill.InternalSanitizer: - + Parcelable classes must be final: android.service.autofill.InternalSanitizer is not final ParcelNotFinal: android.service.autofill.InternalTransformation: - + Parcelable classes must be final: android.service.autofill.InternalTransformation is not final ParcelNotFinal: android.service.autofill.InternalValidator: - + Parcelable classes must be final: android.service.autofill.InternalValidator is not final ProtectedMember: android.app.AppDetailsActivity#onCreate(android.os.Bundle): - -ProtectedMember: android.os.VibrationEffect#scale(int, float, int): - -ProtectedMember: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]): - -ProtectedMember: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.PrintWriter, String[]): - -ProtectedMember: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]): - -ProtectedMember: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context): - -ProtectedMember: android.util.proto.ProtoStream#FIELD_TYPE_NAMES: - + Protected methods not allowed; must be public: method android.app.AppDetailsActivity.onCreate(android.os.Bundle)} ProtectedMember: android.view.View#resetResolvedDrawables(): - + Protected methods not allowed; must be public: method android.view.View.resetResolvedDrawables()} ProtectedMember: android.view.ViewGroup#resetResolvedDrawables(): - + Protected methods not allowed; must be public: method android.view.ViewGroup.resetResolvedDrawables()} -RawAidl: android.telephony.mbms.vendor.MbmsDownloadServiceBase: - -RawAidl: android.telephony.mbms.vendor.MbmsStreamingServiceBase: - +RethrowRemoteException: android.app.ActivityManager#resumeAppSwitches(): + Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) -RethrowRemoteException: android.app.ActivityManager#resumeAppSwitches(): - -RethrowRemoteException: android.os.HwBinder#getService(String, String): - -RethrowRemoteException: android.os.HwBinder#getService(String, String, boolean): - -RethrowRemoteException: android.os.HwBinder#onTransact(int, android.os.HwParcel, android.os.HwParcel, int): - -RethrowRemoteException: android.os.HwBinder#registerService(String): - -RethrowRemoteException: android.os.HwBinder#transact(int, android.os.HwParcel, android.os.HwParcel, int): - -RethrowRemoteException: android.os.IHwBinder#transact(int, android.os.HwParcel, android.os.HwParcel, int): - -RethrowRemoteException: android.telephony.ims.ImsService#onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#cancelDownload(android.telephony.mbms.DownloadRequest): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#dispose(int): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#download(android.telephony.mbms.DownloadRequest): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#listPendingDownloads(int): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#onTransact(int, android.os.Parcel, android.os.Parcel, int): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#requestUpdateFileServices(int, java.util.List<java.lang.String>): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#resetDownloadKnowledge(android.telephony.mbms.DownloadRequest): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#setTempFileRootDirectory(int, String): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#dispose(int): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsStreamingServiceBase#dispose(int): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsStreamingServiceBase#getPlaybackUri(int, String): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsStreamingServiceBase#initialize(android.telephony.mbms.MbmsStreamingSessionCallback, int): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsStreamingServiceBase#onTransact(int, android.os.Parcel, android.os.Parcel, int): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsStreamingServiceBase#requestUpdateStreamingServices(int, java.util.List<java.lang.String>): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsStreamingServiceBase#startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback): - -RethrowRemoteException: android.telephony.mbms.vendor.MbmsStreamingServiceBase#stopStreaming(int, String): - - - -SamShouldBeLast: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int): - SamShouldBeLast: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]): - + SAM-compatible parameters (such as parameter 1, "printer", in android.database.sqlite.SQLiteDebug.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]): - -SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper): - -SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, java.util.concurrent.Executor, android.location.LocationListener): - -SamShouldBeLast: android.os.BugreportManager#startBugreport(android.os.ParcelFileDescriptor, android.os.ParcelFileDescriptor, android.os.BugreportParams, java.util.concurrent.Executor, android.os.BugreportManager.BugreportCallback): - -SamShouldBeLast: android.os.IHwBinder#linkToDeath(android.os.IHwBinder.DeathRecipient, long): - + SAM-compatible parameters (such as parameter 1, "factory", in android.database.sqlite.SQLiteDirectCursorDriver.query) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String): - + SAM-compatible parameters (such as parameter 1, "pw", in android.os.StrictMode.ViolationInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler): - + SAM-compatible parameters (such as parameter 3, "callback", in android.permission.PermissionControllerManager.countPermissionApps) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler): - -SamShouldBeLast: android.permission.PermissionControllerManager#revokeRuntimePermissions(java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback): - + SAM-compatible parameters (such as parameter 2, "callback", in android.permission.PermissionControllerManager.getAppPermissions) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.service.autofill.CharSequenceTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int): - + SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.CharSequenceTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.service.autofill.DateTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int): - + SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.DateTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.service.autofill.ImageTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int): - + SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.ImageTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>): - -SamShouldBeLast: android.telephony.ims.ImsMmTelManager#getFeatureState(java.util.function.Consumer<java.lang.Integer>, java.util.concurrent.Executor): - + SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.InternalTransformation.batchApply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.Choreographer#postCallback(int, Runnable, Object): - + SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.postCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long): - + SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.postCallbackDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SamShouldBeLast: android.view.Choreographer#removeCallbacks(int, Runnable, Object): - -SamShouldBeLast: android.view.ViewDebug#startRenderingCommandsCapture(android.view.View, java.util.concurrent.Executor, java.util.function.Function<android.graphics.Picture,java.lang.Boolean>): - -SamShouldBeLast: android.view.accessibility.AccessibilityManager#addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, android.os.Handler): - - - -ServiceName: android.Manifest.permission#BIND_CELL_BROADCAST_SERVICE: - -ServiceName: android.app.AppOpsManager#OPSTR_BIND_ACCESSIBILITY_SERVICE: - -ServiceName: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE: - -ServiceName: android.provider.Settings.Secure#AUTOFILL_SERVICE: - -ServiceName: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE: - - - -SetterReturnsThis: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener): - -SetterReturnsThis: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener): - - - -StartWithLower: android.content.pm.PackageManager#BINDER(): - Method name must start with lowercase char: BINDER - - -StaticFinalBuilder: android.content.integrity.RuleSet.Builder: - -StaticFinalBuilder: android.hardware.display.BrightnessConfiguration.Builder: - -StaticFinalBuilder: android.media.audiopolicy.AudioMix.Builder: - -StaticFinalBuilder: android.media.audiopolicy.AudioMixingRule.Builder: - -StaticFinalBuilder: android.media.audiopolicy.AudioPolicy.Builder: - -StaticFinalBuilder: android.net.CaptivePortalData.Builder: - -StaticFinalBuilder: android.net.TetheringManager.TetheringRequest.Builder: - -StaticFinalBuilder: android.telephony.ims.stub.ImsFeatureConfiguration.Builder: - + SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.removeCallbacks) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions StaticUtils: android.os.health.HealthKeys: - + Fully-static utility classes must not have constructor StaticUtils: android.service.autofill.InternalTransformation: - -StaticUtils: android.telephony.mbms.vendor.VendorUtils: - + Fully-static utility classes must not have constructor StaticUtils: android.util.FeatureFlagUtils: - -StaticUtils: android.util.proto.ProtoStream: - + Fully-static utility classes must not have constructor StreamFiles: android.os.Environment#buildPath(java.io.File, java.lang.String...): - + Methods accepting `File` should also accept `FileDescriptor` or streams: method android.os.Environment.buildPath(java.io.File,java.lang.String...) StreamFiles: android.os.FileUtils#contains(java.io.File, java.io.File): - -StreamFiles: android.provider.MediaStore#scanFile(android.content.Context, java.io.File): - -StreamFiles: android.provider.MediaStore#scanFileFromShell(android.content.Context, java.io.File): - -StreamFiles: android.provider.MediaStore#scanVolume(android.content.Context, java.io.File): - + Methods accepting `File` should also accept `FileDescriptor` or streams: method android.os.FileUtils.contains(java.io.File,java.io.File) UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getKeyphraseMetadata(String, java.util.Locale) parameter #1: - + Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale` UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale) parameter #2: - + Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale` UseIcu: android.hardware.soundtrigger.KeyphraseMetadata#supportsLocale(java.util.Locale) parameter #0: - -UseIcu: android.hardware.soundtrigger.SoundTrigger.Keyphrase#Keyphrase(int, int, java.util.Locale, String, int[]) parameter #2: - -UseIcu: android.hardware.soundtrigger.SoundTrigger.Keyphrase#getLocale(): - - - -UseParcelFileDescriptor: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.FileDescriptor) parameter #0: - + Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale` -UserHandle: android.app.ActivityManager#switchUser(android.os.UserHandle): - UserHandle: android.app.admin.DevicePolicyManager#getOwnerInstalledCaCerts(android.os.UserHandle): - + When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added UserHandle: android.app.usage.StorageStatsManager#queryCratesForPackage(java.util.UUID, String, android.os.UserHandle): - + When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added UserHandle: android.app.usage.StorageStatsManager#queryCratesForUser(java.util.UUID, android.os.UserHandle): - -UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle): - -UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociatedForWifiConnection(String, android.net.MacAddress, android.os.UserHandle): - + When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle): - -UserHandle: android.content.pm.PackageManager#getPermissionFlags(String, String, android.os.UserHandle): - -UserHandle: android.content.pm.PackageManager#grantRuntimePermission(String, String, android.os.UserHandle): - -UserHandle: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle): - -UserHandle: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle, String): - -UserHandle: android.content.pm.PackageManager#updatePermissionFlags(String, String, int, int, android.os.UserHandle): - -UserHandle: android.location.LocationManager#setLocationEnabledForUser(boolean, android.os.UserHandle): - -UserHandle: android.permission.PermissionControllerManager#applyStagedRuntimePermissionBackup(String, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>): - -UserHandle: android.permission.PermissionControllerManager#getRuntimePermissionBackup(android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<byte[]>): - -UserHandle: android.permission.PermissionControllerManager#stageAndApplyRuntimePermissionsBackup(byte[], android.os.UserHandle): - -UserHandle: android.telecom.TelecomManager#getDefaultDialerPackage(android.os.UserHandle): - + When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added UserHandleName: android.content.AutofillOptions: - + Classes holding a set of parameters should be called `FooParams`, was `AutofillOptions` UserHandleName: android.content.ContentCaptureOptions: - -UserHandleName: android.os.IncidentReportArgs: - -UserHandleName: android.provider.MediaStore#deleteContributedMedia(android.content.Context, String, android.os.UserHandle): - -UserHandleName: android.provider.MediaStore#getContributedMediaSize(android.content.Context, String, android.os.UserHandle): - - - -VisiblySynchronized: PsiClassObjectAccessExpression: - -VisiblySynchronized: PsiThisExpression: - -VisiblySynchronized: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int): - -VisiblySynchronized: android.app.ActivityManager#removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener): - -VisiblySynchronized: android.content.ContentProviderClient#setDetectNotResponding(long): - + Classes holding a set of parameters should be called `FooParams`, was `ContentCaptureOptions` + + +VisiblySynchronized: PsiThisExpression:this: + Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getApkPaths() VisiblySynchronized: android.content.res.AssetManager#getApkPaths(): - + Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getApkPaths() VisiblySynchronized: android.content.res.AssetManager#getLastResourceResolution(): - + Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getLastResourceResolution() VisiblySynchronized: android.content.res.AssetManager#getOverlayablesToString(String): - + Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getOverlayablesToString(String) VisiblySynchronized: android.content.res.AssetManager#setResourceResolutionLoggingEnabled(boolean): - + Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.setResourceResolutionLoggingEnabled(boolean) VisiblySynchronized: android.os.MessageQueue#removeSyncBarrier(int): - + Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.os.MessageQueue.removeSyncBarrier(int) diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index dc38db2134f4..69105016e0ea 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -152,6 +152,7 @@ final class NavigationBarController { private boolean mDrawLegacyNavigationBarBackground; private final Rect mTempRect = new Rect(); + private final int[] mTempPos = new int[2]; Impl(@NonNull InputMethodService inputMethodService) { mService = inputMethodService; @@ -259,21 +260,28 @@ final class NavigationBarController { switch (originalInsets.touchableInsets) { case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME: if (inputFrame.getVisibility() == View.VISIBLE) { - inputFrame.getBoundsOnScreen(mTempRect); + inputFrame.getLocationInWindow(mTempPos); + mTempRect.set(mTempPos[0], mTempPos[1], + mTempPos[0] + inputFrame.getWidth(), + mTempPos[1] + inputFrame.getHeight()); touchableRegion = new Region(mTempRect); } break; case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: if (inputFrame.getVisibility() == View.VISIBLE) { - inputFrame.getBoundsOnScreen(mTempRect); - mTempRect.top = originalInsets.contentTopInsets; + inputFrame.getLocationInWindow(mTempPos); + mTempRect.set(mTempPos[0], originalInsets.contentTopInsets, + mTempPos[0] + inputFrame.getWidth() , + mTempPos[1] + inputFrame.getHeight()); touchableRegion = new Region(mTempRect); } break; case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: if (inputFrame.getVisibility() == View.VISIBLE) { - inputFrame.getBoundsOnScreen(mTempRect); - mTempRect.top = originalInsets.visibleTopInsets; + inputFrame.getLocationInWindow(mTempPos); + mTempRect.set(mTempPos[0], originalInsets.visibleTopInsets, + mTempPos[0] + inputFrame.getWidth(), + mTempPos[1] + inputFrame.getHeight()); touchableRegion = new Region(mTempRect); } break; diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 815e4f0c9071..d71faee4cc8d 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -1205,13 +1205,16 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } static Uri readFrom(Parcel parcel) { - return new HierarchicalUri( - parcel.readString8(), - Part.readFrom(parcel), - PathPart.readFrom(parcel), - Part.readFrom(parcel), - Part.readFrom(parcel) - ); + final String scheme = parcel.readString8(); + final Part authority = Part.readFrom(parcel); + // In RFC3986 the path should be determined based on whether there is a scheme or + // authority present (https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3). + final boolean hasSchemeOrAuthority = + (scheme != null && scheme.length() > 0) || !authority.isEmpty(); + final PathPart path = PathPart.readFrom(hasSchemeOrAuthority, parcel); + final Part query = Part.readFrom(parcel); + final Part fragment = Part.readFrom(parcel); + return new HierarchicalUri(scheme, authority, path, query, fragment); } public int describeContents() { @@ -2270,6 +2273,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } } + static PathPart readFrom(boolean hasSchemeOrAuthority, Parcel parcel) { + final PathPart path = readFrom(parcel); + return hasSchemeOrAuthority ? makeAbsolute(path) : path; + } + /** * Creates a path from the encoded string. * diff --git a/core/java/android/permission/ILegacyPermissionManager.aidl b/core/java/android/permission/ILegacyPermissionManager.aidl index f1f083668711..78e12de04e89 100644 --- a/core/java/android/permission/ILegacyPermissionManager.aidl +++ b/core/java/android/permission/ILegacyPermissionManager.aidl @@ -49,4 +49,6 @@ interface ILegacyPermissionManager { void grantDefaultPermissionsToActiveLuiApp(in String packageName, int userId); void revokeDefaultPermissionsFromLuiApps(in String[] packageNames, int userId); + + void grantDefaultPermissionsToCarrierServiceApp(in String packageName, int userId); } diff --git a/core/java/android/permission/LegacyPermissionManager.java b/core/java/android/permission/LegacyPermissionManager.java index a4fa11b5121b..57776857864e 100644 --- a/core/java/android/permission/LegacyPermissionManager.java +++ b/core/java/android/permission/LegacyPermissionManager.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemService; +import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.PackageManager; import android.os.RemoteException; @@ -244,4 +245,20 @@ public final class LegacyPermissionManager { e.rethrowFromSystemServer(); } } + + /** + * Grant permissions to a newly set Carrier Services app. + * @param packageName The newly set Carrier Services app + * @param userId The user for which to grant the permissions. + * @hide + */ + public void grantDefaultPermissionsToCarrierServiceApp(@NonNull String packageName, + @UserIdInt int userId) { + try { + mLegacyPermissionManager.grantDefaultPermissionsToCarrierServiceApp(packageName, + userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index d25e456270ae..37f44e98c165 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -731,6 +731,13 @@ public final class DeviceConfig { public static final String NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE = "ambient_context_manager_service"; + /** + * Namespace for Vendor System Native related features. + * + * @hide + */ + public static final String NAMESPACE_VENDOR_SYSTEM_NATIVE = "vendor_system_native"; + private static final Object sLock = new Object(); @GuardedBy("sLock") private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners = diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java index 01152943efe3..e8d53d351795 100644 --- a/core/java/android/service/games/GameSession.java +++ b/core/java/android/service/games/GameSession.java @@ -25,7 +25,6 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.ActivityTaskManager; import android.app.Instrumentation; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; @@ -511,14 +510,11 @@ public abstract class GameSession { callback.onActivityResult(result.getResultCode(), result.getData()); }, executor); - final Intent trampolineIntent = new Intent(); - trampolineIntent.setComponent( - new ComponentName( - "android", "android.service.games.GameSessionTrampolineActivity")); - trampolineIntent.putExtra(GameSessionTrampolineActivity.INTENT_KEY, intent); - trampolineIntent.putExtra(GameSessionTrampolineActivity.OPTIONS_KEY, options); - trampolineIntent.putExtra( - GameSessionTrampolineActivity.FUTURE_KEY, future); + final Intent trampolineIntent = + GameSessionTrampolineActivity.createIntent( + intent, + options, + future); try { int result = ActivityTaskManager.getService().startActivityFromGameSession( diff --git a/core/java/android/service/games/GameSessionActivityResult.java b/core/java/android/service/games/GameSessionActivityResult.java index a2ec6ada010c..c8099e6e5eff 100644 --- a/core/java/android/service/games/GameSessionActivityResult.java +++ b/core/java/android/service/games/GameSessionActivityResult.java @@ -22,8 +22,12 @@ import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.annotations.VisibleForTesting; -final class GameSessionActivityResult implements Parcelable { + +/** @hide */ +@VisibleForTesting +public final class GameSessionActivityResult implements Parcelable { public static final Creator<GameSessionActivityResult> CREATOR = new Creator<GameSessionActivityResult>() { @@ -44,17 +48,17 @@ final class GameSessionActivityResult implements Parcelable { @Nullable private final Intent mData; - GameSessionActivityResult(int resultCode, @Nullable Intent data) { + public GameSessionActivityResult(int resultCode, @Nullable Intent data) { mResultCode = resultCode; mData = data; } - int getResultCode() { + public int getResultCode() { return mResultCode; } @Nullable - Intent getData() { + public Intent getData() { return mData; } diff --git a/core/java/android/service/games/GameSessionService.java b/core/java/android/service/games/GameSessionService.java index df5bad5c53b2..52c8ec3d4018 100644 --- a/core/java/android/service/games/GameSessionService.java +++ b/core/java/android/service/games/GameSessionService.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.app.Service; +import android.content.Context; import android.content.Intent; import android.hardware.display.DisplayManager; import android.os.Binder; @@ -28,6 +29,7 @@ import android.os.Handler; import android.os.IBinder; import android.view.Display; import android.view.SurfaceControlViewHost; +import android.view.WindowManager; import com.android.internal.infra.AndroidFuture; import com.android.internal.util.function.pooled.PooledLambda; @@ -117,13 +119,18 @@ public abstract class GameSessionService extends Service { } IBinder hostToken = new Binder(); + + // Use a WindowContext so that views attached to the SurfaceControlViewHost will receive + // configuration changes (rather than always perceiving the global configuration). + final Context windowContext = createWindowContext(display, + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, /*options=*/ null); SurfaceControlViewHost surfaceControlViewHost = - new SurfaceControlViewHost(this, display, hostToken); + new SurfaceControlViewHost(windowContext, display, hostToken); gameSession.attach( gameSessionController, createGameSessionRequest.getTaskId(), - this, + windowContext, surfaceControlViewHost, gameSessionViewHostConfiguration.mWidthPx, gameSessionViewHostConfiguration.mHeightPx); diff --git a/core/java/android/service/games/GameSessionTrampolineActivity.java b/core/java/android/service/games/GameSessionTrampolineActivity.java index 3d97d0f59b33..b23791842284 100644 --- a/core/java/android/service/games/GameSessionTrampolineActivity.java +++ b/core/java/android/service/games/GameSessionTrampolineActivity.java @@ -16,12 +16,15 @@ package android.service.games; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; +import android.content.ComponentName; import android.content.Intent; import android.os.Bundle; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.infra.AndroidFuture; import java.util.concurrent.Executor; @@ -35,6 +38,7 @@ import java.util.concurrent.Executor; * * @hide */ +@VisibleForTesting public final class GameSessionTrampolineActivity extends Activity { private static final String TAG = "GameSessionTrampoline"; private static final int REQUEST_CODE = 1; @@ -42,11 +46,52 @@ public final class GameSessionTrampolineActivity extends Activity { static final String FUTURE_KEY = "GameSessionTrampolineActivity.future"; static final String INTENT_KEY = "GameSessionTrampolineActivity.intent"; static final String OPTIONS_KEY = "GameSessionTrampolineActivity.options"; + private static final String HAS_LAUNCHED_INTENT_KEY = + "GameSessionTrampolineActivity.hasLaunchedIntent"; + private boolean mHasLaunchedIntent = false; + + /** + * Create an {@link Intent} for the {@link GameSessionTrampolineActivity} with the given + * parameters. + * + * @param targetIntent the forwarded {@link Intent} that is associated with the Activity that + * will be launched by the {@link GameSessionTrampolineActivity}. + * @param options Activity options. See {@link #startActivity(Intent, Bundle)}. + * @param resultFuture the {@link AndroidFuture} that will complete with the activity results of + * {@code targetIntent} launched. + * @return the Intent that will launch the {@link GameSessionTrampolineActivity} with the given + * parameters. + * @hide + */ + @VisibleForTesting + public static Intent createIntent( + @NonNull Intent targetIntent, + @Nullable Bundle options, + @NonNull AndroidFuture<GameSessionActivityResult> resultFuture) { + final Intent trampolineIntent = new Intent(); + trampolineIntent.setComponent( + new ComponentName( + "android", "android.service.games.GameSessionTrampolineActivity")); + trampolineIntent.putExtra(INTENT_KEY, targetIntent); + trampolineIntent.putExtra(OPTIONS_KEY, options); + trampolineIntent.putExtra(FUTURE_KEY, resultFuture); + + return trampolineIntent; + } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + mHasLaunchedIntent = savedInstanceState.getBoolean(HAS_LAUNCHED_INTENT_KEY); + } + + if (mHasLaunchedIntent) { + return; + } + mHasLaunchedIntent = true; + try { startActivityAsCaller( getIntent().getParcelableExtra(INTENT_KEY), @@ -60,10 +105,17 @@ public final class GameSessionTrampolineActivity extends Activity { FUTURE_KEY); future.completeExceptionally(e); finish(); + overridePendingTransition(0, 0); } } @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(HAS_LAUNCHED_INTENT_KEY, mHasLaunchedIntent); + } + + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode != REQUEST_CODE) { // Something went very wrong if we hit this code path, and we should bail. @@ -74,5 +126,6 @@ public final class GameSessionTrampolineActivity extends Activity { FUTURE_KEY); future.complete(new GameSessionActivityResult(resultCode, data)); finish(); + overridePendingTransition(0, 0); } } diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 4bbfbc2e717d..b783f6b8fd51 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -1095,7 +1095,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall if (!mWindowVisible) { mWindowVisible = true; if (mUiEnabled) { - mWindow.show(); + showWindow(); } } if (showCallback != null) { @@ -1284,9 +1284,25 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall } } + void showWindow() { + if (mWindow != null) { + mWindow.show(); + try { + mSystemService.setSessionWindowVisible(mToken, true); + } catch (RemoteException e) { + Log.w(TAG, "Failed to notify session window shown", e); + } + } + } + void ensureWindowHidden() { if (mWindow != null) { mWindow.hide(); + try { + mSystemService.setSessionWindowVisible(mToken, false); + } catch (RemoteException e) { + Log.w(TAG, "Failed to notify session window hidden", e); + } } } @@ -1377,7 +1393,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall if (mWindowVisible) { if (enabled) { ensureWindowAdded(); - mWindow.show(); + showWindow(); } else { ensureWindowHidden(); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index cf5727ea1342..62d0d37da84d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -12066,8 +12066,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p>In multiple-screen scenarios, if the surface spans multiple screens, * the coordinate space of the surface also spans multiple screens. * - * <p>After the method returns, the argument array contains the x- and - * y-coordinates of the view relative to the view's left and top edges, + * <p>After the method returns, the argument array contains the x and y + * coordinates of the view relative to the view's left and top edges, * respectively. * * @param location A two-element integer array in which the view coordinates @@ -18743,18 +18743,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * If some part of this view is not clipped by any of its parents, then - * return that area in r in global (root) coordinates. To convert r to local - * coordinates (without taking possible View rotations into account), offset - * it by -globalOffset (e.g. r.offset(-globalOffset.x, -globalOffset.y)). - * If the view is completely clipped or translated out, return false. + * Sets {@code r} to the coordinates of the non-clipped area of this view in + * the coordinate space of the view's root view. Sets {@code globalOffset} + * to the offset of the view's x and y coordinates from the coordinate space + * origin, which is the top left corner of the root view irrespective of + * screen decorations and system UI elements. * - * @param r If true is returned, r holds the global coordinates of the - * visible portion of this view. - * @param globalOffset If true is returned, globalOffset holds the dx,dy - * between this view and its root. globalOffet may be null. - * @return true if r is non-empty (i.e. part of the view is visible at the - * root level. + * <p>To convert {@code r} to coordinates relative to the top left corner of + * this view (without taking view rotations into account), offset {@code r} + * by the inverse values of + * {@code globalOffset}—{@code r.offset(-globalOffset.x, + * -globalOffset.y)}—which is equivalent to calling + * {@link #getLocalVisibleRect(Rect) getLocalVisibleRect(Rect)}. + * + * <p><b>Note:</b> Do not use this method to determine the size of a window + * in multi-window mode; use + * {@link WindowManager#getCurrentWindowMetrics()}. + * + * @param r If the method returns true, contains the coordinates of the + * visible portion of this view in the coordinate space of the view's + * root view. If the method returns false, the contents of {@code r} + * are undefined. + * @param globalOffset If the method returns true, contains the offset of + * the x and y coordinates of this view from the top left corner of the + * view's root view. If the method returns false, the contents of + * {@code globalOffset} are undefined. The argument can be null (see + * {@link #getGlobalVisibleRect(Rect) getGlobalVisibleRect(Rect)}. + * @return true if at least part of the view is visible within the root + * view; false if the view is completely clipped or translated out of + * the visible area of the root view. + * + * @see #getLocalVisibleRect(Rect) */ public boolean getGlobalVisibleRect(Rect r, Point globalOffset) { int width = mRight - mLeft; @@ -18769,10 +18788,48 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return false; } + /** + * Sets {@code r} to the coordinates of the non-clipped area of this view in + * the coordinate space of the view's root view. + * + * <p>See {@link #getGlobalVisibleRect(Rect, Point) + * getGlobalVisibleRect(Rect, Point)} for more information. + * + * @param r If the method returns true, contains the coordinates of the + * visible portion of this view in the coordinate space of the view's + * root view. If the method returns false, the contents of {@code r} + * are undefined. + * @return true if at least part of the view is visible within the root + * view; otherwise false. + */ public final boolean getGlobalVisibleRect(Rect r) { return getGlobalVisibleRect(r, null); } + /** + * Sets {@code r} to the coordinates of the non-clipped area of this view + * relative to the top left corner of the view. + * + * <p>If the view is clipped on the left or top, the left and top + * coordinates are offset from 0 by the clipped amount. For example, if the + * view is off screen 50px on the left and 30px at the top, the left and top + * coordinates are 50 and 30 respectively. + * + * <p>If the view is clipped on the right or bottom, the right and bottom + * coordinates are reduced by the clipped amount. For example, if the view + * is off screen 40px on the right and 20px at the bottom, the right + * coordinate is the view width - 40, and the bottom coordinate is the view + * height - 20. + * + * @param r If the method returns true, contains the coordinates of the + * visible portion of this view relative to the top left corner of the + * view. If the method returns false, the contents of {@code r} are + * undefined. + * @return true if at least part of the view is visible; false if the view + * is completely clipped or translated out of the visible area. + * + * @see #getGlobalVisibleRect(Rect, Point) + */ public final boolean getLocalVisibleRect(Rect r) { final Point offset = mAttachInfo != null ? mAttachInfo.mPoint : new Point(); if (getGlobalVisibleRect(r, offset)) { @@ -25606,8 +25663,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * multiple-screen environment, the coordinate space includes only the * screen on which the app is running. * - * <p>After the method returns, the argument array contains the x- and - * y-coordinates of the view relative to the view's left and top edges, + * <p>After the method returns, the argument array contains the x and y + * coordinates of the view relative to the view's left and top edges, * respectively. * * @param outLocation A two-element integer array in which the view @@ -25637,8 +25694,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * restricted to a single screen, the coordinate space includes only the * screen on which the app is running. * - * <p>After the method returns, the argument array contains the x- and - * y-coordinates of the view relative to the view's left and top edges, + * <p>After the method returns, the argument array contains the x and y + * coordinates of the view relative to the view's left and top edges, * respectively. * * @param outLocation A two-element integer array in which the view diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 79dac19d0927..7dc039d44f95 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -650,6 +650,26 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * Requests focus on the top running Activity in the given TaskFragment. This will only take + * effect if there is no focus, or if the current focus is in the same Task as the requested + * TaskFragment. + * @param fragmentToken client assigned unique token to create TaskFragment with specified in + * {@link TaskFragmentCreationParams#getFragmentToken()}. + * @hide + */ + @NonNull + public WindowContainerTransaction requestFocusOnTaskFragment(@NonNull IBinder fragmentToken) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder( + HierarchyOp.HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT) + .setContainer(fragmentToken) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + + } + + /** * When this {@link WindowContainerTransaction} failed to finish on the server side, it will * trigger callback with this {@param errorCallbackToken}. * @param errorCallbackToken client provided token that will be passed back as parameter in @@ -1057,6 +1077,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER = 15; public static final int HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER = 16; public static final int HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER = 17; + public static final int HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT = 18; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -1368,6 +1389,8 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER: return "{removeLocalInsetsProvider: container=" + mContainer + " insetsType=" + Arrays.toString(mInsetsTypes) + "}"; + case HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT: + return "{requestFocusOnTaskFragment: container=" + mContainer + "}"; default: return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent + " mToTop=" + mToTop diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index 52d54cd1f717..681693b1dbad 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -284,4 +284,9 @@ interface IVoiceInteractionManagerService { * Stops to listen the status of visible activity. */ void stopListeningVisibleActivityChanged(in IBinder token); + + /** + * Notifies when the session window is shown or hidden. + */ + void setSessionWindowVisible(in IBinder token, boolean visible); } diff --git a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl index bc757e24c852..6e409885fa13 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl @@ -30,6 +30,11 @@ void onVoiceSessionHidden(); /** + * Called when a voice session window is shown/hidden. + */ + void onVoiceSessionWindowVisibilityChanged(boolean visible); + + /** * Called when UI hints were received. */ void onSetUiHints(in Bundle args); diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java index 314b0a0c81db..a06ba9be4689 100644 --- a/core/java/com/android/internal/app/LocalePickerWithRegion.java +++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java @@ -247,6 +247,7 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O // In order to make the list view work with CollapsingToolbarLayout, // we have to enable the nested scrolling feature of the list view. getListView().setNestedScrollingEnabled(true); + getListView().setDivider(null); } @Override diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java index 351ac4587def..0a07e0a04a40 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -233,8 +233,14 @@ public class ResolverListAdapter extends BaseAdapter { // copied the original unfiltered items to a separate List instance and can now filter // the remainder in-place without any further bookkeeping. boolean needsCopyOfUnfiltered = (mUnfilteredResolveList == currentResolveList); - mUnfilteredResolveList = performSecondaryResolveListFiltering( + List<ResolvedComponentInfo> originalList = performSecondaryResolveListFiltering( currentResolveList, needsCopyOfUnfiltered); + if (originalList != null) { + // Only need the originalList value if there was a modification (otherwise it's null + // and shouldn't overwrite mUnfilteredResolveList). + mUnfilteredResolveList = originalList; + } + return finishRebuildingListWithFilteredResults(currentResolveList, doPostProcessing); } @@ -293,7 +299,7 @@ public class ResolverListAdapter extends BaseAdapter { * appearing in the rebuilt-list results, while still considering those items for the "other * profile" special-treatment described in {@code rebuildList()}. * - * @return the same (possibly null) List reference as {@code currentResolveList}, if the list is + * @return the same (possibly null) List reference as {@code currentResolveList} if the list is * unmodified as a result of filtering; or, if some item(s) were removed, then either a copy of * the original {@code currentResolveList} (if {@code returnCopyOfOriginalListIfModified} is * true), or null (otherwise). diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java index 18fde4794969..5fe111148c91 100644 --- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java +++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java @@ -27,7 +27,6 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; -import android.widget.FrameLayout; import android.widget.TextView; import com.android.internal.R; @@ -222,6 +221,7 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { convertView = mInflater.inflate( R.layout.app_language_picker_current_locale_item, parent, false); title = convertView.findViewById(R.id.language_picker_item); + addStateDescriptionIntoCurrentLocaleItem(convertView); } else { convertView = mInflater.inflate( R.layout.language_picker_item, parent, false); @@ -234,6 +234,7 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { if (!(convertView instanceof ViewGroup)) { convertView = mInflater.inflate( R.layout.app_language_picker_current_locale_item, parent, false); + addStateDescriptionIntoCurrentLocaleItem(convertView); } updateTextView( convertView, convertView.findViewById(R.id.language_picker_item), position); @@ -369,4 +370,9 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { : View.TEXT_DIRECTION_LTR); } } + + private void addStateDescriptionIntoCurrentLocaleItem(View root) { + String description = root.getContext().getResources().getString(R.string.checked); + root.setStateDescription(description); + } } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 6424989c6b4f..1b52aa93a51d 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -28,6 +28,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR; @@ -189,7 +190,8 @@ public class InteractionJankMonitor { public static final int CUJ_SUW_LOADING_SCREEN_FOR_STATUS = 48; public static final int CUJ_SPLIT_SCREEN_ENTER = 49; public static final int CUJ_SPLIT_SCREEN_EXIT = 50; - public static final int CUJ_SPLIT_SCREEN_RESIZE = 51; + public static final int CUJ_LOCKSCREEN_LAUNCH_CAMERA = 51; // reserved. + public static final int CUJ_SPLIT_SCREEN_RESIZE = 52; private static final int NO_STATSD_LOGGING = -1; @@ -249,6 +251,7 @@ public class InteractionJankMonitor { UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SUW_LOADING_SCREEN_FOR_STATUS, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_ENTER, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_EXIT, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SPLIT_SCREEN_RESIZE, }; @@ -321,6 +324,7 @@ public class InteractionJankMonitor { CUJ_SUW_LOADING_SCREEN_FOR_STATUS, CUJ_SPLIT_SCREEN_ENTER, CUJ_SPLIT_SCREEN_EXIT, + CUJ_LOCKSCREEN_LAUNCH_CAMERA, CUJ_SPLIT_SCREEN_RESIZE }) @Retention(RetentionPolicy.SOURCE) @@ -742,6 +746,8 @@ public class InteractionJankMonitor { return "SPLIT_SCREEN_ENTER"; case CUJ_SPLIT_SCREEN_EXIT: return "SPLIT_SCREEN_EXIT"; + case CUJ_LOCKSCREEN_LAUNCH_CAMERA: + return "CUJ_LOCKSCREEN_LAUNCH_CAMERA"; case CUJ_SPLIT_SCREEN_RESIZE: return "CUJ_SPLIT_SCREEN_RESIZE"; } diff --git a/core/java/com/android/internal/policy/ForceShowNavBarSettingsObserver.java b/core/java/com/android/internal/policy/ForceShowNavBarSettingsObserver.java new file mode 100644 index 000000000000..fc064ea1ff10 --- /dev/null +++ b/core/java/com/android/internal/policy/ForceShowNavBarSettingsObserver.java @@ -0,0 +1,75 @@ +/* + * 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.internal.policy; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; + +/** + * A ContentObserver for listening {@link Settings.Secure#NAV_BAR_FORCE_VISIBLE} setting key. + * + * @hide + */ +public class ForceShowNavBarSettingsObserver extends ContentObserver { + private Context mContext; + private Runnable mOnChangeRunnable; + + public ForceShowNavBarSettingsObserver(Handler handler, Context context) { + super(handler); + mContext = context; + } + + public void setOnChangeRunnable(Runnable r) { + mOnChangeRunnable = r; + } + + /** + * Registers the observer. + */ + public void register() { + final ContentResolver r = mContext.getContentResolver(); + r.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_FORCE_VISIBLE), + false, this, UserHandle.USER_ALL); + } + + /** + * Unregisters the observer. + */ + public void unregister() { + mContext.getContentResolver().unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange) { + if (mOnChangeRunnable != null) { + mOnChangeRunnable.run(); + } + } + + /** + * Returns true only when it's in orce show navigation bar mode. Otherwise, return false. + */ + public boolean isEnabled() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.NAV_BAR_FORCE_VISIBLE, 0, UserHandle.USER_CURRENT) == 1; + } +} diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java index 94a8ae5a8a67..f2c27a494fc9 100644 --- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java +++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java @@ -76,6 +76,7 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa ContentResolver contentResolver = context.getContentResolver(); mPostScrollDelayMillis = Settings.Global.getLong(contentResolver, SETTING_CAPTURE_DELAY, SETTING_CAPTURE_DELAY_DEFAULT); + Log.d(TAG, "screenshot.scroll_capture_delay = " + mPostScrollDelayMillis); } /** Based on ViewRootImpl#updateColorModeIfNeeded */ @@ -271,6 +272,13 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa Rect viewCaptureArea = new Rect(scrollResult.availableArea); viewCaptureArea.offset(0, -scrollResult.scrollDelta); + view.postOnAnimationDelayed( + () -> doCapture(scrollResult, view, viewCaptureArea, onComplete), + mPostScrollDelayMillis); + } + + private void doCapture(ScrollResult scrollResult, V view, Rect viewCaptureArea, + Consumer<Rect> onComplete) { int result = mRenderer.renderView(view, viewCaptureArea); if (result == HardwareRenderer.SYNC_OK || result == HardwareRenderer.SYNC_REDRAW_REQUESTED) { diff --git a/core/proto/android/os/appbackgroundrestrictioninfo.proto b/core/proto/android/os/appbackgroundrestrictioninfo.proto index 502fd64f97e4..5bf8ea79a8ea 100644 --- a/core/proto/android/os/appbackgroundrestrictioninfo.proto +++ b/core/proto/android/os/appbackgroundrestrictioninfo.proto @@ -73,12 +73,17 @@ message AppBackgroundRestrictionsInfo { optional FgsTrackerInfo fgs_tracker_info = 5; message BatteryTrackerInfo { - // total battery usage within last 24h (percentage) + // total battery usage within last 24h (1/10000th) optional int32 battery_24h = 1; - // background battery usage (percentage) + // background battery usage (1/10000th) optional int32 battery_usage_background = 2; - // FGS battery usage (percentage) + // FGS battery usage (1/10000th) optional int32 battery_usage_fgs = 3; + // Foreground battery usage (1/10000th) + optional int32 battery_usage_foreground = 4; + // Cached battery usage (1/10000th) + optional int32 battery_usage_cached = 5; + } optional BatteryTrackerInfo battery_tracker_info = 6; @@ -197,5 +202,8 @@ message AppBackgroundRestrictionsInfo { // indicates if the current device is a low ram device. optional bool low_mem_device = 12; + + // indicates previous background restriction level. + optional RestrictionLevel previous_restriction_level = 13; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 4075c5f4d8ae..217166c6810b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -377,6 +377,8 @@ <protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_ACCEPTED" /> <protected-broadcast android:name="com.android.server.action.REMOTE_BUGREPORT_SHARING_DECLINED" /> <protected-broadcast android:name="com.android.internal.action.EUICC_FACTORY_RESET" /> + <protected-broadcast + android:name="com.android.internal.action.EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS" /> <protected-broadcast android:name="com.android.server.usb.ACTION_OPEN_IN_APPS" /> <protected-broadcast android:name="com.android.server.am.DELETE_DUMPHEAP" /> <protected-broadcast android:name="com.android.server.net.action.SNOOZE_WARNING" /> @@ -6790,7 +6792,7 @@ android:excludeFromRecents="true" android:exported="true" android:permission="android.permission.MANAGE_GAME_ACTIVITY" - android:theme="@style/Theme.Translucent.NoTitleBar"> + android:theme="@style/Theme.GameSessionTrampoline"> </activity> <receiver android:name="com.android.server.BootReceiver" diff --git a/core/res/res/layout/app_language_picker_current_locale_item.xml b/core/res/res/layout/app_language_picker_current_locale_item.xml index bf6d9639791a..990e26c8f6be 100644 --- a/core/res/res/layout/app_language_picker_current_locale_item.xml +++ b/core/res/res/layout/app_language_picker_current_locale_item.xml @@ -39,6 +39,6 @@ android:layout_width="24dp" android:layout_height="24dp" android:src="@drawable/ic_check_24dp" - app:tint="#0F9D58"/> + app:tint="?attr/colorAccentPrimaryVariant"/> </LinearLayout> </LinearLayout> diff --git a/core/res/res/layout/app_language_picker_system_current.xml b/core/res/res/layout/app_language_picker_system_current.xml index 341ee2528671..300da25ea445 100644 --- a/core/res/res/layout/app_language_picker_system_current.xml +++ b/core/res/res/layout/app_language_picker_system_current.xml @@ -40,6 +40,6 @@ android:layout_width="24dp" android:layout_height="24dp" android:src="@drawable/ic_check_24dp" - app:tint="#0F9D58"/> + app:tint="?attr/colorAccentPrimaryVariant"/> </LinearLayout> </LinearLayout> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 77500c42c6bf..d9ac5164f705 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -109,4 +109,12 @@ <!-- Telephony qualified networks service class name to bind to by default. --> <string name="config_qualified_networks_service_class" translatable="false"></string> <java-symbol type="string" name="config_qualified_networks_service_class" /> + + <!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks + will not perform handover if the target transport is out of service, or VoPS not + supported. The network will be torn down on the source transport, and will be + re-established on the target transport when condition is allowed for bringing up a + new network. --> + <bool name="config_enhanced_iwlan_handover_check">true</bool> + <java-symbol type="bool" name="config_enhanced_iwlan_handover_check" /> </resources> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index bf42da080390..a60862b74e15 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -894,6 +894,22 @@ please see themes_device_defaults.xml. <!-- @hide Special theme for the default system Activity-based Alert dialogs. --> <style name="Theme.Dialog.Confirmation" parent="Theme.DeviceDefault.Dialog.Alert.DayNight" /> + <!-- @hide Theme for GameSessionTrampolineActivity that prevents showing UI and activity + transitions. --> + <style name="Theme.GameSessionTrampoline"> + <item name="backgroundDimEnabled">false</item> + <item name="colorBackgroundCacheHint">@null</item> + <item name="navigationBarColor">@color/transparent</item> + <item name="statusBarColor">@color/transparent</item> + <item name="windowAnimationStyle">@null</item> + <item name="windowBackground">@null</item> + <item name="windowContentOverlay">@null</item> + <item name="windowDrawsSystemBarBackgrounds">true</item> + <item name="windowIsFloating">true</item> + <item name="windowIsTranslucent">true</item> + <item name="windowNoTitle">true</item> + </style> + <!-- Theme for a window that looks like a toast. --> <style name="Theme.Toast" parent="Theme.DeviceDefault.Dialog"> <item name="windowBackground">?attr/toastFrameBackground</item> diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java index e083b0d460a2..3733bfa586d1 100644 --- a/core/tests/coretests/src/android/net/UriTest.java +++ b/core/tests/coretests/src/android/net/UriTest.java @@ -48,6 +48,7 @@ public class UriTest extends TestCase { public void testParcelling() { parcelAndUnparcel(Uri.parse("foo:bob%20lee")); parcelAndUnparcel(Uri.fromParts("foo", "bob lee", "fragment")); + parcelAndUnparcel(Uri.fromParts("https", "www.google.com", null)); parcelAndUnparcel(new Uri.Builder() .scheme("http") .authority("crazybob.org") @@ -890,9 +891,62 @@ public class UriTest extends TestCase { Throwable targetException = expected.getTargetException(); // Check that the exception was thrown for the correct reason. assertEquals("Unknown representation: 0", targetException.getMessage()); + } finally { + parcel.recycle(); } } + private Uri buildUriFromRawParcel(boolean argumentsEncoded, + String scheme, + String authority, + String path, + String query, + String fragment) { + // Representation value (from AbstractPart.REPRESENTATION_{ENCODED,DECODED}). + final int representation = argumentsEncoded ? 1 : 2; + Parcel parcel = Parcel.obtain(); + try { + parcel.writeInt(3); // hierarchical + parcel.writeString8(scheme); + parcel.writeInt(representation); + parcel.writeString8(authority); + parcel.writeInt(representation); + parcel.writeString8(path); + parcel.writeInt(representation); + parcel.writeString8(query); + parcel.writeInt(representation); + parcel.writeString8(fragment); + parcel.setDataPosition(0); + return Uri.CREATOR.createFromParcel(parcel); + } finally { + parcel.recycle(); + } + } + + public void testUnparcelMalformedPath() { + // Regression tests for b/171966843. + + // Test cases with arguments encoded (covering testing `scheme` * `authority` options). + Uri uri0 = buildUriFromRawParcel(true, "https", "google.com", "@evil.com", null, null); + assertEquals("https://google.com/@evil.com", uri0.toString()); + Uri uri1 = buildUriFromRawParcel(true, null, "google.com", "@evil.com", "name=spark", "x"); + assertEquals("//google.com/@evil.com?name=spark#x", uri1.toString()); + Uri uri2 = buildUriFromRawParcel(true, "http:", null, "@evil.com", null, null); + assertEquals("http::/@evil.com", uri2.toString()); + Uri uri3 = buildUriFromRawParcel(true, null, null, "@evil.com", null, null); + assertEquals("@evil.com", uri3.toString()); + + // Test cases with arguments not encoded (covering testing `scheme` * `authority` options). + Uri uriA = buildUriFromRawParcel(false, "https", "google.com", "@evil.com", null, null); + assertEquals("https://google.com/%40evil.com", uriA.toString()); + Uri uriB = buildUriFromRawParcel(false, null, "google.com", "@evil.com", null, null); + assertEquals("//google.com/%40evil.com", uriB.toString()); + Uri uriC = buildUriFromRawParcel(false, "http:", null, "@evil.com", null, null); + assertEquals("http::/%40evil.com", uriC.toString()); + Uri uriD = buildUriFromRawParcel(false, null, null, "@evil.com", "name=spark", "y"); + assertEquals("%40evil.com?name%3Dspark#y", uriD.toString()); + } + public void testToSafeString() { checkToSafeString("tel:xxxxxx", "tel:Google"); checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890"); diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java index b04b82629b92..a76d74edc0f4 100644 --- a/graphics/java/android/graphics/drawable/Icon.java +++ b/graphics/java/android/graphics/drawable/Icon.java @@ -128,6 +128,7 @@ public final class Icon implements Parcelable { // TYPE_RESOURCE: Resources // TYPE_DATA: DataBytes private Object mObj1; + private boolean mCachedAshmem = false; // TYPE_RESOURCE: package name // TYPE_URI: uri string @@ -156,6 +157,8 @@ public final class Icon implements Parcelable { /** * @return The {@link android.graphics.Bitmap} held by this {@link #TYPE_BITMAP} or * {@link #TYPE_ADAPTIVE_BITMAP} Icon. + * + * Note that this will always return an immutable Bitmap. * @hide */ @UnsupportedAppUsage @@ -166,8 +169,20 @@ public final class Icon implements Parcelable { return (Bitmap) mObj1; } + /** + * Sets the Icon's contents to a particular Bitmap. Note that this may make a copy of the Bitmap + * if the supplied Bitmap is mutable. In that case, the value returned by getBitmap() may not + * equal the Bitmap passed to setBitmap(). + * + * @hide + */ private void setBitmap(Bitmap b) { - mObj1 = b; + if (b.isMutable()) { + mObj1 = b.copy(b.getConfig(), false); + } else { + mObj1 = b; + } + mCachedAshmem = false; } /** @@ -488,6 +503,7 @@ public final class Icon implements Parcelable { getBitmap().getAllocationByteCount() >= MIN_ASHMEM_ICON_SIZE) { setBitmap(getBitmap().asShared()); } + mCachedAshmem = true; } /** @@ -913,7 +929,10 @@ public final class Icon implements Parcelable { switch (mType) { case TYPE_BITMAP: case TYPE_ADAPTIVE_BITMAP: - final Bitmap bits = getBitmap(); + if (!mCachedAshmem) { + mObj1 = ((Bitmap) mObj1).asShared(); + mCachedAshmem = true; + } getBitmap().writeToParcel(dest, flags); break; case TYPE_RESOURCE: diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java index e50b9a1cd469..81caf7786cf5 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -16,7 +16,6 @@ package androidx.window.extensions.embedding; -import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import android.app.Activity; @@ -49,7 +48,8 @@ import java.util.concurrent.Executor; class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { /** Mapping from the client assigned unique token to the {@link TaskFragmentInfo}. */ - private final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>(); + @VisibleForTesting + final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>(); /** * Mapping from the client assigned unique token to the TaskFragment parent @@ -120,25 +120,29 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { * @param secondaryFragmentBounds the initial bounds for the secondary TaskFragment * @param activityIntent Intent to start the secondary Activity with. * @param activityOptions ActivityOptions to start the secondary Activity with. + * @param windowingMode the windowing mode to set for the TaskFragments. */ void startActivityToSide(@NonNull WindowContainerTransaction wct, @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds, @NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken, @NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent, - @Nullable Bundle activityOptions, @NonNull SplitRule rule) { + @Nullable Bundle activityOptions, @NonNull SplitRule rule, + @WindowingMode int windowingMode) { final IBinder ownerToken = launchingActivity.getActivityToken(); // Create or resize the launching TaskFragment. if (mFragmentInfos.containsKey(launchingFragmentToken)) { resizeTaskFragment(wct, launchingFragmentToken, launchingFragmentBounds); + wct.setWindowingMode(mFragmentInfos.get(launchingFragmentToken).getToken(), + windowingMode); } else { createTaskFragmentAndReparentActivity(wct, launchingFragmentToken, ownerToken, - launchingFragmentBounds, WINDOWING_MODE_MULTI_WINDOW, launchingActivity); + launchingFragmentBounds, windowingMode, launchingActivity); } // Create a TaskFragment for the secondary activity. createTaskFragmentAndStartActivity(wct, secondaryFragmentToken, ownerToken, - secondaryFragmentBounds, WINDOWING_MODE_MULTI_WINDOW, activityIntent, + secondaryFragmentBounds, windowingMode, activityIntent, activityOptions); // Set adjacent to each other so that the containers below will be invisible. @@ -153,6 +157,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { void expandTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) { resizeTaskFragment(wct, fragmentToken, new Rect()); setAdjacentTaskFragments(wct, fragmentToken, null /* secondary */, null /* splitRule */); + setWindowingMode(wct, fragmentToken, WINDOWING_MODE_UNDEFINED); } /** @@ -255,6 +260,15 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { wct.setBounds(mFragmentInfos.get(fragmentToken).getToken(), bounds); } + private void setWindowingMode(WindowContainerTransaction wct, IBinder fragmentToken, + @WindowingMode int windowingMode) { + if (!mFragmentInfos.containsKey(fragmentToken)) { + throw new IllegalArgumentException( + "Can't find an existing TaskFragment with fragmentToken=" + fragmentToken); + } + wct.setWindowingMode(mFragmentInfos.get(fragmentToken).getToken(), windowingMode); + } + void deleteTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) { if (!mFragmentInfos.containsKey(fragmentToken)) { throw new IllegalArgumentException( diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 9f33cbcbcbd5..b370e59ac7c8 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -112,9 +112,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen */ public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent, @Nullable Bundle options, @NonNull SplitRule sideRule, - @Nullable Consumer<Exception> failureCallback) { + @Nullable Consumer<Exception> failureCallback, boolean isPlaceholder) { try { - mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule); + mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule, + isPlaceholder); } catch (Exception e) { if (failureCallback != null) { failureCallback.accept(e); @@ -256,9 +257,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (taskContainer == null) { return; } - final boolean wasInPip = isInPictureInPicture(taskContainer.getConfiguration()); + final boolean wasInPip = taskContainer.isInPictureInPicture(); final boolean isInPIp = isInPictureInPicture(config); - taskContainer.setConfiguration(config); + taskContainer.setWindowingMode(config.windowConfiguration.getWindowingMode()); // We need to check the animation override when enter/exit PIP or has bounds changed. boolean shouldUpdateAnimationOverride = wasInPip != isInPIp; @@ -277,8 +278,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * bounds is large enough for at least one split rule. */ private void updateAnimationOverride(@NonNull TaskContainer taskContainer) { - if (!taskContainer.isTaskBoundsInitialized()) { - // We don't know about the Task bounds yet. + if (!taskContainer.isTaskBoundsInitialized() + || !taskContainer.isWindowingModeInitialized()) { + // We don't know about the Task bounds/windowingMode yet. return; } @@ -292,7 +294,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private boolean supportSplit(@NonNull TaskContainer taskContainer) { // No split inside PIP. - if (isInPictureInPicture(taskContainer.getConfiguration())) { + if (taskContainer.isInPictureInPicture()) { return false; } // Check if the parent container bounds can support any split rule. @@ -460,8 +462,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (!taskContainer.setTaskBounds(taskBounds)) { Log.w(TAG, "Can't find bounds from activity=" + activityInTask); } - updateAnimationOverride(taskContainer); } + if (!taskContainer.isWindowingModeInitialized()) { + taskContainer.setWindowingMode(activityInTask.getResources().getConfiguration() + .windowConfiguration.getWindowingMode()); + } + updateAnimationOverride(taskContainer); return container; } @@ -710,8 +716,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } // TODO(b/190433398): Handle failed request - startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), null, - placeholderRule, null); + startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), null /* options */, + placeholderRule, null /* failureCallback */, true /* isPlaceholder */); return true; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 716a087203d3..e64e5d1c66d5 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -16,10 +16,11 @@ package androidx.window.extensions.embedding; -import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import android.app.Activity; import android.app.WindowConfiguration; +import android.app.WindowConfiguration.WindowingMode; import android.content.Context; import android.content.Intent; import android.graphics.Rect; @@ -111,13 +112,16 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { primaryActivity, primaryRectBounds, null); // Create new empty task fragment + final int taskId = primaryContainer.getTaskId(); final TaskFragmentContainer secondaryContainer = mController.newContainer( - null /* activity */, primaryActivity, primaryContainer.getTaskId()); + null /* activity */, primaryActivity, taskId); final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule, isLtr(primaryActivity, rule)); + final int windowingMode = mController.getTaskContainer(taskId) + .getWindowingModeForSplitTaskFragment(secondaryRectBounds); createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(), primaryActivity.getActivityToken(), secondaryRectBounds, - WINDOWING_MODE_MULTI_WINDOW); + windowingMode); secondaryContainer.setLastRequestedBounds(secondaryRectBounds); // Set adjacent to each other so that the containers below will be invisible. @@ -173,7 +177,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { final WindowContainerTransaction wct = new WindowContainerTransaction(); createTaskFragment(wct, newContainer.getTaskFragmentToken(), - launchingActivity.getActivityToken(), new Rect(), WINDOWING_MODE_MULTI_WINDOW); + launchingActivity.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED); applyTransaction(wct); return newContainer; @@ -189,15 +193,17 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @NonNull Rect bounds, @Nullable TaskFragmentContainer containerToAvoid) { TaskFragmentContainer container = mController.getContainerWithActivity( activity.getActivityToken()); + final int taskId = container != null ? container.getTaskId() : activity.getTaskId(); + final int windowingMode = mController.getTaskContainer(taskId) + .getWindowingModeForSplitTaskFragment(bounds); if (container == null || container == containerToAvoid) { - container = mController.newContainer(activity, activity.getTaskId()); - + container = mController.newContainer(activity, taskId); final TaskFragmentCreationParams fragmentOptions = createFragmentOptions( container.getTaskFragmentToken(), activity.getActivityToken(), bounds, - WINDOWING_MODE_MULTI_WINDOW); + windowingMode); wct.createTaskFragment(fragmentOptions); wct.reparentActivityToTaskFragment(container.getTaskFragmentToken(), @@ -206,6 +212,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { container.setLastRequestedBounds(bounds); } else { resizeTaskFragmentIfRegistered(wct, container, bounds); + updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode); } return container; @@ -217,12 +224,13 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { * @param launchingActivity An activity that should be in the primary container. If it is not * currently in an existing container, a new one will be created and * the activity will be re-parented to it. - * @param activityIntent The intent to start the new activity. - * @param activityOptions The options to apply to new activity start. - * @param rule The split rule to be applied to the container. + * @param activityIntent The intent to start the new activity. + * @param activityOptions The options to apply to new activity start. + * @param rule The split rule to be applied to the container. + * @param isPlaceholder Whether the launch is a placeholder. */ void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent, - @Nullable Bundle activityOptions, @NonNull SplitRule rule) { + @Nullable Bundle activityOptions, @NonNull SplitRule rule, boolean isPlaceholder) { final Rect parentBounds = getParentContainerBounds(launchingActivity); final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule, isLtr(launchingActivity, rule)); @@ -236,14 +244,21 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { launchingActivity.getTaskId()); } + final int taskId = primaryContainer.getTaskId(); TaskFragmentContainer secondaryContainer = mController.newContainer(null /* activity */, - launchingActivity, primaryContainer.getTaskId()); + launchingActivity, taskId); + final int windowingMode = mController.getTaskContainer(taskId) + .getWindowingModeForSplitTaskFragment(primaryRectBounds); final WindowContainerTransaction wct = new WindowContainerTransaction(); mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer, rule); startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds, launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds, - activityIntent, activityOptions, rule); + activityIntent, activityOptions, rule, windowingMode); + if (isPlaceholder) { + // When placeholder is launched in split, we should keep the focus on the primary. + wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken()); + } applyTransaction(wct); primaryContainer.setLastRequestedBounds(primaryRectBounds); @@ -272,14 +287,27 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { isLtr); final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule, isLtr); + final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer(); + // Whether the placeholder is becoming side-by-side with the primary from fullscreen. + final boolean isPlaceholderBecomingSplit = splitContainer.isPlaceholderContainer() + && secondaryContainer.areLastRequestedBoundsEqual(null /* bounds */) + && !secondaryRectBounds.isEmpty(); // If the task fragments are not registered yet, the positions will be updated after they // are created again. resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds); - final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer(); resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds); - setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule); + if (isPlaceholderBecomingSplit) { + // When placeholder is shown in split, we should keep the focus on the primary. + wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken()); + } + final TaskContainer taskContainer = mController.getTaskContainer( + updatedContainer.getTaskId()); + final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment( + primaryRectBounds); + updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode); + updateTaskFragmentWindowingModeIfRegistered(wct, secondaryContainer, windowingMode); } private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, @@ -311,6 +339,15 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { resizeTaskFragment(wct, container.getTaskFragmentToken(), bounds); } + private void updateTaskFragmentWindowingModeIfRegistered( + @NonNull WindowContainerTransaction wct, + @NonNull TaskFragmentContainer container, + @WindowingMode int windowingMode) { + if (container.getInfo() != null) { + wct.setWindowingMode(container.getInfo().getToken(), windowingMode); + } + } + @Override void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @Nullable Rect bounds) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index be793018d969..3c0762d81494 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -16,9 +16,14 @@ package androidx.window.extensions.embedding; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.res.Configuration; +import android.app.WindowConfiguration; +import android.app.WindowConfiguration.WindowingMode; import android.graphics.Rect; import android.os.IBinder; import android.util.ArraySet; @@ -37,9 +42,9 @@ class TaskContainer { /** Available window bounds of this Task. */ private final Rect mTaskBounds = new Rect(); - /** Configuration of the Task. */ - @Nullable - private Configuration mConfiguration; + /** Windowing mode of this Task. */ + @WindowingMode + private int mWindowingMode = WINDOWING_MODE_UNDEFINED; /** Active TaskFragments in this Task. */ final List<TaskFragmentContainer> mContainers = new ArrayList<>(); @@ -81,13 +86,42 @@ class TaskContainer { return !mTaskBounds.isEmpty(); } - @Nullable - Configuration getConfiguration() { - return mConfiguration; + void setWindowingMode(int windowingMode) { + mWindowingMode = windowingMode; + } + + /** Whether the Task windowing mode has been initialized. */ + boolean isWindowingModeInitialized() { + return mWindowingMode != WINDOWING_MODE_UNDEFINED; + } + + /** + * Returns the windowing mode for the TaskFragments below this Task, which should be split with + * other TaskFragments. + * + * @param taskFragmentBounds Requested bounds for the TaskFragment. It will be empty when + * the pair of TaskFragments are stacked due to the limited space. + */ + @WindowingMode + int getWindowingModeForSplitTaskFragment(@Nullable Rect taskFragmentBounds) { + // Only set to multi-windowing mode if the pair are showing side-by-side. Otherwise, it + // will be set to UNDEFINED which will then inherit the Task windowing mode. + if (taskFragmentBounds == null || taskFragmentBounds.isEmpty()) { + return WINDOWING_MODE_UNDEFINED; + } + // We use WINDOWING_MODE_MULTI_WINDOW when the Task is fullscreen. + // However, when the Task is in other multi windowing mode, such as Freeform, we need to + // have the activity windowing mode to match the Task, otherwise things like + // DecorCaptionView won't work correctly. As a result, have the TaskFragment to be in the + // Task windowing mode if the Task is in multi window. + // TODO we won't need this anymore after we migrate Freeform caption to WM Shell. + return WindowConfiguration.inMultiWindowMode(mWindowingMode) + ? mWindowingMode + : WINDOWING_MODE_MULTI_WINDOW; } - void setConfiguration(@Nullable Configuration configuration) { - mConfiguration = configuration; + boolean isInPictureInPicture() { + return mWindowingMode == WINDOWING_MODE_PINNED; } /** Whether there is any {@link TaskFragmentContainer} below this Task. */ diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java index b06ce4c19d5c..1f12c4484159 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java @@ -16,15 +16,23 @@ package androidx.window.extensions.embedding; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import android.content.res.Configuration; +import android.graphics.Point; import android.platform.test.annotations.Presubmit; +import android.window.TaskFragmentInfo; +import android.window.WindowContainerToken; +import android.window.WindowContainerTransaction; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -35,6 +43,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; + /** * Test class for {@link JetpackTaskFragmentOrganizer}. * @@ -48,6 +58,8 @@ public class JetpackTaskFragmentOrganizerTest { private static final int TASK_ID = 10; @Mock + private WindowContainerTransaction mTransaction; + @Mock private JetpackTaskFragmentOrganizer.TaskFragmentCallback mCallback; private JetpackTaskFragmentOrganizer mOrganizer; @@ -91,4 +103,24 @@ public class JetpackTaskFragmentOrganizerTest { verify(mOrganizer).unregisterRemoteAnimations(TASK_ID); } + + @Test + public void testExpandTaskFragment() { + final TaskFragmentContainer container = new TaskFragmentContainer(null, TASK_ID); + final TaskFragmentInfo info = createMockInfo(container); + mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info); + container.setInfo(info); + + mOrganizer.expandTaskFragment(mTransaction, container.getTaskFragmentToken()); + + verify(mTransaction).setWindowingMode(container.getInfo().getToken(), + WINDOWING_MODE_UNDEFINED); + } + + private TaskFragmentInfo createMockInfo(TaskFragmentContainer container) { + return new TaskFragmentInfo(container.getTaskFragmentToken(), + mock(WindowContainerToken.class), new Configuration(), 0 /* runningActivityCount */, + false /* isVisible */, new ArrayList<>(), new Point(), + false /* isTaskClearedForReuse */, false /* isTaskFragmentClearedForPip */); + } } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index 9fb08dffbab8..c7feb7e59de3 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -16,6 +16,13 @@ package androidx.window.extensions.embedding; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -64,6 +71,56 @@ public class TaskContainerTest { } @Test + public void testIsWindowingModeInitialized() { + final TaskContainer taskContainer = new TaskContainer(TASK_ID); + + assertFalse(taskContainer.isWindowingModeInitialized()); + + taskContainer.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + assertTrue(taskContainer.isWindowingModeInitialized()); + } + + @Test + public void testGetWindowingModeForSplitTaskFragment() { + final TaskContainer taskContainer = new TaskContainer(TASK_ID); + final Rect splitBounds = new Rect(0, 0, 500, 1000); + + assertEquals(WINDOWING_MODE_MULTI_WINDOW, + taskContainer.getWindowingModeForSplitTaskFragment(splitBounds)); + + taskContainer.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + assertEquals(WINDOWING_MODE_MULTI_WINDOW, + taskContainer.getWindowingModeForSplitTaskFragment(splitBounds)); + + taskContainer.setWindowingMode(WINDOWING_MODE_FREEFORM); + + assertEquals(WINDOWING_MODE_FREEFORM, + taskContainer.getWindowingModeForSplitTaskFragment(splitBounds)); + + // Empty bounds means the split pair are stacked, so it should be UNDEFINED which will then + // inherit the Task windowing mode + assertEquals(WINDOWING_MODE_UNDEFINED, + taskContainer.getWindowingModeForSplitTaskFragment(new Rect())); + } + + @Test + public void testIsInPictureInPicture() { + final TaskContainer taskContainer = new TaskContainer(TASK_ID); + + assertFalse(taskContainer.isInPictureInPicture()); + + taskContainer.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + assertFalse(taskContainer.isInPictureInPicture()); + + taskContainer.setWindowingMode(WINDOWING_MODE_PINNED); + + assertTrue(taskContainer.isInPictureInPicture()); + } + + @Test public void testIsEmpty() { final TaskContainer taskContainer = new TaskContainer(TASK_ID); diff --git a/libs/WindowManager/Shell/res/layout/split_decor.xml b/libs/WindowManager/Shell/res/layout/split_decor.xml index dfb90affe7f6..443ecb2ed3f3 100644 --- a/libs/WindowManager/Shell/res/layout/split_decor.xml +++ b/libs/WindowManager/Shell/res/layout/split_decor.xml @@ -20,8 +20,8 @@ android:layout_width="match_parent"> <ImageView android:id="@+id/split_resizing_icon" - android:layout_height="@*android:dimen/starting_surface_icon_size" - android:layout_width="@*android:dimen/starting_surface_icon_size" + android:layout_height="@dimen/split_icon_size" + android:layout_width="@dimen/split_icon_size" android:layout_gravity="center" android:scaleType="fitCenter" android:padding="0dp" diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index c21381d1486a..1dac9caba01e 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -87,6 +87,8 @@ <!-- How high we lift the divider when touching --> <dimen name="docked_stack_divider_lift_elevation">4dp</dimen> + <!-- Icon size for split screen --> + <dimen name="split_icon_size">72dp</dimen> <!-- Divider handle size for legacy split screen --> <dimen name="docked_divider_handle_width">16dp</dimen> <dimen name="docked_divider_handle_height">2dp</dimen> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java index 5dc6bd19853a..de30dbbe7e46 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java @@ -73,6 +73,8 @@ public class SplitDecorManager extends WindowlessWindowManager { private Rect mBounds = new Rect(); private ValueAnimator mFadeAnimator; + private int mIconSize; + public SplitDecorManager(Configuration configuration, IconProvider iconProvider, SurfaceSession surfaceSession) { super(configuration, null /* rootSurface */, null /* hostInputToken */); @@ -104,6 +106,7 @@ public class SplitDecorManager extends WindowlessWindowManager { mHostLeash = rootLeash; mViewHost = new SurfaceControlViewHost(context, context.getDisplay(), this); + mIconSize = context.getResources().getDimensionPixelSize(R.dimen.split_icon_size); final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(context) .inflate(R.layout.split_decor, null); mResizingIconView = rootLayout.findViewById(R.id.split_resizing_icon); @@ -171,14 +174,14 @@ public class SplitDecorManager extends WindowlessWindowManager { WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); - lp.width = mIcon.getIntrinsicWidth(); - lp.height = mIcon.getIntrinsicHeight(); + lp.width = mIconSize; + lp.height = mIconSize; mViewHost.relayout(lp); t.setLayer(mIconLeash, Integer.MAX_VALUE); } t.setPosition(mIconLeash, - newBounds.width() / 2 - mIcon.getIntrinsicWidth() / 2, - newBounds.height() / 2 - mIcon.getIntrinsicWidth() / 2); + newBounds.width() / 2 - mIconSize / 2, + newBounds.height() / 2 - mIconSize / 2); boolean show = newBounds.width() > mBounds.width() || newBounds.height() > mBounds.height(); if (show != mShown) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java index 0cea36ed48c8..28f59b53b5b6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java @@ -111,8 +111,7 @@ public class DropZoneView extends FrameLayout { mColorDrawable = new ColorDrawable(); setBackgroundDrawable(mColorDrawable); - final int iconSize = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.starting_surface_icon_size); + final int iconSize = context.getResources().getDimensionPixelSize(R.dimen.split_icon_size); mSplashScreenView = new ImageView(context); mSplashScreenView.setScaleType(ImageView.ScaleType.FIT_CENTER); addView(mSplashScreenView, diff --git a/core/java/com/android/internal/policy/KidsModeSettingsObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeSettingsObserver.java index 8a1d407382bc..f8f9d6b8f8a0 100644 --- a/core/java/com/android/internal/policy/KidsModeSettingsObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeSettingsObserver.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.policy; +package com.android.wm.shell.kidsmode; import android.content.ContentResolver; import android.content.Context; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java index 28681526b4f2..dc703583a449 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java @@ -40,7 +40,6 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.policy.KidsModeSettingsObserver; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayInsetsController; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java index 5526d5be7594..440a6f8fb59a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java @@ -44,7 +44,6 @@ import android.window.WindowContainerTransaction; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.internal.policy.KidsModeSettingsObserver; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.ShellExecutor; diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java index 7c9a045f4a42..fc0e05f7fb46 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java @@ -122,9 +122,10 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw * Adds a listener for switch changes */ public void addOnSwitchChangeListener(OnMainSwitchChangeListener listener) { - if (mMainSwitchBar == null) { + if (!mSwitchChangeListeners.contains(listener)) { mSwitchChangeListeners.add(listener); - } else { + } + if (mMainSwitchBar != null) { mMainSwitchBar.addOnSwitchChangeListener(listener); } } @@ -133,9 +134,8 @@ public class MainSwitchPreference extends TwoStatePreference implements OnMainSw * Remove a listener for switch changes */ public void removeOnSwitchChangeListener(OnMainSwitchChangeListener listener) { - if (mMainSwitchBar == null) { - mSwitchChangeListeners.remove(listener); - } else { + mSwitchChangeListeners.remove(listener); + if (mMainSwitchBar != null) { mMainSwitchBar.removeOnSwitchChangeListener(listener); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiPermissionChecker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiPermissionChecker.java new file mode 100644 index 000000000000..2fe6e4695b3c --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiPermissionChecker.java @@ -0,0 +1,98 @@ +/* + * 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.settingslib.wifi; + +import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.Manifest.permission.ACCESS_WIFI_STATE; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.IActivityManager; +import android.content.pm.PackageManager; +import android.os.RemoteException; +import android.text.TextUtils; +import android.util.Log; + +/** + * Helper class to check Wi-Fi permissions. + */ +public class WifiPermissionChecker { + + private static final String TAG = "WifiPermChecker"; + + private IActivityManager mActivityManager; + private PackageManager mPackageManager; + private String mLaunchedPackage; + + public WifiPermissionChecker(Activity activity) { + this(activity, ActivityManager.getService()); + } + + public WifiPermissionChecker(Activity activity, IActivityManager activityManager) { + mActivityManager = activityManager; + mPackageManager = activity.getPackageManager(); + mLaunchedPackage = getLaunchedFromPackage(activity); + } + + /** + * Returns the launched package name + */ + public String getLaunchedPackage() { + return mLaunchedPackage; + } + + /** + * Returns whether the launched package can access Wi-Fi information + */ + public boolean canAccessWifiState() { + return checkPermission(ACCESS_WIFI_STATE); + } + + /** + * Returns whether the launched package can access precise location + */ + public boolean canAccessFineLocation() { + return checkPermission(ACCESS_FINE_LOCATION); + } + + private boolean checkPermission(String permission) { + if (mPackageManager == null || TextUtils.isEmpty(mLaunchedPackage)) { + Log.e(TAG, "Failed to check package permission!" + + " {PackageManager:" + mPackageManager + + ", LaunchedPackage:" + mLaunchedPackage + "}"); + return false; + } + + if (mPackageManager.checkPermission(permission, mLaunchedPackage) == PERMISSION_GRANTED) { + return true; + } + + Log.w(TAG, "The launched package does not have the required permission!" + + " {LaunchedPackage:" + mLaunchedPackage + ", Permission:" + permission + "}"); + return false; + } + + private String getLaunchedFromPackage(Activity activity) { + try { + return mActivityManager.getLaunchedFromPackage(activity.getActivityToken()); + } catch (RemoteException e) { + Log.e(TAG, "Can not get the launched package from activity manager!"); + return null; + } + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiPermissionCheckerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiPermissionCheckerTest.java new file mode 100644 index 000000000000..ec84141360e4 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiPermissionCheckerTest.java @@ -0,0 +1,111 @@ +/* + * 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.settingslib.wifi; + +import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.Manifest.permission.ACCESS_WIFI_STATE; +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import android.app.Activity; +import android.app.IActivityManager; +import android.content.pm.PackageManager; +import android.os.RemoteException; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class WifiPermissionCheckerTest { + + static final String LAUNCHED_PACKAGE = "TestPackage"; + + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Mock + PackageManager mPackageManager; + @Mock + IActivityManager mActivityManager; + @Mock + Activity mActivity; + + WifiPermissionChecker mWifiPermissionChecker; + + @Before + public void setUp() { + when(mActivity.getPackageManager()).thenReturn(mPackageManager); + fakeGetLaunchedFromPackage(LAUNCHED_PACKAGE); + + mWifiPermissionChecker = new WifiPermissionChecker(mActivity, mActivityManager); + } + + @Test + public void getLaunchedPackage_returnLaunchedFromPackage() { + assertThat(mWifiPermissionChecker.getLaunchedPackage()).isEqualTo(LAUNCHED_PACKAGE); + } + + @Test + public void canAccessWifiState_noPermission_returnFalse() { + when(mPackageManager.checkPermission(ACCESS_WIFI_STATE, LAUNCHED_PACKAGE)) + .thenReturn(PERMISSION_DENIED); + + assertThat(mWifiPermissionChecker.canAccessWifiState()).isFalse(); + } + + @Test + public void canAccessWifiState_hasPermission_returnTrue() { + when(mPackageManager.checkPermission(ACCESS_WIFI_STATE, LAUNCHED_PACKAGE)) + .thenReturn(PERMISSION_GRANTED); + + assertThat(mWifiPermissionChecker.canAccessWifiState()).isTrue(); + } + + @Test + public void canAccessFineLocation_noPermission_returnFalse() { + when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, LAUNCHED_PACKAGE)) + .thenReturn(PERMISSION_DENIED); + + assertThat(mWifiPermissionChecker.canAccessFineLocation()).isFalse(); + } + + @Test + public void canAccessFineLocation_hasPermission_returnTrue() { + when(mPackageManager.checkPermission(ACCESS_FINE_LOCATION, LAUNCHED_PACKAGE)) + .thenReturn(PERMISSION_GRANTED); + + assertThat(mWifiPermissionChecker.canAccessFineLocation()).isTrue(); + } + + void fakeGetLaunchedFromPackage(String packageName) { + try { + when(mActivityManager.getLaunchedFromPackage(any())).thenReturn(packageName); + } catch (RemoteException e) { + // Do nothing + } + } +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt index ca557796462f..093589f8c636 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt @@ -20,6 +20,7 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ObjectAnimator import android.animation.PropertyValuesHolder +import android.animation.ValueAnimator import android.util.IntProperty import android.view.View import android.view.ViewGroup @@ -37,6 +38,7 @@ class ViewHierarchyAnimator { private const val DEFAULT_DURATION = 500L private val DEFAULT_INTERPOLATOR = Interpolators.STANDARD private val DEFAULT_ADDITION_INTERPOLATOR = Interpolators.STANDARD_DECELERATE + private val DEFAULT_REMOVAL_INTERPOLATOR = Interpolators.STANDARD_ACCELERATE /** The properties used to animate the view bounds. */ private val PROPERTIES = mapOf( @@ -113,7 +115,7 @@ class ViewHierarchyAnimator { } val listener = createUpdateListener(interpolator, duration, ephemeral) - recursivelyAddListener(rootView, listener) + addListener(rootView, listener, recursive = true) return true } @@ -183,7 +185,7 @@ class ViewHierarchyAnimator { val listener = createAdditionListener( origin, interpolator, duration, ignorePreviousValues = !includeMargins ) - recursivelyAddListener(rootView, listener) + addListener(rootView, listener, recursive = true) return true } @@ -298,6 +300,183 @@ class ViewHierarchyAnimator { } /** + * Animates the removal of [rootView] and its children from the hierarchy. It uses the given + * [interpolator] and [duration]. + * + * The end state of the animation is controlled by [destination]. This value can be any of + * the four corners, any of the four edges, or the center of the view. + */ + @JvmOverloads + fun animateRemoval( + rootView: View, + destination: Hotspot = Hotspot.CENTER, + interpolator: Interpolator = DEFAULT_REMOVAL_INTERPOLATOR, + duration: Long = DEFAULT_DURATION + ): Boolean { + if (!isVisible( + rootView.visibility, + rootView.left, + rootView.top, + rootView.right, + rootView.bottom + ) + ) { + return false + } + + val parent = rootView.parent as ViewGroup + + // Ensure that rootView's siblings animate nicely around the removal. + val listener = createUpdateListener( + interpolator, + duration, + ephemeral = true + ) + for (i in 0 until parent.childCount) { + val child = parent.getChildAt(i) + if (child == rootView) continue + addListener(child, listener, recursive = false) + } + + // Remove the view so that a layout update is triggered for the siblings and they + // animate to their next position while the view's removal is also animating. + parent.removeView(rootView) + // By adding the view to the overlay, we can animate it while it isn't part of the view + // hierarchy. It is correctly positioned because we have its previous bounds, and we set + // them manually during the animation. + parent.overlay.add(rootView) + + val startValues = mapOf( + Bound.LEFT to rootView.left, + Bound.TOP to rootView.top, + Bound.RIGHT to rootView.right, + Bound.BOTTOM to rootView.bottom + ) + val endValues = processEndValuesForRemoval( + destination, + rootView.left, + rootView.top, + rootView.right, + rootView.bottom + ) + + val boundsToAnimate = mutableSetOf<Bound>() + if (rootView.left != endValues.getValue(Bound.LEFT)) boundsToAnimate.add(Bound.LEFT) + if (rootView.top != endValues.getValue(Bound.TOP)) boundsToAnimate.add(Bound.TOP) + if (rootView.right != endValues.getValue(Bound.RIGHT)) boundsToAnimate.add(Bound.RIGHT) + if (rootView.bottom != endValues.getValue(Bound.BOTTOM)) { + boundsToAnimate.add(Bound.BOTTOM) + } + + startAnimation( + rootView, + boundsToAnimate, + startValues, + endValues, + interpolator, + duration, + ephemeral = true + ) + + if (rootView is ViewGroup) { + // Shift the children so they maintain a consistent position within the shrinking + // view. + shiftChildrenForRemoval(rootView, destination, endValues, interpolator, duration) + + // Fade out the children during the first half of the removal, so they don't clutter + // too much once the view becomes very small. Then we fade out the view itself, in + // case it has its own content and/or background. + val startAlphas = FloatArray(rootView.childCount) + for (i in 0 until rootView.childCount) { + startAlphas[i] = rootView.getChildAt(i).alpha + } + + val animator = ValueAnimator.ofFloat(1f, 0f) + animator.interpolator = Interpolators.ALPHA_OUT + animator.duration = duration / 2 + animator.addUpdateListener { animation -> + for (i in 0 until rootView.childCount) { + rootView.getChildAt(i).alpha = + (animation.animatedValue as Float) * startAlphas[i] + } + } + animator.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + rootView.animate() + .alpha(0f) + .setInterpolator(Interpolators.ALPHA_OUT) + .setDuration(duration / 2) + .withEndAction { parent.overlay.remove(rootView) } + .start() + } + }) + animator.start() + } else { + // Fade out the view during the second half of the removal. + rootView.animate() + .alpha(0f) + .setInterpolator(Interpolators.ALPHA_OUT) + .setDuration(duration / 2) + .setStartDelay(duration / 2) + .withEndAction { parent.overlay.remove(rootView) } + .start() + } + + return true + } + + /** + * Animates the children of [rootView] so that its layout remains internally consistent as + * it shrinks towards [destination] and changes its bounds to [endValues]. + * + * Uses [interpolator] and [duration], which should match those of the removal animation. + */ + private fun shiftChildrenForRemoval( + rootView: ViewGroup, + destination: Hotspot, + endValues: Map<Bound, Int>, + interpolator: Interpolator, + duration: Long + ) { + for (i in 0 until rootView.childCount) { + val child = rootView.getChildAt(i) + val childStartValues = mapOf( + Bound.LEFT to child.left, + Bound.TOP to child.top, + Bound.RIGHT to child.right, + Bound.BOTTOM to child.bottom + ) + val childEndValues = processChildEndValuesForRemoval( + destination, + child.left, + child.top, + child.right, + child.bottom, + endValues.getValue(Bound.RIGHT) - endValues.getValue(Bound.LEFT), + endValues.getValue(Bound.BOTTOM) - endValues.getValue(Bound.TOP) + ) + + val boundsToAnimate = mutableSetOf<Bound>() + if (child.left != endValues.getValue(Bound.LEFT)) boundsToAnimate.add(Bound.LEFT) + if (child.top != endValues.getValue(Bound.TOP)) boundsToAnimate.add(Bound.TOP) + if (child.right != endValues.getValue(Bound.RIGHT)) boundsToAnimate.add(Bound.RIGHT) + if (child.bottom != endValues.getValue(Bound.BOTTOM)) { + boundsToAnimate.add(Bound.BOTTOM) + } + + startAnimation( + child, + boundsToAnimate, + childStartValues, + childEndValues, + interpolator, + duration, + ephemeral = true + ) + } + } + + /** * Returns whether the given [visibility] and bounds are consistent with a view being * currently visible on screen. */ @@ -312,7 +491,7 @@ class ViewHierarchyAnimator { } /** - * Compute the actual starting values based on the requested [origin] and on + * Computes the actual starting values based on the requested [origin] and on * [ignorePreviousValues]. * * If [origin] is null, the resolved start values will be the same as those passed in, or @@ -422,7 +601,140 @@ class ViewHierarchyAnimator { ) } - private fun recursivelyAddListener(view: View, listener: View.OnLayoutChangeListener) { + /** + * Computes a removal animation's end values based on the requested [destination] and the + * view's starting bounds. + * + * Examples: + * 1) destination=TOP + * x---------x x---------x x---------x x---------x x---------x + * | | | | | | x---------x + * | | -> | | -> x---------x -> -> + * | | x---------x + * x---------x + * 2) destination=BOTTOM_LEFT + * x---------x + * | | x-------x + * | | -> | | -> x----x -> -> + * | | | | | | x--x + * x---------x x-------x x----x x--x x + * 3) destination=CENTER + * x---------x + * | | x-------x x-----x + * | | -> | | -> | | -> x---x -> x + * | | x-------x x-----x + * x---------x + */ + private fun processEndValuesForRemoval( + destination: Hotspot, + left: Int, + top: Int, + right: Int, + bottom: Int + ): Map<Bound, Int> { + val endLeft = when (destination) { + Hotspot.CENTER -> (left + right) / 2 + Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT, Hotspot.TOP -> + left + Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> right + } + val endTop = when (destination) { + Hotspot.CENTER -> (top + bottom) / 2 + Hotspot.LEFT, Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT, Hotspot.RIGHT -> + top + Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> bottom + } + val endRight = when (destination) { + Hotspot.CENTER -> (left + right) / 2 + Hotspot.TOP, Hotspot.TOP_RIGHT, Hotspot.RIGHT, + Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM -> + right + Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> left + } + val endBottom = when (destination) { + Hotspot.CENTER -> (top + bottom) / 2 + Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, + Hotspot.BOTTOM_LEFT, Hotspot.LEFT -> + bottom + Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> top + } + + return mapOf( + Bound.LEFT to endLeft, + Bound.TOP to endTop, + Bound.RIGHT to endRight, + Bound.BOTTOM to endBottom + ) + } + + /** + * Computes the end values for the child of a view being removed, based on the child's + * starting bounds, the removal's [destination], and the [parentWidth] and [parentHeight]. + * + * The end values always represent the child's position after it has been translated so that + * its center is at the [destination]. + * + * Examples: + * 1) destination=TOP + * The child maintains its left and right positions, but is shifted up so that its + * center is on the parent's end top edge. + * 2) destination=BOTTOM_LEFT + * The child shifts so that its center is on the parent's end bottom left corner. + * 3) destination=CENTER + * The child shifts so that its own center is on the parent's end center. + */ + private fun processChildEndValuesForRemoval( + destination: Hotspot, + left: Int, + top: Int, + right: Int, + bottom: Int, + parentWidth: Int, + parentHeight: Int + ): Map<Bound, Int> { + val halfWidth = (right - left) / 2 + val halfHeight = (bottom - top) / 2 + + val endLeft = when (destination) { + Hotspot.CENTER -> (parentWidth / 2) - halfWidth + Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> -halfWidth + Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> parentWidth - halfWidth + Hotspot.TOP, Hotspot.BOTTOM -> left + } + val endTop = when (destination) { + Hotspot.CENTER -> (parentHeight / 2) - halfHeight + Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> -halfHeight + Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> + parentHeight - halfHeight + Hotspot.LEFT, Hotspot.RIGHT -> top + } + val endRight = when (destination) { + Hotspot.CENTER -> (parentWidth / 2) + halfWidth + Hotspot.TOP_RIGHT, Hotspot.RIGHT, Hotspot.BOTTOM_RIGHT -> parentWidth + halfWidth + Hotspot.BOTTOM_LEFT, Hotspot.LEFT, Hotspot.TOP_LEFT -> halfWidth + Hotspot.TOP, Hotspot.BOTTOM -> right + } + val endBottom = when (destination) { + Hotspot.CENTER -> (parentHeight / 2) + halfHeight + Hotspot.BOTTOM_RIGHT, Hotspot.BOTTOM, Hotspot.BOTTOM_LEFT -> + parentHeight + halfHeight + Hotspot.TOP_LEFT, Hotspot.TOP, Hotspot.TOP_RIGHT -> halfHeight + Hotspot.LEFT, Hotspot.RIGHT -> bottom + } + + return mapOf( + Bound.LEFT to endLeft, + Bound.TOP to endTop, + Bound.RIGHT to endRight, + Bound.BOTTOM to endBottom + ) + } + + private fun addListener( + view: View, + listener: View.OnLayoutChangeListener, + recursive: Boolean = false + ) { // Make sure that only one listener is active at a time. val previousListener = view.getTag(R.id.tag_layout_listener) if (previousListener != null && previousListener is View.OnLayoutChangeListener) { @@ -431,9 +743,9 @@ class ViewHierarchyAnimator { view.addOnLayoutChangeListener(listener) view.setTag(R.id.tag_layout_listener, listener) - if (view is ViewGroup) { + if (view is ViewGroup && recursive) { for (i in 0 until view.childCount) { - recursivelyAddListener(view.getChildAt(i), listener) + addListener(view.getChildAt(i), listener, recursive = true) } } } @@ -490,6 +802,8 @@ class ViewHierarchyAnimator { } }.toTypedArray() + (view.getTag(R.id.tag_animator) as? ObjectAnimator)?.cancel() + val animator = ObjectAnimator.ofPropertyValuesHolder(view, *propertyValuesHolders) animator.interpolator = interpolator animator.duration = duration diff --git a/packages/SystemUI/res/drawable/keyguard_framed_avatar_background.xml b/packages/SystemUI/res/drawable/keyguard_framed_avatar_background.xml new file mode 100644 index 000000000000..a461bf836d61 --- /dev/null +++ b/packages/SystemUI/res/drawable/keyguard_framed_avatar_background.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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 + --> +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="@dimen/kg_framed_avatar_size"/> + <solid android:color="@color/kg_user_avatar_frame"/> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml index 9cf09ff328c4..6f3362308484 100644 --- a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml +++ b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml @@ -22,18 +22,25 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="end"> - <com.android.systemui.statusbar.phone.UserAvatarView - android:id="@+id/kg_multi_user_avatar" - android:layout_width="@dimen/kg_framed_avatar_size" - android:layout_height="@dimen/kg_framed_avatar_size" - android:layout_centerHorizontal="true" + <!-- We add a background behind the UserAvatarView with the same color and with a circular shape + so that this view can be expanded into a Dialog or an Activity. --> + <FrameLayout + android:id="@+id/kg_multi_user_avatar_with_background" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:layout_gravity="top|end" android:layout_marginEnd="16dp" - systemui:avatarPadding="0dp" - systemui:badgeDiameter="18dp" - systemui:badgeMargin="1dp" - systemui:frameColor="@color/kg_user_avatar_frame" - systemui:framePadding="0dp" - systemui:frameWidth="0dp"> - </com.android.systemui.statusbar.phone.UserAvatarView> + android:background="@drawable/keyguard_framed_avatar_background"> + <com.android.systemui.statusbar.phone.UserAvatarView + android:id="@+id/kg_multi_user_avatar" + android:layout_width="@dimen/kg_framed_avatar_size" + android:layout_height="@dimen/kg_framed_avatar_size" + systemui:avatarPadding="0dp" + systemui:badgeDiameter="18dp" + systemui:badgeMargin="1dp" + systemui:frameColor="@color/kg_user_avatar_frame" + systemui:framePadding="0dp" + systemui:frameWidth="0dp"> + </com.android.systemui.statusbar.phone.UserAvatarView> + </FrameLayout> </FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 2264671e5067..008299bd9b1c 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -60,11 +60,15 @@ <dimen name="global_actions_grid_item_layout_height">80dp</dimen> + <dimen name="qs_brightness_margin_bottom">16dp</dimen> + <!-- For large screens the security footer appears below the footer, same as phones in portrait --> <dimen name="qs_security_footer_single_line_height">48dp</dimen> <dimen name="qs_security_footer_background_inset">0dp</dimen> + <dimen name="qs_panel_padding_top">8dp</dimen> + <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) --> <dimen name="large_dialog_width">472dp</dimen> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 83b1b52fff3d..c8fff39cc8e5 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -711,4 +711,10 @@ <item>@*android:string/status_bar_alarm_clock</item> <item>@*android:string/status_bar_call_strength</item> </string-array> + + <!-- Packages of SystemUI --> + <string-array name="system_ui_packages" translatable="false"> + <item>com.android.keyguard</item> + <item>com.android.systemui</item> + </string-array> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java index f925eaa0e40b..eb6705a2e979 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java @@ -15,6 +15,8 @@ */ package com.android.keyguard; +import static com.android.systemui.util.ColorUtilKt.getPrivateAttrColorIfUnset; + import android.animation.AnimatorSet; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; @@ -152,7 +154,7 @@ class NumPadAnimator { ContextThemeWrapper ctw = new ContextThemeWrapper(context, mStyle); TypedArray a = ctw.obtainStyledAttributes(customAttrs); - mNormalColor = Utils.getPrivateAttrColorIfUnset(ctw, a, 0, 0, + mNormalColor = getPrivateAttrColorIfUnset(ctw, a, 0, 0, com.android.internal.R.attr.colorSurface); mHighlightColor = a.getColor(1, 0); a.recycle(); diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt index 3641e1d52144..0df2730a48eb 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt @@ -380,7 +380,7 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec ) { if (hasTopRoundedCorner == hasTop && hasBottomRoundedCorner == hasBottom && - roundedCornerBottomSize == bottomSize && + roundedCornerTopSize == topSize && roundedCornerBottomSize == bottomSize) { return } diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index b98fc03e3acd..8d6509874776 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -348,7 +348,8 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab @Override public void onDisplayChanged(int displayId) { final int newRotation = mContext.getDisplay().getRotation(); - if (mOverlays != null && mRotation != newRotation) { + if ((mOverlays != null || mScreenDecorHwcWindow != null) + && mRotation != newRotation) { // We cannot immediately update the orientation. Otherwise // WindowManager is still deferring layout until it has finished dispatching // the config changes, which may cause divergence between what we draw @@ -362,11 +363,13 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab + mRotation); } - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] != null) { - final ViewGroup overlayView = mOverlays[i].getRootView(); - overlayView.getViewTreeObserver().addOnPreDrawListener( - new RestartingPreDrawListener(overlayView, i, newRotation)); + if (mOverlays != null) { + for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { + if (mOverlays[i] != null) { + final ViewGroup overlayView = mOverlays[i].getRootView(); + overlayView.getViewTreeObserver().addOnPreDrawListener( + new RestartingPreDrawListener(overlayView, i, newRotation)); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 9d5b93c2b329..7c2673c31bf5 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -187,6 +187,14 @@ public class AssistManager { } @Override + public void onVoiceSessionWindowVisibilityChanged(boolean visible) + throws RemoteException { + if (VERBOSE) { + Log.v(TAG, "Window visibility changed: " + visible); + } + } + + @Override public void onSetUiHints(Bundle hints) { if (VERBOSE) { Log.v(TAG, "UI hints received"); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java index dbfce2ed2532..2f097924817e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java @@ -112,17 +112,16 @@ public class UdfpsDialogMeasureAdapter { if (child.getId() == R.id.biometric_icon_frame) { final FrameLayout iconFrame = (FrameLayout) child; final View icon = iconFrame.getChildAt(0); - - // Ensure that the icon is never larger than the sensor. - icon.measure( - MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST), - MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST)); - // Create a frame that's exactly the height of the sensor circle. iconFrame.measure( MeasureSpec.makeMeasureSpec( child.getLayoutParams().width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY)); + + // Ensure that the icon is never larger than the sensor. + icon.measure( + MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST)); } else if (child.getId() == R.id.space_above_icon) { child.measure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), @@ -208,16 +207,15 @@ public class UdfpsDialogMeasureAdapter { if (child.getId() == R.id.biometric_icon_frame) { final FrameLayout iconFrame = (FrameLayout) child; final View icon = iconFrame.getChildAt(0); + // Create a frame that's exactly the height of the sensor circle. + iconFrame.measure( + MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY)); // Ensure that the icon is never larger than the sensor. icon.measure( MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST)); - - // Create a frame that's exactly the height of the sensor circle. - iconFrame.measure( - MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY)); } else if (child.getId() == R.id.space_above_icon) { // Adjust the width and height of the top spacer if necessary. final int newTopSpacerHeight = child.getLayoutParams().height diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java index 842791f8ed93..937b81337cbb 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -127,17 +127,19 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { mBurnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(), darkAmountForAnimation); if (mAnimatingBetweenAodAndLockscreen && !mPauseAuth) { + mLockScreenFp.setTranslationX(mBurnInOffsetX); + mLockScreenFp.setTranslationY(mBurnInOffsetY); mBgProtection.setAlpha(1f - mInterpolatedDarkAmount); mLockScreenFp.setAlpha(1f - mInterpolatedDarkAmount); } else if (mInterpolatedDarkAmount == 0f) { + mLockScreenFp.setTranslationX(0); + mLockScreenFp.setTranslationY(0); mBgProtection.setAlpha(mAlpha / 255f); mLockScreenFp.setAlpha(mAlpha / 255f); } else { mBgProtection.setAlpha(0f); mLockScreenFp.setAlpha(0f); } - mLockScreenFp.setTranslationX(mBurnInOffsetX); - mLockScreenFp.setTranslationY(mBurnInOffsetY); mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount); mAodFp.setTranslationX(mBurnInOffsetX); diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt index 9dbeb77ebc00..e316722b64ea 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt @@ -105,7 +105,7 @@ private fun Int.toLayoutGravity(@Surface.Rotation rotation: Int): Int = when (ro DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.BOTTOM DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.LEFT DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.TOP - else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.RIGHT } Surface.ROTATION_270 -> when (this) { DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.TOP diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java index 3ae11ff28345..9c7411bf3649 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java @@ -54,7 +54,7 @@ public class FragmentHostManager { private final View mRootView; private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE - | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_ASSETS_PATHS); + | ActivityInfo.CONFIG_ASSETS_PATHS); private final FragmentService mManager; private final ExtensionFragmentManager mPlugins = new ExtensionFragmentManager(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 2e1373259975..29e940f24df6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -849,7 +849,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, @Override public void onLaunchAnimationCancelled() { - setOccluded(true /* occluded */, false /* animate */); + Log.d(TAG, "Occlude launch animation cancelled. " + + "Occluded state is now: " + mOccluded); } @NonNull @@ -894,7 +895,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, }; private IRemoteAnimationRunner mOccludeAnimationRunner = - new ActivityLaunchRemoteAnimationRunner(mOccludeAnimationController); + new OccludeActivityLaunchRemoteAnimationRunner(mOccludeAnimationController); /** * Animation controller for activities that unocclude the keyguard. This does not use the @@ -919,13 +920,17 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { - final RemoteAnimationTarget primary = apps[0]; + if (apps == null || apps.length == 0 || apps[0] == null) { + Log.d(TAG, "No apps provided to unocclude runner; " + + "skipping animation and unoccluding."); - if (primary == null) { finishedCallback.onAnimationFinished(); + setOccluded(false /* isOccluded */, true /* animate */); return; } + final RemoteAnimationTarget primary = apps[0]; + final SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier( mKeyguardViewControllerLazy.get().getViewRootImpl().getView()); @@ -965,6 +970,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, @Override public void onAnimationEnd(Animator animation) { try { + setOccluded(false /* isOccluded */, true /* animate */); finishedCallback.onAnimationFinished(); mUnoccludeAnimator = null; } catch (RemoteException e) { @@ -3125,4 +3131,36 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mRunner.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); } } + + /** + * Subclass of {@link ActivityLaunchRemoteAnimationRunner} that calls {@link #setOccluded} when + * onAnimationStart is called. + */ + private class OccludeActivityLaunchRemoteAnimationRunner + extends ActivityLaunchRemoteAnimationRunner { + + OccludeActivityLaunchRemoteAnimationRunner( + ActivityLaunchAnimator.Controller controller) { + super(controller); + } + + @Override + public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { + super.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); + + // This is the first signal we have from WM that we're going to be occluded. Set our + // internal state to reflect that immediately, vs. waiting for the launch animator to + // begin. Otherwise, calls to setShowingLocked, etc. will not know that we're about to + // be occluded and might re-show the keyguard. + setOccluded(true /* isOccluded */, false /* animate */); + } + + @Override + public void onAnimationCancelled() throws RemoteException { + super.onAnimationCancelled(); + Log.d(TAG, "Occlude launch animation cancelled. Occluded state is now: " + mOccluded); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt index 5a214d1cd5e0..70052fd45abf 100644 --- a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt @@ -21,24 +21,41 @@ import android.animation.ValueAnimator.AnimatorUpdateListener import android.animation.ValueAnimator import android.content.Context import android.content.res.ColorStateList +import android.graphics.drawable.GradientDrawable import com.android.internal.R import com.android.internal.annotations.VisibleForTesting import com.android.settingslib.Utils import com.android.systemui.monet.ColorScheme +import com.android.systemui.util.getColorWithAlpha /** - * ColorTransition is responsible for managing the animation between two specific colors. + * A [ColorTransition] is an object that updates the colors of views each time [updateColorScheme] + * is triggered. + */ +interface ColorTransition { + fun updateColorScheme(scheme: ColorScheme?) +} + +/** A generic implementation of [ColorTransition] so that we can define a factory method. */ +open class GenericColorTransition( + private val applyTheme: (ColorScheme?) -> Unit +) : ColorTransition { + override fun updateColorScheme(scheme: ColorScheme?) = applyTheme(scheme) +} + +/** + * A [ColorTransition] that animates between two specific colors. * It uses a ValueAnimator to execute the animation and interpolate between the source color and * the target color. * * Selection of the target color from the scheme, and application of the interpolated color * are delegated to callbacks. */ -open class ColorTransition( +open class AnimatingColorTransition( private val defaultColor: Int, private val extractColor: (ColorScheme) -> Int, private val applyColor: (Int) -> Unit -) : AnimatorUpdateListener { +) : AnimatorUpdateListener, ColorTransition { private val argbEvaluator = ArgbEvaluator() private val valueAnimator = buildAnimator() @@ -53,7 +70,7 @@ open class ColorTransition( applyColor(currentColor) } - fun updateColorScheme(scheme: ColorScheme?) { + override fun updateColorScheme(scheme: ColorScheme?) { val newTargetColor = if (scheme == null) defaultColor else extractColor(scheme) if (newTargetColor != targetColor) { sourceColor = currentColor @@ -76,7 +93,9 @@ open class ColorTransition( } } -typealias ColorTransitionFactory = (Int, (ColorScheme) -> Int, (Int) -> Unit) -> ColorTransition +typealias AnimatingColorTransitionFactory = + (Int, (ColorScheme) -> Int, (Int) -> Unit) -> AnimatingColorTransition +typealias GenericColorTransitionFactory = ((ColorScheme?) -> Unit) -> GenericColorTransition /** * ColorSchemeTransition constructs a ColorTransition for each color in the scheme @@ -86,27 +105,26 @@ typealias ColorTransitionFactory = (Int, (ColorScheme) -> Int, (Int) -> Unit) -> class ColorSchemeTransition internal constructor( private val context: Context, mediaViewHolder: MediaViewHolder, - colorTransitionFactory: ColorTransitionFactory + animatingColorTransitionFactory: AnimatingColorTransitionFactory, + genericColorTransitionFactory: GenericColorTransitionFactory ) { constructor(context: Context, mediaViewHolder: MediaViewHolder) : - this(context, mediaViewHolder, ::ColorTransition) + this(context, mediaViewHolder, ::AnimatingColorTransition, ::GenericColorTransition) val bgColor = context.getColor(com.android.systemui.R.color.material_dynamic_secondary95) - val surfaceColor = colorTransitionFactory( + val surfaceColor = animatingColorTransitionFactory( bgColor, ::surfaceFromScheme ) { surfaceColor -> val colorList = ColorStateList.valueOf(surfaceColor) mediaViewHolder.player.backgroundTintList = colorList - mediaViewHolder.albumView.foregroundTintList = colorList - mediaViewHolder.albumView.backgroundTintList = colorList mediaViewHolder.seamlessIcon.imageTintList = colorList mediaViewHolder.seamlessText.setTextColor(surfaceColor) mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor) } - val accentPrimary = colorTransitionFactory( + val accentPrimary = animatingColorTransitionFactory( loadDefaultColor(R.attr.textColorPrimary), ::accentPrimaryFromScheme ) { accentPrimary -> @@ -116,7 +134,7 @@ class ColorSchemeTransition internal constructor( mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary) } - val textPrimary = colorTransitionFactory( + val textPrimary = animatingColorTransitionFactory( loadDefaultColor(R.attr.textColorPrimary), ::textPrimaryFromScheme ) { textPrimary -> @@ -132,28 +150,65 @@ class ColorSchemeTransition internal constructor( mediaViewHolder.gutsViewHolder.setTextPrimaryColor(textPrimary) } - val textPrimaryInverse = colorTransitionFactory( + val textPrimaryInverse = animatingColorTransitionFactory( loadDefaultColor(R.attr.textColorPrimaryInverse), ::textPrimaryInverseFromScheme ) { textPrimaryInverse -> mediaViewHolder.actionPlayPause.imageTintList = ColorStateList.valueOf(textPrimaryInverse) } - val textSecondary = colorTransitionFactory( + val textSecondary = animatingColorTransitionFactory( loadDefaultColor(R.attr.textColorSecondary), ::textSecondaryFromScheme ) { textSecondary -> mediaViewHolder.artistText.setTextColor(textSecondary) } - val textTertiary = colorTransitionFactory( + val textTertiary = animatingColorTransitionFactory( loadDefaultColor(R.attr.textColorTertiary), ::textTertiaryFromScheme ) { textTertiary -> mediaViewHolder.seekBar.progressBackgroundTintList = ColorStateList.valueOf(textTertiary) } + // Note: This background gradient currently doesn't animate between colors. + val backgroundGradient = genericColorTransitionFactory { scheme -> + val defaultTintColor = ColorStateList.valueOf(bgColor) + if (scheme == null) { + mediaViewHolder.albumView.foregroundTintList = defaultTintColor + mediaViewHolder.albumView.backgroundTintList = defaultTintColor + return@genericColorTransitionFactory + } + + // If there's no album art, just hide the gradient so we show the solid background. + val showGradient = mediaViewHolder.albumView.drawable != null + val startColor = getColorWithAlpha( + backgroundStartFromScheme(scheme), + alpha = if (showGradient) .25f else 0f + ) + val endColor = getColorWithAlpha( + backgroundEndFromScheme(scheme), + alpha = if (showGradient) .90f else 0f + ) + val gradientColors = intArrayOf(startColor, endColor) + + val foregroundGradient = mediaViewHolder.albumView.foreground.mutate() + if (foregroundGradient is GradientDrawable) { + foregroundGradient.colors = gradientColors + } + val backgroundGradient = mediaViewHolder.albumView.background.mutate() + if (backgroundGradient is GradientDrawable) { + backgroundGradient.colors = gradientColors + } + } + val colorTransitions = arrayOf( - surfaceColor, accentPrimary, textPrimary, - textPrimaryInverse, textSecondary, textTertiary) + surfaceColor, + accentPrimary, + textPrimary, + textPrimaryInverse, + textSecondary, + textTertiary, + backgroundGradient + ) private fun loadDefaultColor(id: Int): Int { return Utils.getColorAttr(context, id).defaultColor diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt b/packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt index 97c6014c91bd..5e767b0458b9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaColorSchemes.kt @@ -35,3 +35,9 @@ internal fun textSecondaryFromScheme(scheme: ColorScheme) = scheme.neutral2[3] / /** Returns the tertiary text color for media controls based on the scheme. */ internal fun textTertiaryFromScheme(scheme: ColorScheme) = scheme.neutral2[5] // N2-400 + +/** Returns the color for the start of the background gradient based on the scheme. */ +internal fun backgroundStartFromScheme(scheme: ColorScheme) = scheme.accent2[8] // A2-700 + +/** Returns the color for the end of the background gradient based on the scheme. */ +internal fun backgroundEndFromScheme(scheme: ColorScheme) = scheme.accent1[8] // A1-700 diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index d2c35bd96d5a..d9ee8f3f06b4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -30,10 +30,12 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.ColorStateList; +import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Rect; import android.graphics.drawable.Animatable; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.graphics.drawable.TransitionDrawable; @@ -159,6 +161,7 @@ public class MediaControlPanel { private MetadataAnimationHandler mMetadataAnimationHandler; private ColorSchemeTransition mColorSchemeTransition; private Drawable mPrevArtwork = null; + private boolean mIsArtworkBound = false; private int mArtworkBoundId = 0; private int mArtworkNextBindRequestId = 0; @@ -586,6 +589,9 @@ public class MediaControlPanel { private void bindArtworkAndColors(MediaData data, boolean updateBackground) { final int reqId = mArtworkNextBindRequestId++; + if (updateBackground) { + mIsArtworkBound = false; + } // Capture width & height from views in foreground for artwork scaling in background int width = mMediaViewHolder.getPlayer().getWidth(); @@ -597,15 +603,18 @@ public class MediaControlPanel { // Album art ColorScheme mutableColorScheme = null; Drawable artwork; + boolean isArtworkBound; Icon artworkIcon = data.getArtwork(); if (artworkIcon != null) { WallpaperColors wallpaperColors = WallpaperColors .fromBitmap(artworkIcon.getBitmap()); mutableColorScheme = new ColorScheme(wallpaperColors, true); artwork = getScaledBackground(artworkIcon, width, height); + isArtworkBound = true; } else { // If there's no artwork, use colors from the app icon - artwork = null; + artwork = new ColorDrawable(Color.TRANSPARENT); + isArtworkBound = false; try { Drawable icon = mContext.getPackageManager() .getApplicationIcon(data.getPackageName()); @@ -625,16 +634,20 @@ public class MediaControlPanel { ImageView albumView = mMediaViewHolder.getAlbumView(); albumView.setPadding(0, 0, 0, 0); albumView.setClipToOutline(true); - if (updateBackground) { - if (mPrevArtwork == null || artwork == null) { + if (updateBackground || (!mIsArtworkBound && isArtworkBound)) { + if (mPrevArtwork == null) { albumView.setImageDrawable(artwork); } else { + // Since we throw away the last transition, this'll pop if you backgrounds + // are cycled too fast (or the correct background arrives very soon after + // the metadata changes). TransitionDrawable transitionDrawable = new TransitionDrawable( - new Drawable[] { mPrevArtwork, artwork }); + new Drawable[]{mPrevArtwork, artwork}); albumView.setImageDrawable(transitionDrawable); - transitionDrawable.startTransition(333); + transitionDrawable.startTransition(isArtworkBound ? 333 : 80); } mPrevArtwork = artwork; + mIsArtworkBound = isArtworkBound; } // Transition Colors to current color scheme diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 34f771ce0431..ce50ddff7b0f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -39,6 +39,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { private static final boolean DEBUG = false; private static final String CURRENT_PAGE = "current_page"; + private static final int NO_PAGE = -1; private static final String TAG = "PagedTileLayout"; private static final int REVEAL_SCROLL_DURATION_MILLIS = 750; @@ -109,13 +110,14 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } public void saveInstanceState(Bundle outState) { - outState.putInt(CURRENT_PAGE, getCurrentItem()); + int resolvedPage = mPageToRestore != NO_PAGE ? mPageToRestore : getCurrentPageNumber(); + outState.putInt(CURRENT_PAGE, resolvedPage); } public void restoreInstanceState(Bundle savedInstanceState) { // There's only 1 page at this point. We want to restore the correct page once the // pages have been inflated - mPageToRestore = savedInstanceState.getInt(CURRENT_PAGE, -1); + mPageToRestore = savedInstanceState.getInt(CURRENT_PAGE, NO_PAGE); } @Override @@ -151,12 +153,15 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @Override public void onRtlPropertiesChanged(int layoutDirection) { + // The configuration change will change the flag in the view (that's returned in + // isLayoutRtl). As we detect the change, we use the cached direction to store the page + // before setting it. + final int page = getPageNumberForDirection(mLayoutDirection == LAYOUT_DIRECTION_RTL); super.onRtlPropertiesChanged(layoutDirection); if (mLayoutDirection != layoutDirection) { mLayoutDirection = layoutDirection; setAdapter(mAdapter); - setCurrentItem(0, false); - mPageToRestore = 0; + setCurrentItem(page, false); } } @@ -172,8 +177,12 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { * Obtains the current page number respecting RTL */ private int getCurrentPageNumber() { + return getPageNumberForDirection(isLayoutRtl()); + } + + private int getPageNumberForDirection(boolean isLayoutRTL) { int page = getCurrentItem(); - if (mLayoutDirection == LAYOUT_DIRECTION_RTL) { + if (isLayoutRTL) { page = mPages.size() - 1 - page; } return page; @@ -388,9 +397,9 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { mPageIndicator.setNumPages(mPages.size()); setAdapter(mAdapter); mAdapter.notifyDataSetChanged(); - if (mPageToRestore != -1) { + if (mPageToRestore != NO_PAGE) { setCurrentItem(mPageToRestore, false); - mPageToRestore = -1; + mPageToRestore = NO_PAGE; } } @@ -479,9 +488,27 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { maxHeight = height; } } + if (mPages.get(0).getParent() == null) { + // Measure page 0 so we know how tall it is if it's not attached to the pager. + mPages.get(0).measure(widthMeasureSpec, heightMeasureSpec); + int height = mPages.get(0).getMeasuredHeight(); + if (height > maxHeight) { + maxHeight = height; + } + } setMeasuredDimension(getMeasuredWidth(), maxHeight + getPaddingBottom()); } + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + if (mPages.get(0).getParent() == null) { + // Layout page 0, so we can get the bottom of the tiles. We only do this if the page + // is not attached. + mPages.get(0).layout(l, t, r, b); + } + } + public int getColumnCount() { if (mPages.size() == 0) return 0; return mPages.get(0).mColumns; @@ -625,8 +652,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { if (mPageIndicator == null) return; if (mPageListener != null) { int pageNumber = isLayoutRtl() ? mPages.size() - 1 - position : position; - mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1 - : position == 0, pageNumber); + mPageListener.onPageChanged(pageNumber == 0, pageNumber); } } @@ -645,8 +671,8 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { mPageIndicator.setLocation(mPageIndicatorPosition); if (mPageListener != null) { int pageNumber = isLayoutRtl() ? mPages.size() - 1 - position : position; - mPageListener.onPageChanged(positionOffsetPixels == 0 && - (isLayoutRtl() ? position == mPages.size() - 1 : position == 0), + mPageListener.onPageChanged( + positionOffsetPixels == 0 && pageNumber == 0, // Send only valid page number on integer pages positionOffsetPixels == 0 ? pageNumber : PageListener.INVALID_PAGE ); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java index 8ca095d9a609..6eb54f799a24 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java @@ -24,7 +24,6 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.net.Network; import android.net.NetworkCapabilities; -import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.telephony.ServiceState; @@ -90,8 +89,6 @@ public class InternetDialog extends SystemUIDialog implements @VisibleForTesting protected InternetAdapter mAdapter; @VisibleForTesting - protected WifiManager mWifiManager; - @VisibleForTesting protected View mDialogView; @VisibleForTesting protected boolean mCanConfigWifi; @@ -179,7 +176,6 @@ public class InternetDialog extends SystemUIDialog implements mSubscriptionManager = mInternetDialogController.getSubscriptionManager(); mDefaultDataSubId = mInternetDialogController.getDefaultDataSubscriptionId(); mTelephonyManager = mInternetDialogController.getTelephonyManager(); - mWifiManager = mInternetDialogController.getWifiManager(); mCanConfigMobileData = canConfigMobileData; mCanConfigWifi = canConfigWifi; mCanChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context); @@ -332,7 +328,7 @@ public class InternetDialog extends SystemUIDialog implements showProgressBar(); final boolean isDeviceLocked = mInternetDialogController.isDeviceLocked(); - final boolean isWifiEnabled = mWifiManager != null && mWifiManager.isWifiEnabled(); + final boolean isWifiEnabled = mInternetDialogController.isWifiEnabled(); final boolean isWifiScanEnabled = mInternetDialogController.isWifiScanEnabled(); updateWifiToggle(isWifiEnabled, isDeviceLocked); updateConnectedWifi(isWifiEnabled, isDeviceLocked); @@ -362,9 +358,8 @@ public class InternetDialog extends SystemUIDialog implements mSeeAllLayout.setOnClickListener(this::onClickSeeMoreButton); mWiFiToggle.setOnCheckedChangeListener( (buttonView, isChecked) -> { - if (mWifiManager == null) return; - buttonView.setChecked(isChecked); - mWifiManager.setWifiEnabled(isChecked); + if (mInternetDialogController.isWifiEnabled() == isChecked) return; + mInternetDialogController.setWifiEnabled(isChecked); }); mDoneButton.setOnClickListener(v -> dismiss()); mAirplaneModeButton.setOnClickListener(v -> { @@ -388,7 +383,7 @@ public class InternetDialog extends SystemUIDialog implements Log.d(TAG, "setMobileDataLayout, isCarrierNetworkActive = " + isCarrierNetworkActive); } - boolean isWifiEnabled = mWifiManager != null && mWifiManager.isWifiEnabled(); + boolean isWifiEnabled = mInternetDialogController.isWifiEnabled(); if (!mInternetDialogController.hasActiveSubId() && (!isWifiEnabled || !isCarrierNetworkActive)) { mMobileNetworkLayout.setVisibility(View.GONE); @@ -444,7 +439,9 @@ public class InternetDialog extends SystemUIDialog implements @MainThread private void updateWifiToggle(boolean isWifiEnabled, boolean isDeviceLocked) { - mWiFiToggle.setChecked(isWifiEnabled); + if (mWiFiToggle.isChecked() != isWifiEnabled) { + mWiFiToggle.setChecked(isWifiEnabled); + } if (isDeviceLocked) { mWifiToggleTitleText.setTextAppearance((mConnectedWifiEntry != null) ? R.style.TextAppearance_InternetDialog_Active @@ -572,7 +569,7 @@ public class InternetDialog extends SystemUIDialog implements } protected void showProgressBar() { - if (mWifiManager == null || !mWifiManager.isWifiEnabled() + if (!mInternetDialogController.isWifiEnabled() || mInternetDialogController.isDeviceLocked()) { setProgressBarVisible(false); return; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index d97ce7757d8c..90a3d4586fd3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -22,6 +22,7 @@ import static com.android.wifitrackerlib.WifiEntry.CONNECTED_STATE_CONNECTED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.annotation.AnyThread; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -157,6 +158,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi private LocationController mLocationController; private DialogLaunchAnimator mDialogLaunchAnimator; private boolean mHasWifiEntries; + private WifiStateWorker mWifiStateWorker; @VisibleForTesting static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f; @@ -210,7 +212,9 @@ public class InternetDialogController implements AccessPointController.AccessPoi @Background Handler workerHandler, CarrierConfigTracker carrierConfigTracker, LocationController locationController, - DialogLaunchAnimator dialogLaunchAnimator) { + DialogLaunchAnimator dialogLaunchAnimator, + WifiStateWorker wifiStateWorker + ) { if (DEBUG) { Log.d(TAG, "Init InternetDialogController"); } @@ -241,6 +245,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi mLocationController = locationController; mDialogLaunchAnimator = dialogLaunchAnimator; mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor(); + mWifiStateWorker = wifiStateWorker; } void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) { @@ -323,7 +328,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi @Nullable CharSequence getSubtitleText(boolean isProgressBarVisible) { - if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) { + if (mCanConfigWifi && !isWifiEnabled()) { // When Wi-Fi is disabled. // Sub-Title: Wi-Fi is off if (DEBUG) { @@ -648,6 +653,27 @@ public class InternetDialogController implements AccessPointController.AccessPoi startActivity(intent, view); } + /** + * Enable or disable Wi-Fi. + * + * @param enabled {@code true} to enable, {@code false} to disable. + */ + @AnyThread + public void setWifiEnabled(boolean enabled) { + mWifiStateWorker.setWifiEnabled(enabled); + } + + /** + * Return whether Wi-Fi is enabled or disabled. + * + * @return {@code true} if Wi-Fi is enabled or enabling + * @see WifiManager#getWifiState() + */ + @AnyThread + public boolean isWifiEnabled() { + return mWifiStateWorker.isWifiEnabled(); + } + void connectCarrierNetwork() { final MergedCarrierEntry mergedCarrierEntry = mAccessPointController.getMergedCarrierEntry(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/WifiStateWorker.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/WifiStateWorker.java new file mode 100644 index 000000000000..a7ea50e5e6dd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/WifiStateWorker.java @@ -0,0 +1,124 @@ +/* + * 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.systemui.qs.tiles.dialog; + +import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE; +import static android.net.wifi.WifiManager.WIFI_STATE_CHANGED_ACTION; +import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; +import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.wifi.WifiManager; +import android.util.Log; + +import androidx.annotation.AnyThread; +import androidx.annotation.Nullable; + +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import javax.inject.Inject; + +/** + * Worker for the Wi-Fi enabled state cache. + */ +@SysUISingleton +public class WifiStateWorker extends BroadcastReceiver { + + private static final String TAG = "WifiStateWorker"; + + private DelayableExecutor mBackgroundExecutor; + private WifiManager mWifiManager; + private int mWifiState = WIFI_STATE_DISABLED; + + @Inject + public WifiStateWorker( + BroadcastDispatcher broadcastDispatcher, + @Background DelayableExecutor backgroundExecutor, + @Nullable WifiManager wifiManager) { + mWifiManager = wifiManager; + mBackgroundExecutor = backgroundExecutor; + + broadcastDispatcher.registerReceiver(this, new IntentFilter(WIFI_STATE_CHANGED_ACTION)); + mBackgroundExecutor.execute(() -> { + if (mWifiManager == null) return; + + mWifiState = mWifiManager.getWifiState(); + Log.i(TAG, "WifiManager.getWifiState():" + mWifiState); + }); + } + + /** + * Enable or disable Wi-Fi. + * + * @param enabled {@code true} to enable, {@code false} to disable. + */ + @AnyThread + public void setWifiEnabled(boolean enabled) { + mBackgroundExecutor.execute(() -> { + if (mWifiManager == null) return; + + mWifiState = (enabled) ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING; + if (!mWifiManager.setWifiEnabled(enabled)) { + Log.e(TAG, "Failed to WifiManager.setWifiEnabled(" + enabled + ");"); + } + }); + } + + /** + * Gets the Wi-Fi enabled state. + * + * @return One of {@link WifiManager#WIFI_STATE_DISABLED}, + * {@link WifiManager#WIFI_STATE_DISABLING}, {@link WifiManager#WIFI_STATE_ENABLED}, + * {@link WifiManager#WIFI_STATE_ENABLING} + */ + @AnyThread + public int getWifiState() { + return mWifiState; + } + + /** + * Return whether Wi-Fi is enabled or disabled. + * + * @return {@code true} if Wi-Fi is enabled or enabling + * @see WifiManager#getWifiState() + */ + @AnyThread + public boolean isWifiEnabled() { + return (mWifiState == WIFI_STATE_ENABLED || mWifiState == WIFI_STATE_ENABLING); + } + + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null) return; + + if (WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) { + final int wifiState = intent.getIntExtra(EXTRA_WIFI_STATE, WIFI_STATE_DISABLED); + if (wifiState == WIFI_STATE_UNKNOWN) return; + + mWifiState = wifiState; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 270bdc785178..0a616c095551 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -17,6 +17,7 @@ import android.util.MathUtils.lerp import android.view.View import com.android.systemui.animation.Interpolators import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold +import com.android.systemui.util.getColorWithAlpha import java.util.function.Consumer /** @@ -367,7 +368,7 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, } if (startColorAlpha > 0f) { - canvas.drawColor(updateColorAlpha(revealGradientEndColor, startColorAlpha)) + canvas.drawColor(getColorWithAlpha(revealGradientEndColor, startColorAlpha)) } with(shaderGradientMatrix) { @@ -383,15 +384,7 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, private fun setPaintColorFilter() { gradientPaint.colorFilter = PorterDuffColorFilter( - updateColorAlpha(revealGradientEndColor, revealGradientEndColorAlpha), + getColorWithAlpha(revealGradientEndColor, revealGradientEndColorAlpha), PorterDuff.Mode.MULTIPLY) } - - private fun updateColorAlpha(color: Int, alpha: Float): Int = - Color.argb( - (alpha * 255).toInt(), - Color.red(color), - Color.green(color), - Color.blue(color) - ) }
\ No newline at end of file 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 bf27550b331b..36cd173d2d1c 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 @@ -415,6 +415,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private boolean mForwardScrollable; private boolean mBackwardScrollable; private NotificationShelf mShelf; + /** + * Limits the number of visible notifications. The remaining are collapsed in the notification + * shelf. -1 when there is no limit. + */ private int mMaxDisplayedNotifications = -1; private float mKeyguardBottomPadding = -1; @VisibleForTesting int mStatusBarHeight; @@ -1323,7 +1327,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } private float updateStackEndHeight(float height, float bottomMargin, float topPadding) { - final float stackEndHeight = Math.max(0f, height - bottomMargin - topPadding); + final float stackEndHeight; + if (mMaxDisplayedNotifications != -1) { + // The stack intrinsic height already contains the correct value when there is a limit + // in the max number of notifications (e.g. as in keyguard). + stackEndHeight = mIntrinsicContentHeight; + } else { + stackEndHeight = Math.max(0f, height - bottomMargin - topPadding); + } mAmbientState.setStackEndHeight(stackEndHeight); return stackEndHeight; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index ae1fd2b180e5..c2750c2d2a6f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -1207,7 +1207,7 @@ public class NotificationStackScrollLayoutController { */ public void updateShowEmptyShadeView() { Trace.beginSection("NSSLC.updateShowEmptyShadeView"); - mShowEmptyShadeView = mBarState != KEYGUARD + mShowEmptyShadeView = mStatusBarStateController.getCurrentOrUpcomingState() != KEYGUARD && !mView.isQsFullScreen() && getVisibleNotificationCount() == 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index b14e92bf39cf..83017c41629c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -1748,6 +1748,23 @@ public class CentralSurfaces extends CoreStartable implements } @Override + public void onLaunchAnimationStart(boolean isExpandingFullyAbove) { + super.onLaunchAnimationStart(isExpandingFullyAbove); + + // Double check that the keyguard is still showing and not going away, but if so + // set the keyguard occluded. Typically, WM will let KeyguardViewMediator know + // directly, but we're overriding that to play the custom launch animation, so + // we need to take care of that here. The unocclude animation is not overridden, + // so WM will call KeyguardViewMediator's unocclude animation runner when the + // activity is exited. + if (mKeyguardStateController.isShowing() + && !mKeyguardStateController.isKeyguardGoingAway()) { + mKeyguardViewMediator.setOccluded(true /* isOccluded */, + true /* animate */); + } + } + + @Override public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) { // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the // animation so that we can assume that mIsLaunchingActivityOverLockscreen @@ -4514,9 +4531,12 @@ public class CentralSurfaces extends CoreStartable implements * @return UserHandle */ private UserHandle getActivityUserHandle(Intent intent) { - if (intent.getComponent() != null - && mContext.getPackageName().equals(intent.getComponent().getPackageName())) { - return new UserHandle(UserHandle.myUserId()); + String[] packages = mContext.getResources().getStringArray(R.array.system_ui_packages); + for (String pkg : packages) { + if (intent.getComponent() == null) break; + if (pkg.equals(intent.getComponent().getPackageName())) { + return new UserHandle(UserHandle.myUserId()); + } } return UserHandle.CURRENT; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index adf70a255b4f..0b46f07f8e8b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -1233,6 +1233,11 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardBottomArea.initQRCodeScanner(mQRCodeScannerController); } + @VisibleForTesting + void setMaxDisplayedNotifications(int maxAllowed) { + mMaxAllowedKeyguardNotifications = maxAllowed; + } + private void updateMaxDisplayedNotifications(boolean recompute) { if (recompute) { mMaxAllowedKeyguardNotifications = Math.max(computeMaxKeyguardNotifications(), 1); @@ -1463,7 +1468,11 @@ public class NotificationPanelViewController extends PanelViewController { /** * @return the maximum keyguard notifications that can fit on the screen */ - private int computeMaxKeyguardNotifications() { + @VisibleForTesting + int computeMaxKeyguardNotifications() { + if (mAmbientState.getFractionToShade() > 0 || mAmbientState.getDozeAmount() > 0) { + return mMaxAllowedKeyguardNotifications; + } float topPadding = mNotificationStackScrollLayoutController.getTopPadding(); float shelfIntrinsicHeight = mNotificationShelfController.getVisibility() == View.GONE diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 052a4f7c5f7c..639be24ac46e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -390,6 +390,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb && !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) { mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */); } + } else if (!mShowing && mBouncer.inTransit()) { + // Keyguard is not visible anymore, but expansion animation was still running. + // We need to keep propagating the expansion state to the bouncer, otherwise it will be + // stuck in transit. + mBouncer.setExpansion(fraction); } else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) { // Panel expanded while pulsing but didn't translate the bouncer (because we are // unlocked.) Let's simply wake-up to dismiss the lock screen. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java index 2a9048a6eb73..169347a5ac1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java @@ -78,6 +78,7 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout> private final UiEventLogger mUiEventLogger; @VisibleForTesting UserAvatarView mUserAvatarView; + private View mUserAvatarViewWithBackground; UserSwitcherController.UserRecord mCurrentUser; private boolean mIsKeyguardShowing; @@ -167,6 +168,8 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout> super.onInit(); if (DEBUG) Log.d(TAG, "onInit"); mUserAvatarView = mView.findViewById(R.id.kg_multi_user_avatar); + mUserAvatarViewWithBackground = mView.findViewById( + R.id.kg_multi_user_avatar_with_background); mAdapter = new UserSwitcherController.BaseUserAdapter(mUserSwitcherController) { @Override public View getView(int position, View convertView, ViewGroup parent) { @@ -186,7 +189,7 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout> mUiEventLogger.log( LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_SWITCH_USER_TAP); - mUserSwitchDialogController.showDialog(mView); + mUserSwitchDialogController.showDialog(mUserAvatarViewWithBackground); }); mUserAvatarView.setAccessibilityDelegate(new View.AccessibilityDelegate() { diff --git a/packages/SystemUI/src/com/android/systemui/util/ColorUtil.kt b/packages/SystemUI/src/com/android/systemui/util/ColorUtil.kt new file mode 100644 index 000000000000..27a53bf2ceda --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/ColorUtil.kt @@ -0,0 +1,53 @@ +/* + * 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.systemui.util + +import android.content.res.TypedArray +import android.graphics.Color +import android.view.ContextThemeWrapper + +/** Returns an ARGB color version of [color] at the given [alpha]. */ +fun getColorWithAlpha(color: Int, alpha: Float): Int = + Color.argb( + (alpha * 255).toInt(), + Color.red(color), + Color.green(color), + Color.blue(color) + ) + + +/** + * Returns the color provided at the specified {@param attrIndex} in {@param a} if it exists, + * otherwise, returns the color from the private attribute {@param privAttrId}. + */ +fun getPrivateAttrColorIfUnset( + ctw: ContextThemeWrapper, attrArray: TypedArray, + attrIndex: Int, defColor: Int, privAttrId: Int +): Int { + // If the index is specified, use that value + var a = attrArray + if (a.hasValue(attrIndex)) { + return a.getColor(attrIndex, defColor) + } + + // Otherwise fallback to the value of the private attribute + val customAttrs = intArrayOf(privAttrId) + a = ctw.obtainStyledAttributes(customAttrs) + val color = a.getColor(0, defColor) + a.recycle() + return color +} diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java index 8e5e1d2e1b87..5b5dca30620a 100644 --- a/packages/SystemUI/src/com/android/systemui/util/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java @@ -105,25 +105,6 @@ public class Utils { } /** - * Returns the color provided at the specified {@param attrIndex} in {@param a} if it exists, - * otherwise, returns the color from the private attribute {@param privAttrId}. - */ - public static int getPrivateAttrColorIfUnset(ContextThemeWrapper ctw, TypedArray a, - int attrIndex, int defColor, int privAttrId) { - // If the index is specified, use that value - if (a.hasValue(attrIndex)) { - return a.getColor(attrIndex, defColor); - } - - // Otherwise fallback to the value of the private attribute - int[] customAttrs = { privAttrId }; - a = ctw.obtainStyledAttributes(customAttrs); - int color = a.getColor(0, defColor); - a.recycle(); - return color; - } - - /** * Gets the {@link R.dimen#status_bar_header_height_keyguard}. */ public static int getStatusBarHeaderHeightKeyguard(Context context) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt index 23129d247ad5..6a9bb3e343be 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt @@ -6,8 +6,10 @@ import android.testing.TestableLooper import android.view.View import android.view.ViewGroup import android.widget.LinearLayout +import android.widget.RelativeLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.util.children import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertNotNull @@ -28,18 +30,11 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { private val TEST_INTERPOLATOR = Interpolators.LINEAR } - private val childParams = LinearLayout.LayoutParams( - 0 /* width */, - LinearLayout.LayoutParams.MATCH_PARENT - ) - private lateinit var rootView: LinearLayout + private lateinit var rootView: ViewGroup @Before fun setUp() { rootView = LinearLayout(mContext) - rootView.orientation = LinearLayout.HORIZONTAL - rootView.weightSum = 1f - childParams.weight = 0.5f } @After @@ -93,6 +88,19 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { animator = rootView.getTag(R.id.tag_animator) as ObjectAnimator assertEquals(animator.interpolator, TEST_INTERPOLATOR) assertEquals(animator.duration, TEST_DURATION) + + // animateRemoval() + setUpRootWithChildren() + val child = rootView.getChildAt(0) + success = ViewHierarchyAnimator.animateRemoval( + child, interpolator = TEST_INTERPOLATOR, duration = TEST_DURATION + ) + + assertTrue(success) + assertNotNull(child.getTag(R.id.tag_animator)) + animator = child.getTag(R.id.tag_animator) as ObjectAnimator + assertEquals(animator.interpolator, TEST_INTERPOLATOR) + assertEquals(animator.duration, TEST_DURATION) } @Test @@ -170,17 +178,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { @Test fun animatesRootAndChildren() { - val firstChild = View(mContext) - firstChild.layoutParams = childParams - rootView.addView(firstChild) - val secondChild = View(mContext) - secondChild.layoutParams = childParams - rootView.addView(secondChild) - rootView.measure( - View.MeasureSpec.makeMeasureSpec(150, View.MeasureSpec.EXACTLY), - View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) - ) - rootView.layout(0 /* l */, 0 /* t */, 150 /* r */, 100 /* b */) + setUpRootWithChildren() val success = ViewHierarchyAnimator.animate(rootView) // Change all bounds. @@ -192,20 +190,20 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { assertTrue(success) assertNotNull(rootView.getTag(R.id.tag_animator)) - assertNotNull(firstChild.getTag(R.id.tag_animator)) - assertNotNull(secondChild.getTag(R.id.tag_animator)) + assertNotNull(rootView.getChildAt(0).getTag(R.id.tag_animator)) + assertNotNull(rootView.getChildAt(1).getTag(R.id.tag_animator)) // The initial values should be those of the previous layout. - checkBounds(rootView, l = 0, t = 0, r = 150, b = 100) - checkBounds(firstChild, l = 0, t = 0, r = 75, b = 100) - checkBounds(secondChild, l = 75, t = 0, r = 150, b = 100) + checkBounds(rootView, l = 0, t = 0, r = 200, b = 100) + checkBounds(rootView.getChildAt(0), l = 0, t = 0, r = 100, b = 100) + checkBounds(rootView.getChildAt(1), l = 100, t = 0, r = 200, b = 100) endAnimation(rootView) assertNull(rootView.getTag(R.id.tag_animator)) - assertNull(firstChild.getTag(R.id.tag_animator)) - assertNull(secondChild.getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(0).getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(1).getTag(R.id.tag_animator)) // The end values should be those of the latest layout. checkBounds(rootView, l = 10, t = 20, r = 200, b = 120) - checkBounds(firstChild, l = 0, t = 0, r = 95, b = 100) - checkBounds(secondChild, l = 95, t = 0, r = 190, b = 100) + checkBounds(rootView.getChildAt(0), l = 0, t = 0, r = 95, b = 100) + checkBounds(rootView.getChildAt(1), l = 95, t = 0, r = 190, b = 100) } @Test @@ -522,6 +520,251 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { endAnimation(rootView) } + fun animatesViewRemovalFromStartToEnd() { + setUpRootWithChildren() + + val child = rootView.getChildAt(0) + val success = ViewHierarchyAnimator.animateRemoval( + child, + destination = ViewHierarchyAnimator.Hotspot.LEFT, + interpolator = Interpolators.LINEAR + ) + + assertTrue(success) + assertNotNull(child.getTag(R.id.tag_animator)) + checkBounds(child, l = 0, t = 0, r = 100, b = 100) + advanceAnimation(child, 0.5f) + checkBounds(child, l = 0, t = 0, r = 50, b = 100) + advanceAnimation(child, 1.0f) + checkBounds(child, l = 0, t = 0, r = 0, b = 100) + endAnimation(rootView) + endAnimation(child) + assertEquals(1, rootView.childCount) + assertFalse(child in rootView.children) + } + + @Test + fun animatesViewRemovalRespectingDestination() { + // CENTER + setUpRootWithChildren() + var removedChild = rootView.getChildAt(0) + var remainingChild = rootView.getChildAt(1) + var success = ViewHierarchyAnimator.animateRemoval( + removedChild, destination = ViewHierarchyAnimator.Hotspot.CENTER + ) + // Ensure that the layout happens before the checks. + forceLayout() + + assertTrue(success) + assertNotNull(removedChild.getTag(R.id.tag_animator)) + advanceAnimation(removedChild, 1.0f) + checkBounds(removedChild, l = 50, t = 50, r = 50, b = 50) + endAnimation(rootView) + endAnimation(removedChild) + checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) + + // LEFT + setUpRootWithChildren() + removedChild = rootView.getChildAt(0) + remainingChild = rootView.getChildAt(1) + success = ViewHierarchyAnimator.animateRemoval( + removedChild, destination = ViewHierarchyAnimator.Hotspot.LEFT + ) + // Ensure that the layout happens before the checks. + forceLayout() + + assertTrue(success) + assertNotNull(removedChild.getTag(R.id.tag_animator)) + advanceAnimation(removedChild, 1.0f) + checkBounds(removedChild, l = 0, t = 0, r = 0, b = 100) + endAnimation(rootView) + endAnimation(removedChild) + checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) + + // TOP_LEFT + setUpRootWithChildren() + removedChild = rootView.getChildAt(0) + remainingChild = rootView.getChildAt(1) + success = ViewHierarchyAnimator.animateRemoval( + removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP_LEFT + ) + // Ensure that the layout happens before the checks. + forceLayout() + + assertTrue(success) + assertNotNull(removedChild.getTag(R.id.tag_animator)) + advanceAnimation(removedChild, 1.0f) + checkBounds(removedChild, l = 0, t = 0, r = 0, b = 0) + endAnimation(rootView) + endAnimation(removedChild) + checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) + + // TOP + setUpRootWithChildren() + removedChild = rootView.getChildAt(0) + remainingChild = rootView.getChildAt(1) + success = ViewHierarchyAnimator.animateRemoval( + removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP + ) + // Ensure that the layout happens before the checks. + forceLayout() + + assertTrue(success) + assertNotNull(removedChild.getTag(R.id.tag_animator)) + advanceAnimation(removedChild, 1.0f) + checkBounds(removedChild, l = 0, t = 0, r = 100, b = 0) + endAnimation(rootView) + endAnimation(removedChild) + checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) + + // TOP_RIGHT + setUpRootWithChildren() + removedChild = rootView.getChildAt(0) + remainingChild = rootView.getChildAt(1) + success = ViewHierarchyAnimator.animateRemoval( + removedChild, destination = ViewHierarchyAnimator.Hotspot.TOP_RIGHT + ) + // Ensure that the layout happens before the checks. + forceLayout() + + assertTrue(success) + assertNotNull(removedChild.getTag(R.id.tag_animator)) + advanceAnimation(removedChild, 1.0f) + checkBounds(removedChild, l = 100, t = 0, r = 100, b = 0) + endAnimation(rootView) + endAnimation(removedChild) + checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) + + // RIGHT + setUpRootWithChildren() + removedChild = rootView.getChildAt(0) + remainingChild = rootView.getChildAt(1) + success = ViewHierarchyAnimator.animateRemoval( + removedChild, destination = ViewHierarchyAnimator.Hotspot.RIGHT + ) + // Ensure that the layout happens before the checks. + forceLayout() + + assertTrue(success) + assertNotNull(removedChild.getTag(R.id.tag_animator)) + advanceAnimation(removedChild, 1.0f) + checkBounds(removedChild, l = 100, t = 0, r = 100, b = 100) + endAnimation(rootView) + endAnimation(removedChild) + checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) + + // BOTTOM_RIGHT + setUpRootWithChildren() + removedChild = rootView.getChildAt(0) + remainingChild = rootView.getChildAt(1) + success = ViewHierarchyAnimator.animateRemoval( + removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM_RIGHT + ) + // Ensure that the layout happens before the checks. + forceLayout() + + assertTrue(success) + assertNotNull(removedChild.getTag(R.id.tag_animator)) + advanceAnimation(removedChild, 1.0f) + checkBounds(removedChild, l = 100, t = 100, r = 100, b = 100) + endAnimation(rootView) + endAnimation(removedChild) + checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) + + // BOTTOM + setUpRootWithChildren() + removedChild = rootView.getChildAt(0) + remainingChild = rootView.getChildAt(1) + success = ViewHierarchyAnimator.animateRemoval( + removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM + ) + // Ensure that the layout happens before the checks. + forceLayout() + + assertTrue(success) + assertNotNull(removedChild.getTag(R.id.tag_animator)) + advanceAnimation(removedChild, 1.0f) + checkBounds(removedChild, l = 0, t = 100, r = 100, b = 100) + endAnimation(rootView) + endAnimation(removedChild) + checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) + + // BOTTOM_LEFT + setUpRootWithChildren() + removedChild = rootView.getChildAt(0) + remainingChild = rootView.getChildAt(1) + success = ViewHierarchyAnimator.animateRemoval( + removedChild, destination = ViewHierarchyAnimator.Hotspot.BOTTOM_LEFT + ) + // Ensure that the layout happens before the checks. + forceLayout() + + assertTrue(success) + assertNotNull(removedChild.getTag(R.id.tag_animator)) + advanceAnimation(removedChild, 1.0f) + checkBounds(removedChild, l = 0, t = 100, r = 0, b = 100) + endAnimation(rootView) + endAnimation(removedChild) + checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) + } + + @Test + fun animatesChildrenDuringViewRemoval() { + setUpRootWithChildren() + + val child = rootView.getChildAt(0) as ViewGroup + val firstGrandChild = child.getChildAt(0) + val secondGrandChild = child.getChildAt(1) + val success = ViewHierarchyAnimator.animateRemoval( + child, interpolator = Interpolators.LINEAR + ) + + assertTrue(success) + assertNotNull(child.getTag(R.id.tag_animator)) + assertNotNull(firstGrandChild.getTag(R.id.tag_animator)) + assertNotNull(secondGrandChild.getTag(R.id.tag_animator)) + checkBounds(child, l = 0, t = 0, r = 100, b = 100) + checkBounds(firstGrandChild, l = 0, t = 0, r = 40, b = 40) + checkBounds(secondGrandChild, l = 60, t = 60, r = 100, b = 100) + + advanceAnimation(child, 0.5f) + checkBounds(child, l = 25, t = 25, r = 75, b = 75) + checkBounds(firstGrandChild, l = -10, t = -10, r = 30, b = 30) + checkBounds(secondGrandChild, l = 20, t = 20, r = 60, b = 60) + + advanceAnimation(child, 1.0f) + checkBounds(child, l = 50, t = 50, r = 50, b = 50) + checkBounds(firstGrandChild, l = -20, t = -20, r = 20, b = 20) + checkBounds(secondGrandChild, l = -20, t = -20, r = 20, b = 20) + + endAnimation(rootView) + endAnimation(child) + } + + @Test + fun animatesSiblingsDuringViewRemoval() { + setUpRootWithChildren() + + val removedChild = rootView.getChildAt(0) + val remainingChild = rootView.getChildAt(1) + val success = ViewHierarchyAnimator.animateRemoval( + removedChild, interpolator = Interpolators.LINEAR + ) + // Ensure that the layout happens before the checks. + forceLayout() + + assertTrue(success) + assertNotNull(remainingChild.getTag(R.id.tag_animator)) + checkBounds(remainingChild, l = 100, t = 0, r = 200, b = 100) + advanceAnimation(rootView, 0.5f) + checkBounds(remainingChild, l = 50, t = 0, r = 150, b = 100) + advanceAnimation(rootView, 1.0f) + checkBounds(remainingChild, l = 0, t = 0, r = 100, b = 100) + endAnimation(rootView) + endAnimation(removedChild) + assertNull(remainingChild.getTag(R.id.tag_animator)) + } + @Test fun cleansUpListenersCorrectly() { val firstChild = View(mContext) @@ -700,6 +943,49 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { checkBounds(rootView, l = 10, t = 10, r = 50, b = 50) } + private fun setUpRootWithChildren() { + rootView = LinearLayout(mContext) + (rootView as LinearLayout).orientation = LinearLayout.HORIZONTAL + (rootView as LinearLayout).weightSum = 1f + + val firstChild = RelativeLayout(mContext) + rootView.addView(firstChild) + val firstGrandChild = View(mContext) + firstChild.addView(firstGrandChild) + val secondGrandChild = View(mContext) + firstChild.addView(secondGrandChild) + val secondChild = View(mContext) + rootView.addView(secondChild) + + val childParams = LinearLayout.LayoutParams( + 0 /* width */, + LinearLayout.LayoutParams.MATCH_PARENT + ) + childParams.weight = 0.5f + firstChild.layoutParams = childParams + secondChild.layoutParams = childParams + firstGrandChild.layoutParams = RelativeLayout.LayoutParams(40 /* width */, 40 /* height */) + (firstGrandChild.layoutParams as RelativeLayout.LayoutParams) + .addRule(RelativeLayout.ALIGN_PARENT_START) + (firstGrandChild.layoutParams as RelativeLayout.LayoutParams) + .addRule(RelativeLayout.ALIGN_PARENT_TOP) + secondGrandChild.layoutParams = RelativeLayout.LayoutParams(40 /* width */, 40 /* height */) + (secondGrandChild.layoutParams as RelativeLayout.LayoutParams) + .addRule(RelativeLayout.ALIGN_PARENT_END) + (secondGrandChild.layoutParams as RelativeLayout.LayoutParams) + .addRule(RelativeLayout.ALIGN_PARENT_BOTTOM) + + forceLayout() + } + + private fun forceLayout() { + rootView.measure( + View.MeasureSpec.makeMeasureSpec(200 /* width */, View.MeasureSpec.AT_MOST), + View.MeasureSpec.makeMeasureSpec(100 /* height */, View.MeasureSpec.AT_MOST) + ) + rootView.layout(0 /* l */, 0 /* t */, 200 /* r */, 100 /* b */) + } + private fun checkBounds(v: View, l: Int, t: Int, r: Int, b: Int) { assertEquals(l, v.left) assertEquals(t, v.top) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt index 8f967ab5294f..65d501442d87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/ColorSchemeTransitionTest.kt @@ -19,9 +19,9 @@ package com.android.systemui.media import org.mockito.Mockito.`when` as whenever import android.animation.ValueAnimator import android.graphics.Color -import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.monet.ColorScheme import junit.framework.Assert.assertEquals @@ -46,28 +46,35 @@ class ColorSchemeTransitionTest : SysuiTestCase() { private interface ExtractCB : (ColorScheme) -> Int private interface ApplyCB : (Int) -> Unit - private lateinit var colorTransition: ColorTransition + private lateinit var colorTransition: AnimatingColorTransition private lateinit var colorSchemeTransition: ColorSchemeTransition - @Mock private lateinit var mockTransition: ColorTransition + @Mock private lateinit var mockAnimatingTransition: AnimatingColorTransition + @Mock private lateinit var mockGenericTransition: GenericColorTransition @Mock private lateinit var valueAnimator: ValueAnimator @Mock private lateinit var colorScheme: ColorScheme @Mock private lateinit var extractColor: ExtractCB @Mock private lateinit var applyColor: ApplyCB - private lateinit var transitionFactory: ColorTransitionFactory + private lateinit var animatingColorTransitionFactory: AnimatingColorTransitionFactory + private lateinit var genericColorTransitionFactory: GenericColorTransitionFactory @Mock private lateinit var mediaViewHolder: MediaViewHolder @JvmField @Rule val mockitoRule = MockitoJUnit.rule() @Before fun setUp() { - transitionFactory = { default, extractColor, applyColor -> mockTransition } + animatingColorTransitionFactory = { _, _, _ -> mockAnimatingTransition } + genericColorTransitionFactory = { _ -> mockGenericTransition } whenever(extractColor.invoke(colorScheme)).thenReturn(TARGET_COLOR) - colorSchemeTransition = ColorSchemeTransition(context, mediaViewHolder, transitionFactory) + colorSchemeTransition = ColorSchemeTransition( + context, mediaViewHolder, animatingColorTransitionFactory, genericColorTransitionFactory + ) - colorTransition = object : ColorTransition(DEFAULT_COLOR, extractColor, applyColor) { + colorTransition = object : AnimatingColorTransition( + DEFAULT_COLOR, extractColor, applyColor + ) { override fun buildAnimator(): ValueAnimator { return valueAnimator } @@ -142,6 +149,7 @@ class ColorSchemeTransitionTest : SysuiTestCase() { @Test fun testColorSchemeTransition_update() { colorSchemeTransition.updateColorScheme(colorScheme) - verify(mockTransition, times(6)).updateColorScheme(colorScheme) + verify(mockAnimatingTransition, times(6)).updateColorScheme(colorScheme) + verify(mockGenericTransition).updateColorScheme(colorScheme) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index 6a9c3e349522..b8c85bb41726 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -25,8 +25,12 @@ import org.mockito.Mockito.`when` as whenever import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color import android.graphics.drawable.Animatable2 import android.graphics.drawable.AnimatedVectorDrawable +import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.Icon import android.graphics.drawable.RippleDrawable @@ -124,7 +128,7 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var falsingManager: FalsingManager @Mock private lateinit var transitionParent: ViewGroup private lateinit var appIcon: ImageView - private lateinit var albumView: ImageView + @Mock private lateinit var albumView: ImageView private lateinit var titleText: TextView private lateinit var artistText: TextView private lateinit var seamless: ViewGroup @@ -296,7 +300,6 @@ public class MediaControlPanelTest : SysuiTestCase() { // Set up mock views for the players appIcon = ImageView(context) - albumView = ImageView(context) titleText = TextView(context) artistText = TextView(context) seamless = FrameLayout(context) @@ -416,7 +419,6 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(coverContainer1.context).thenReturn(mockContext) whenever(coverContainer2.context).thenReturn(mockContext) whenever(coverContainer3.context).thenReturn(mockContext) - } @After @@ -537,6 +539,60 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test + fun bindAlbumView_setAfterExecutors() { + val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) + val canvas = Canvas(bmp) + canvas.drawColor(Color.RED) + val albumArt = Icon.createWithBitmap(bmp) + val state = mediaData.copy(artwork = albumArt) + + player.attachPlayer(viewHolder) + player.bindPlayer(state, PACKAGE) + bgExecutor.runAllReady() + mainExecutor.runAllReady() + + verify(albumView).setImageDrawable(any(Drawable::class.java)) + } + + @Test + fun bindAlbumView_bitmapInLaterStates_setAfterExecutors() { + val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) + val canvas = Canvas(bmp) + canvas.drawColor(Color.RED) + val albumArt = Icon.createWithBitmap(bmp) + + val state0 = mediaData.copy(artwork = null) + val state1 = mediaData.copy(artwork = albumArt) + val state2 = mediaData.copy(artwork = albumArt) + player.attachPlayer(viewHolder) + + // First binding sets (empty) drawable + player.bindPlayer(state0, PACKAGE) + bgExecutor.runAllReady() + mainExecutor.runAllReady() + verify(albumView).setImageDrawable(any(Drawable::class.java)) + + // Run Metadata update so that later states don't update + val captor = argumentCaptor<Animator.AnimatorListener>() + verify(mockAnimator, times(2)).addListener(captor.capture()) + captor.value.onAnimationEnd(mockAnimator) + assertThat(titleText.getText()).isEqualTo(TITLE) + assertThat(artistText.getText()).isEqualTo(ARTIST) + + // Second binding sets transition drawable + player.bindPlayer(state1, PACKAGE) + bgExecutor.runAllReady() + mainExecutor.runAllReady() + verify(albumView, times(2)).setImageDrawable(any(Drawable::class.java)) + + // Third binding does run transition or update background + player.bindPlayer(state2, PACKAGE) + bgExecutor.runAllReady() + mainExecutor.runAllReady() + verify(albumView, times(2)).setImageDrawable(any(Drawable::class.java)) + } + + @Test fun bind_seekBarDisabled_hasActions_seekBarVisibilityIsSetToInvisible() { useRealConstraintSets() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java index 633a9c3a03d8..4a8cb0b76dc4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java @@ -138,6 +138,8 @@ public class InternetDialogControllerTest extends SysuiTestCase { private DialogLaunchAnimator mDialogLaunchAnimator; @Mock private View mDialogLaunchView; + @Mock + private WifiStateWorker mWifiStateWorker; private TestableResources mTestableResources; private InternetDialogController mInternetDialogController; @@ -166,6 +168,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { when(mSystemUIToast.getView()).thenReturn(mToastView); when(mSystemUIToast.getGravity()).thenReturn(GRAVITY_FLAGS); when(mSystemUIToast.getInAnimation()).thenReturn(mAnimator); + when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); mInternetDialogController = new InternetDialogController(mContext, mock(UiEventLogger.class), mock(ActivityStarter.class), mAccessPointController, @@ -173,7 +176,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher, mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController, mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker, - mLocationController, mDialogLaunchAnimator); + mLocationController, mDialogLaunchAnimator, mWifiStateWorker); mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, mInternetDialogController.mOnSubscriptionsChangedListener); mInternetDialogController.onStart(mInternetDialogCallback, true); @@ -239,7 +242,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { @Test public void getSubtitleText_withApmOnAndWifiOff_returnWifiIsOff() { fakeAirplaneModeEnabled(true); - when(mWifiManager.isWifiEnabled()).thenReturn(false); + when(mWifiStateWorker.isWifiEnabled()).thenReturn(false); assertThat(mInternetDialogController.getSubtitleText(false)) .isEqualTo(getResourcesString("wifi_is_off")); @@ -254,7 +257,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { @Test public void getSubtitleText_withWifiOff_returnWifiIsOff() { fakeAirplaneModeEnabled(false); - when(mWifiManager.isWifiEnabled()).thenReturn(false); + when(mWifiStateWorker.isWifiEnabled()).thenReturn(false); assertThat(mInternetDialogController.getSubtitleText(false)) .isEqualTo(getResourcesString("wifi_is_off")); @@ -269,7 +272,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { @Test public void getSubtitleText_withNoWifiEntry_returnSearchWifi() { fakeAirplaneModeEnabled(false); - when(mWifiManager.isWifiEnabled()).thenReturn(true); + when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); assertThat(mInternetDialogController.getSubtitleText(true)) @@ -286,7 +289,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { public void getSubtitleText_withWifiEntry_returnTapToConnect() { // The preconditions WiFi Entries is already in setUp() fakeAirplaneModeEnabled(false); - when(mWifiManager.isWifiEnabled()).thenReturn(true); + when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); assertThat(mInternetDialogController.getSubtitleText(false)) .isEqualTo(getResourcesString("tap_a_network_to_connect")); @@ -301,7 +304,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { @Test public void getSubtitleText_deviceLockedWithWifiOn_returnUnlockToViewNetworks() { fakeAirplaneModeEnabled(false); - when(mWifiManager.isWifiEnabled()).thenReturn(true); + when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); when(mKeyguardStateController.isUnlocked()).thenReturn(false); assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false), @@ -311,7 +314,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { @Test public void getSubtitleText_withNoService_returnNoNetworksAvailable() { fakeAirplaneModeEnabled(false); - when(mWifiManager.isWifiEnabled()).thenReturn(true); + when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState(); @@ -325,7 +328,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { @Test public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() { fakeAirplaneModeEnabled(false); - when(mWifiManager.isWifiEnabled()).thenReturn(true); + when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState(); @@ -346,7 +349,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { @Test public void getSubtitleText_withCarrierNetworkActiveOnly_returnNoOtherAvailable() { fakeAirplaneModeEnabled(false); - when(mWifiManager.isWifiEnabled()).thenReturn(true); + when(mWifiStateWorker.isWifiEnabled()).thenReturn(true); mInternetDialogController.onAccessPointsChanged(null /* accessPoints */); when(mMergedCarrierEntry.isDefaultNetwork()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java index 616f89455c74..d09a5a11040f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java @@ -14,7 +14,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.net.wifi.WifiManager; import android.os.Handler; import android.telephony.TelephonyManager; import android.testing.AndroidTestingRunner; @@ -64,8 +63,6 @@ public class InternetDialogTest extends SysuiTestCase { @Mock private TelephonyManager mTelephonyManager; @Mock - private WifiManager mWifiManager; - @Mock private WifiEntry mInternetWifiEntry; @Mock private List<WifiEntry> mWifiEntries; @@ -97,7 +94,6 @@ public class InternetDialogTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt()); - when(mWifiManager.isWifiEnabled()).thenReturn(true); when(mInternetWifiEntry.getTitle()).thenReturn(WIFI_TITLE); when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY); when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true); @@ -107,7 +103,7 @@ public class InternetDialogTest extends SysuiTestCase { when(mInternetDialogController.getMobileNetworkTitle()).thenReturn(MOBILE_NETWORK_TITLE); when(mInternetDialogController.getMobileNetworkSummary()) .thenReturn(MOBILE_NETWORK_SUMMARY); - when(mInternetDialogController.getWifiManager()).thenReturn(mWifiManager); + when(mInternetDialogController.isWifiEnabled()).thenReturn(true); mMockitoSession = ExtendedMockito.mockitoSession() .spyStatic(WifiEnterpriseRestrictionUtils.class) @@ -232,7 +228,7 @@ public class InternetDialogTest extends SysuiTestCase { // Carrier network should be gone if airplane mode ON and Wi-Fi is off. when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true); when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); - when(mWifiManager.isWifiEnabled()).thenReturn(false); + when(mInternetDialogController.isWifiEnabled()).thenReturn(false); mInternetDialog.updateDialog(true); @@ -241,7 +237,7 @@ public class InternetDialogTest extends SysuiTestCase { // Carrier network should be visible if airplane mode ON and Wi-Fi is ON. when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true); when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true); - when(mWifiManager.isWifiEnabled()).thenReturn(true); + when(mInternetDialogController.isWifiEnabled()).thenReturn(true); mInternetDialog.updateDialog(true); @@ -468,7 +464,7 @@ public class InternetDialogTest extends SysuiTestCase { @Test public void updateDialog_wifiOffAndWifiScanOff_hideWifiScanNotify() { - when(mWifiManager.isWifiEnabled()).thenReturn(false); + when(mInternetDialogController.isWifiEnabled()).thenReturn(false); when(mInternetDialogController.isWifiScanEnabled()).thenReturn(false); mInternetDialog.updateDialog(false); @@ -478,7 +474,7 @@ public class InternetDialogTest extends SysuiTestCase { @Test public void updateDialog_wifiOffAndWifiScanOnAndDeviceLocked_hideWifiScanNotify() { - when(mWifiManager.isWifiEnabled()).thenReturn(false); + when(mInternetDialogController.isWifiEnabled()).thenReturn(false); when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true); when(mInternetDialogController.isDeviceLocked()).thenReturn(true); @@ -489,7 +485,7 @@ public class InternetDialogTest extends SysuiTestCase { @Test public void updateDialog_wifiOffAndWifiScanOnAndDeviceUnlocked_showWifiScanNotify() { - when(mWifiManager.isWifiEnabled()).thenReturn(false); + when(mInternetDialogController.isWifiEnabled()).thenReturn(false); when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true); when(mInternetDialogController.isDeviceLocked()).thenReturn(false); @@ -502,6 +498,26 @@ public class InternetDialogTest extends SysuiTestCase { } @Test + public void updateDialog_wifiIsDisabled_uncheckWifiSwitch() { + when(mInternetDialogController.isWifiEnabled()).thenReturn(false); + mWifiToggleSwitch.setChecked(true); + + mInternetDialog.updateDialog(false); + + assertThat(mWifiToggleSwitch.isChecked()).isFalse(); + } + + @Test + public void updateDialog_wifiIsEnabled_checkWifiSwitch() { + when(mInternetDialogController.isWifiEnabled()).thenReturn(true); + mWifiToggleSwitch.setChecked(false); + + mInternetDialog.updateDialog(false); + + assertThat(mWifiToggleSwitch.isChecked()).isTrue(); + } + + @Test public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() { mSeeAll.performClick(); @@ -512,7 +528,7 @@ public class InternetDialogTest extends SysuiTestCase { @Test public void showProgressBar_wifiDisabled_hideProgressBar() { Mockito.reset(mHandler); - when(mWifiManager.isWifiEnabled()).thenReturn(false); + when(mInternetDialogController.isWifiEnabled()).thenReturn(false); mInternetDialog.showProgressBar(); @@ -534,7 +550,7 @@ public class InternetDialogTest extends SysuiTestCase { @Test public void showProgressBar_wifiEnabledWithWifiEntry_showProgressBarThenHide() { Mockito.reset(mHandler); - when(mWifiManager.isWifiEnabled()).thenReturn(true); + when(mInternetDialogController.isWifiEnabled()).thenReturn(true); mInternetDialog.showProgressBar(); @@ -553,7 +569,7 @@ public class InternetDialogTest extends SysuiTestCase { @Test public void showProgressBar_wifiEnabledWithoutWifiEntries_showProgressBarThenHideSearch() { Mockito.reset(mHandler); - when(mWifiManager.isWifiEnabled()).thenReturn(true); + when(mInternetDialogController.isWifiEnabled()).thenReturn(true); mInternetDialog.mConnectedWifiEntry = null; mInternetDialog.mWifiEntriesCount = 0; diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java new file mode 100644 index 000000000000..5d7ba7bc673d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java @@ -0,0 +1,205 @@ +/* + * 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.systemui.qs.tiles.dialog; + +import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE; +import static android.net.wifi.WifiManager.WIFI_STATE_CHANGED_ACTION; +import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; +import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Intent; +import android.net.wifi.WifiManager; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class WifiStateWorkerTest extends SysuiTestCase { + + @Rule + public MockitoRule mRule = MockitoJUnit.rule(); + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private WifiManager mWifiManager; + @Mock + private Intent mIntent; + + private WifiStateWorker mWifiStateWorker; + private FakeExecutor mBackgroundExecutor = new FakeExecutor(new FakeSystemClock()); + + @Before + public void setup() { + when(mWifiManager.setWifiEnabled(anyBoolean())).thenReturn(true); + when(mWifiManager.getWifiState()).thenReturn(WIFI_STATE_ENABLED); + when(mIntent.getAction()).thenReturn(WIFI_STATE_CHANGED_ACTION); + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_ENABLED); + + mWifiStateWorker = new WifiStateWorker(mBroadcastDispatcher, mBackgroundExecutor, + mWifiManager); + mBackgroundExecutor.runAllReady(); + } + + @Test + public void constructor_shouldGetWifiState() { + verify(mWifiManager).getWifiState(); + } + + @Test + public void setWifiEnabled_wifiManagerIsNull_shouldNotSetWifiEnabled() { + mWifiStateWorker = new WifiStateWorker(mBroadcastDispatcher, mBackgroundExecutor, + null /* wifiManager */); + + mWifiStateWorker.setWifiEnabled(true); + mBackgroundExecutor.runAllReady(); + + verify(mWifiManager, never()).setWifiEnabled(anyBoolean()); + } + + @Test + public void setWifiEnabled_enabledIsTrue_shouldSetWifiEnabled() { + mWifiStateWorker.setWifiEnabled(true); + mBackgroundExecutor.runAllReady(); + + verify(mWifiManager).setWifiEnabled(true); + } + + @Test + public void setWifiEnabled_enabledIsFalse_shouldSetWifiDisabled() { + mWifiStateWorker.setWifiEnabled(false); + mBackgroundExecutor.runAllReady(); + + verify(mWifiManager).setWifiEnabled(false); + } + + @Test + public void getWifiState_receiveWifiStateDisabling_getWifiStateDisabling() { + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_DISABLING); + mWifiStateWorker.onReceive(mContext, mIntent); + + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_DISABLING); + } + + @Test + public void getWifiState_receiveWifiStateDisabled_getWifiStateDisabled() { + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_DISABLED); + mWifiStateWorker.onReceive(mContext, mIntent); + + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_DISABLED); + } + + @Test + public void getWifiState_receiveWifiStateEnabling_getWifiStateEnabling() { + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_ENABLING); + mWifiStateWorker.onReceive(mContext, mIntent); + + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_ENABLING); + } + + @Test + public void getWifiState_receiveWifiStateEnabled_getWifiStateEnabled() { + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_ENABLED); + mWifiStateWorker.onReceive(mContext, mIntent); + + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_ENABLED); + } + + @Test + public void getWifiState_receiveWifiStateUnknown_ignoreTheIntent() { + // Update the Wi-Fi state to WIFI_STATE_DISABLED + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_DISABLED); + mWifiStateWorker.onReceive(mContext, mIntent); + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_DISABLED); + + // Receiver WIFI_STATE_UNKNOWN + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_UNKNOWN); + mWifiStateWorker.onReceive(mContext, mIntent); + + // Ignore the intent and keep the Wi-Fi state to WIFI_STATE_DISABLED + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_DISABLED); + + // Update the Wi-Fi state to WIFI_STATE_ENABLED + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_ENABLED); + mWifiStateWorker.onReceive(mContext, mIntent); + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_ENABLED); + + // Receiver WIFI_STATE_UNKNOWN change + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_UNKNOWN); + mWifiStateWorker.onReceive(mContext, mIntent); + + // Ignore the intent and keep the Wi-Fi state to WIFI_STATE_ENABLED + assertThat(mWifiStateWorker.getWifiState()).isEqualTo(WIFI_STATE_ENABLED); + } + + @Test + public void isWifiEnabled_receiveWifiStateDisabling_returnFalse() { + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_DISABLING); + mWifiStateWorker.onReceive(mContext, mIntent); + + assertThat(mWifiStateWorker.isWifiEnabled()).isFalse(); + } + + @Test + public void isWifiEnabled_receiveWifiStateDisabled_returnFalse() { + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_DISABLED); + mWifiStateWorker.onReceive(mContext, mIntent); + + assertThat(mWifiStateWorker.isWifiEnabled()).isFalse(); + } + + @Test + public void isWifiEnabled_receiveWifiStateEnabling_returnTrue() { + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_ENABLING); + mWifiStateWorker.onReceive(mContext, mIntent); + + assertThat(mWifiStateWorker.isWifiEnabled()).isTrue(); + } + + @Test + public void isWifiEnabled_receiveWifiStateEnabled_returnTrue() { + when(mIntent.getIntExtra(eq(EXTRA_WIFI_STATE), anyInt())).thenReturn(WIFI_STATE_ENABLED); + mWifiStateWorker.onReceive(mContext, mIntent); + + assertThat(mWifiStateWorker.isWifiEnabled()).isTrue(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index c9de60806b66..6409967aca9b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -228,19 +228,15 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { public void testUpdateEmptyShadeView_notificationsVisible_zenHiding() { when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); mController.attach(mNotificationStackScrollLayout); - verify(mSysuiStatusBarStateController).addCallback( - mStateListenerArgumentCaptor.capture(), anyInt()); - StatusBarStateController.StateListener stateListener = - mStateListenerArgumentCaptor.getValue(); - setupShowEmptyShadeViewState(stateListener, true); + setupShowEmptyShadeViewState(true); reset(mNotificationStackScrollLayout); mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( /* visible= */ true, /* notifVisibleInShade= */ true); - setupShowEmptyShadeViewState(stateListener, false); + setupShowEmptyShadeViewState(false); reset(mNotificationStackScrollLayout); mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( @@ -252,19 +248,15 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { public void testUpdateEmptyShadeView_notificationsHidden_zenNotHiding() { when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); mController.attach(mNotificationStackScrollLayout); - verify(mSysuiStatusBarStateController).addCallback( - mStateListenerArgumentCaptor.capture(), anyInt()); - StatusBarStateController.StateListener stateListener = - mStateListenerArgumentCaptor.getValue(); - setupShowEmptyShadeViewState(stateListener, true); + setupShowEmptyShadeViewState(true); reset(mNotificationStackScrollLayout); mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( /* visible= */ true, /* notifVisibleInShade= */ false); - setupShowEmptyShadeViewState(stateListener, false); + setupShowEmptyShadeViewState(false); reset(mNotificationStackScrollLayout); mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( @@ -407,15 +399,13 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { return argThat(new LogMatcher(category, type)); } - private void setupShowEmptyShadeViewState( - StatusBarStateController.StateListener statusBarStateListener, - boolean toShow) { + private void setupShowEmptyShadeViewState(boolean toShow) { if (toShow) { - statusBarStateListener.onStateChanged(SHADE); + when(mSysuiStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(SHADE); mController.setQsFullScreen(false); mController.getView().removeAllViews(); } else { - statusBarStateListener.onStateChanged(KEYGUARD); + when(mSysuiStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD); mController.setQsFullScreen(true); mController.getView().addContainerView(mock(ExpandableNotificationRow.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index 71f1f0b0f7cf..cad603c85bbc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -558,6 +558,37 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test + public void computeMaxKeyguardNotifications_lockscreenToShade_returnsExistingMax() { + when(mAmbientState.getFractionToShade()).thenReturn(0.5f); + mNotificationPanelViewController.setMaxDisplayedNotifications(-1); + + // computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value + assertThat(mNotificationPanelViewController.computeMaxKeyguardNotifications()) + .isEqualTo(-1); + } + + @Test + public void computeMaxKeyguardNotifications_dozeAmountNotZero_returnsExistingMax() { + when(mAmbientState.getDozeAmount()).thenReturn(0.5f); + mNotificationPanelViewController.setMaxDisplayedNotifications(-1); + + // computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value + assertThat(mNotificationPanelViewController.computeMaxKeyguardNotifications()) + .isEqualTo(-1); + } + + @Test + public void computeMaxKeyguardNotifications_noTransition_updatesMax() { + when(mAmbientState.getFractionToShade()).thenReturn(0f); + when(mAmbientState.getDozeAmount()).thenReturn(0f); + mNotificationPanelViewController.setMaxDisplayedNotifications(-1); + + // computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value + assertThat(mNotificationPanelViewController.computeMaxKeyguardNotifications()) + .isNotEqualTo(-1); + } + + @Test public void testSetPanelScrimMinFraction() { mNotificationPanelViewController.setPanelScrimMinFraction(0.5f); verify(mNotificationShadeDepthController).setPanelPullDownMinFraction(eq(0.5f)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index a94ad0b763aa..38e018b42985 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -192,6 +192,15 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + public void onPanelExpansionChanged_propagatesToBouncer_evenAfterHidden() { + mStatusBarKeyguardViewManager.hide(0, 0); + when(mBouncer.inTransit()).thenReturn(true); + + mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT); + verify(mBouncer).setExpansion(eq(EXPANSION_EVENT.getFraction())); + } + + @Test public void onPanelExpansionChanged_showsBouncerWhenSwiping() { when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false); mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 8c4db70c301d..aa5c501cd94e 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -805,12 +805,13 @@ final class AutofillManagerServiceImpl * Updates the last fill response when a dataset was selected. */ void logDatasetSelected(@Nullable String selectedDataset, int sessionId, - @Nullable Bundle clientState) { + @Nullable Bundle clientState, int presentationType) { synchronized (mLock) { if (isValidEventLocked("logDatasetSelected()", sessionId)) { mEventHistory.addEvent( new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null, - null, null, null, null, null, null, null)); + null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE, + presentationType)); } } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 7e277ba3c0d0..f18d13d30da6 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -21,6 +21,7 @@ import static android.service.autofill.AutofillService.EXTRA_FILL_RESPONSE; import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG; import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE; import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU; +import static android.service.autofill.FillEventHistory.Event.UI_TYPE_UNKNOWN; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG; @@ -1406,7 +1407,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // AutoFillUiCallback @Override - public void fill(int requestId, int datasetIndex, Dataset dataset) { + public void fill(int requestId, int datasetIndex, Dataset dataset, int uiType) { synchronized (mLock) { if (mDestroyed) { Slog.w(TAG, "Call to Session#fill() rejected - session: " @@ -1416,7 +1417,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } mHandler.sendMessage(obtainMessage( Session::autoFill, - this, requestId, datasetIndex, dataset, true)); + this, requestId, datasetIndex, dataset, true, uiType)); } // AutoFillUiCallback @@ -1657,7 +1658,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (!isAuthResultDatasetEphemeral(oldDataset, data)) { authenticatedResponse.getDatasets().set(datasetIdx, dataset); } - autoFill(requestId, datasetIdx, dataset, false); + autoFill(requestId, datasetIdx, dataset, false, UI_TYPE_UNKNOWN); } else { Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id " + authenticationId); @@ -3376,7 +3377,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState new InlineFillUi.InlineSuggestionUiCallback() { @Override public void autofill(@NonNull Dataset dataset, int datasetIndex) { - fill(response.getRequestId(), datasetIndex, dataset); + fill(response.getRequestId(), datasetIndex, dataset, UI_TYPE_INLINE); } @Override @@ -3895,7 +3896,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return viewState; } - void autoFill(int requestId, int datasetIndex, Dataset dataset, boolean generateEvent) { + void autoFill(int requestId, int datasetIndex, Dataset dataset, boolean generateEvent, + int uiType) { if (sDebug) { Slog.d(TAG, "autoFill(): requestId=" + requestId + "; datasetIdx=" + datasetIndex + "; dataset=" + dataset); @@ -3909,7 +3911,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Autofill it directly... if (dataset.getAuthentication() == null) { if (generateEvent) { - mService.logDatasetSelected(dataset.getId(), id, mClientState); + mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType); } if (mCurrentViewId != null) { mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index 056ab92fffb2..57768ef32391 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -15,6 +15,9 @@ */ package com.android.server.autofill.ui; +import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG; +import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU; + import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sVerbose; @@ -31,6 +34,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.service.autofill.Dataset; +import android.service.autofill.FillEventHistory; import android.service.autofill.FillResponse; import android.service.autofill.SaveInfo; import android.service.autofill.ValueFinder; @@ -81,7 +85,8 @@ public final class AutoFillUI { public interface AutoFillUiCallback { void authenticate(int requestId, int datasetIndex, @NonNull IntentSender intent, @Nullable Bundle extras, boolean authenticateInline); - void fill(int requestId, int datasetIndex, @NonNull Dataset dataset); + void fill(int requestId, int datasetIndex, @NonNull Dataset dataset, + @FillEventHistory.Event.UiType int uiType); void save(); void cancelSave(); void requestShowFillUi(AutofillId id, int width, int height, @@ -236,7 +241,8 @@ public final class AutoFillUI { hideFillUiUiThread(callback, true); if (mCallback != null) { final int datasetIndex = response.getDatasets().indexOf(dataset); - mCallback.fill(response.getRequestId(), datasetIndex, dataset); + mCallback.fill(response.getRequestId(), datasetIndex, + dataset, UI_TYPE_MENU); } } @@ -414,7 +420,8 @@ public final class AutoFillUI { hideFillDialogUiThread(callback); if (mCallback != null) { final int datasetIndex = response.getDatasets().indexOf(dataset); - mCallback.fill(response.getRequestId(), datasetIndex, dataset); + mCallback.fill(response.getRequestId(), datasetIndex, dataset, + UI_TYPE_DIALOG); } } diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java index 9894a52b58e1..6e28d8fbad59 100644 --- a/services/core/java/com/android/server/am/AppBatteryTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryTracker.java @@ -276,7 +276,9 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> AppBackgroundRestrictionsInfo.REASON_UNKNOWN, // ExemptionReason AppBackgroundRestrictionsInfo.UNKNOWN, // OptimizationLevel AppBackgroundRestrictionsInfo.SDK_UNKNOWN, // TargetSdk - isLowRamDeviceStatic()); + isLowRamDeviceStatic(), + AppBackgroundRestrictionsInfo.LEVEL_UNKNOWN // previous RestrictionLevel + ); } } } @@ -304,11 +306,17 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_BACKGROUND]; final double usageFgs = bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND_SERVICE]; + final double usageForeground = + bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND]; + final double usageCached = + bgUsage.mPercentage[BatteryUsage.BATTERY_USAGE_INDEX_CACHED]; if (DEBUG_BACKGROUND_BATTERY_TRACKER_VERBOSE) { Slog.d(TAG, "getBatteryTrackerInfoProtoLocked uid:" + uid + " allUsage:" + String.format("%4.2f%%", allUsage) + " usageBackground:" + String.format("%4.2f%%", usageBackground) - + " usageFgs:" + String.format("%4.2f%%", usageFgs)); + + " usageFgs:" + String.format("%4.2f%%", usageFgs) + + " usageForeground:" + String.format("%4.2f%%", usageForeground) + + " usageCached:" + String.format("%4.2f%%", usageCached)); } final ProtoOutputStream proto = new ProtoOutputStream(); proto.write(AppBackgroundRestrictionsInfo.BatteryTrackerInfo.BATTERY_24H, @@ -317,6 +325,10 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> usageBackground * 10000); proto.write(AppBackgroundRestrictionsInfo.BatteryTrackerInfo.BATTERY_USAGE_FGS, usageFgs * 10000); + proto.write(AppBackgroundRestrictionsInfo.BatteryTrackerInfo.BATTERY_USAGE_FOREGROUND, + usageForeground * 10000); + proto.write(AppBackgroundRestrictionsInfo.BatteryTrackerInfo.BATTERY_USAGE_CACHED, + usageCached * 10000); proto.flush(); return proto.getBytes(); } diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index 0c1ab8178b38..f7abb117e3de 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -2086,6 +2086,9 @@ public final class AppRestrictionController { int curLevel; int prevReason; final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal(); + if (trackerInfo == null) { + trackerInfo = mEmptyTrackerInfo; + } synchronized (mSettingsLock) { curLevel = getRestrictionLevel(uid, pkgName); if (curLevel == level) { @@ -2138,14 +2141,21 @@ public final class AppRestrictionController { // It's currently active, enqueue it. final int localReason = reason; final int localSubReason = subReason; - mActiveUids.add(uid, pkgName, () -> appStandbyInternal.restrictApp( - pkgName, UserHandle.getUserId(uid), localReason, localSubReason)); + final TrackerInfo localTrackerInfo = trackerInfo; + mActiveUids.add(uid, pkgName, () -> { + appStandbyInternal.restrictApp(pkgName, UserHandle.getUserId(uid), + localReason, localSubReason); + logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level, + localTrackerInfo, localReason); + }); doIt = false; } } if (doIt) { appStandbyInternal.restrictApp(pkgName, UserHandle.getUserId(uid), reason, subReason); + logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level, trackerInfo, + reason); } } } else if (curLevel >= RESTRICTION_LEVEL_RESTRICTED_BUCKET @@ -2160,11 +2170,14 @@ public final class AppRestrictionController { appStandbyInternal.maybeUnrestrictApp(pkgName, UserHandle.getUserId(uid), prevReason & REASON_MAIN_MASK, prevReason & REASON_SUB_MASK, reason, subReason); + logAppBackgroundRestrictionInfo(pkgName, uid, curLevel, level, trackerInfo, + reason); } + } - if (trackerInfo == null) { - trackerInfo = mEmptyTrackerInfo; - } + private void logAppBackgroundRestrictionInfo(String pkgName, int uid, + @RestrictionLevel int prevLevel, @RestrictionLevel int level, + @NonNull TrackerInfo trackerInfo, int reason) { FrameworkStatsLog.write(FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO, uid, getRestrictionLevelStatsd(level), getThresholdStatsd(reason), @@ -2176,7 +2189,8 @@ public final class AppRestrictionController { getExemptionReasonStatsd(uid, level), getOptimizationLevelStatsd(level), getTargetSdkStatsd(pkgName), - ActivityManager.isLowRamDeviceStatic()); + ActivityManager.isLowRamDeviceStatic(), + getRestrictionLevelStatsd(prevLevel)); } private void handleBackgroundRestrictionChanged(int uid, String pkgName, boolean restricted) { @@ -2449,7 +2463,8 @@ public final class AppRestrictionController { mBgController.getBackgroundRestrictionExemptionReason(uid)), AppBackgroundRestrictionsInfo.UNKNOWN, // OptimizationLevel AppBackgroundRestrictionsInfo.SDK_UNKNOWN, // TargetSdk - ActivityManager.isLowRamDeviceStatic()); + ActivityManager.isLowRamDeviceStatic(), + mBgController.getRestrictionLevel(uid)); PendingIntent pendingIntent; if (!mBgController.mConstantsObserver.mBgPromptFgsWithNotiOnLongRunning && mBgController.hasForegroundServiceNotifications(packageName, uid)) { diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index c48ff9f9f2cc..2dadcecc9f1f 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -97,6 +97,7 @@ public class SettingsToPropertiesMapper { DeviceConfig.NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT, DeviceConfig.NAMESPACE_SWCODEC_NATIVE, DeviceConfig.NAMESPACE_TETHERING, + DeviceConfig.NAMESPACE_VENDOR_SYSTEM_NATIVE, DeviceConfig.NAMESPACE_VIRTUALIZATION_FRAMEWORK_NATIVE, DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT, }; diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java index a76eb8f1e55d..90b1b634f2ee 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceFactoryImpl.java @@ -20,9 +20,11 @@ import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityTaskManager; +import android.app.IActivityTaskManager; import android.content.Context; import android.content.Intent; import android.os.ServiceManager; +import android.os.UserHandle; import android.service.games.GameService; import android.service.games.GameSessionService; import android.service.games.IGameService; @@ -47,14 +49,17 @@ final class GameServiceProviderInstanceFactoryImpl implements GameServiceProvide @Override public GameServiceProviderInstance create( @NonNull GameServiceComponentConfiguration configuration) { + final UserHandle userHandle = configuration.getUserHandle(); + final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService(); return new GameServiceProviderInstanceImpl( - configuration.getUserHandle(), + userHandle, BackgroundThread.getExecutor(), mContext, - new GameClassifierImpl(mContext.getPackageManager()), + new GameTaskInfoProvider(userHandle, activityTaskManager, + new GameClassifierImpl(mContext.getPackageManager())), ActivityManager.getService(), LocalServices.getService(ActivityManagerInternal.class), - ActivityTaskManager.getService(), + activityTaskManager, (WindowManagerService) ServiceManager.getService(Context.WINDOW_SERVICE), LocalServices.getService(WindowManagerInternal.class), new GameServiceConnector(mContext, configuration), diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java index b38195aed250..a200067b8f3f 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java @@ -64,7 +64,6 @@ import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener; import com.android.server.wm.WindowManagerService; -import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @@ -218,7 +217,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan private final UserHandle mUserHandle; private final Executor mBackgroundExecutor; private final Context mContext; - private final GameClassifier mGameClassifier; + private final GameTaskInfoProvider mGameTaskInfoProvider; private final IActivityManager mActivityManager; private final ActivityManagerInternal mActivityManagerInternal; private final IActivityTaskManager mActivityTaskManager; @@ -244,7 +243,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan @NonNull UserHandle userHandle, @NonNull Executor backgroundExecutor, @NonNull Context context, - @NonNull GameClassifier gameClassifier, + @NonNull GameTaskInfoProvider gameTaskInfoProvider, @NonNull IActivityManager activityManager, @NonNull ActivityManagerInternal activityManagerInternal, @NonNull IActivityTaskManager activityTaskManager, @@ -256,7 +255,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan mUserHandle = userHandle; mBackgroundExecutor = backgroundExecutor; mContext = context; - mGameClassifier = gameClassifier; + mGameTaskInfoProvider = gameTaskInfoProvider; mActivityManager = activityManager; mActivityManagerInternal = activityManagerInternal; mActivityTaskManager = activityTaskManager; @@ -344,13 +343,14 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } private void onTaskCreated(int taskId, @NonNull ComponentName componentName) { - String packageName = componentName.getPackageName(); - if (!mGameClassifier.isGame(packageName, mUserHandle)) { + final GameTaskInfo taskInfo = mGameTaskInfoProvider.get(taskId, componentName); + + if (!taskInfo.mIsGameTask) { return; } synchronized (mLock) { - gameTaskStartedLocked(taskId, componentName); + gameTaskStartedLocked(taskInfo); } } @@ -367,7 +367,17 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } final GameSessionRecord gameSessionRecord = mGameSessions.get(taskId); - if (gameSessionRecord == null || gameSessionRecord.getGameSession() == null) { + if (gameSessionRecord == null) { + if (focused) { + // The game session for a game task may have been destroyed when the game task + // was put into the background by pressing the back button. If the task is restored + // via the Recents UI there will be no TaskStackListener#onCreated call for the + // restoration, so this focus event is the first opportunity to re-create the game + // session. + maybeCreateGameSessionForFocusedTaskLocked(taskId); + } + return; + } else if (gameSessionRecord.getGameSession() == null) { return; } @@ -379,30 +389,50 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } @GuardedBy("mLock") - private void gameTaskStartedLocked(int taskId, @NonNull ComponentName componentName) { + private void maybeCreateGameSessionForFocusedTaskLocked(int taskId) { + if (DEBUG) { + Slog.d(TAG, "maybeRecreateGameSessionForFocusedTaskLocked() id: " + taskId); + } + + final GameTaskInfo taskInfo = mGameTaskInfoProvider.get(taskId); + if (taskInfo == null) { + Slog.w(TAG, "No task info for focused task: " + taskId); + return; + } + + if (!taskInfo.mIsGameTask) { + return; + } + + gameTaskStartedLocked(taskInfo); + } + + @GuardedBy("mLock") + private void gameTaskStartedLocked(@NonNull GameTaskInfo gameTaskInfo) { if (DEBUG) { - Slog.i(TAG, "gameStartedLocked() id: " + taskId + " component: " + componentName); + Slog.i(TAG, "gameStartedLocked(): " + gameTaskInfo); } if (!mIsRunning) { return; } - GameSessionRecord existingGameSessionRecord = mGameSessions.get(taskId); + GameSessionRecord existingGameSessionRecord = mGameSessions.get(gameTaskInfo.mTaskId); if (existingGameSessionRecord != null) { - Slog.w(TAG, "Existing game session found for task (id: " + taskId + Slog.w(TAG, "Existing game session found for task (id: " + gameTaskInfo.mTaskId + ") creation. Ignoring."); return; } GameSessionRecord gameSessionRecord = GameSessionRecord.awaitingGameSessionRequest( - taskId, componentName); - mGameSessions.put(taskId, gameSessionRecord); + gameTaskInfo.mTaskId, gameTaskInfo.mComponentName); + mGameSessions.put(gameTaskInfo.mTaskId, gameSessionRecord); AndroidFuture<Void> unusedPostGameStartedFuture = mGameServiceConnector.post( gameService -> { gameService.gameStarted( - new GameStartedEvent(taskId, componentName.getPackageName())); + new GameStartedEvent(gameTaskInfo.mTaskId, + gameTaskInfo.mComponentName.getPackageName())); }); } @@ -769,7 +799,7 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan @Nullable private GameSessionViewHostConfiguration createViewHostConfigurationForTask(int taskId) { - RunningTaskInfo runningTaskInfo = getRunningTaskInfoForTask(taskId); + RunningTaskInfo runningTaskInfo = mGameTaskInfoProvider.getRunningTaskInfo(taskId); if (runningTaskInfo == null) { return null; } @@ -781,28 +811,6 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan bounds.height()); } - @Nullable - private RunningTaskInfo getRunningTaskInfoForTask(int taskId) { - List<RunningTaskInfo> runningTaskInfos; - try { - runningTaskInfos = mActivityTaskManager.getTasks( - /* maxNum= */ Integer.MAX_VALUE, - /* filterOnlyVisibleRecents= */ true, - /* keepIntentExtra= */ false); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to fetch running tasks"); - return null; - } - - for (RunningTaskInfo taskInfo : runningTaskInfos) { - if (taskInfo.taskId == taskId) { - return taskInfo; - } - } - - return null; - } - @VisibleForTesting void takeScreenshot(int taskId, @NonNull AndroidFuture callback) { GameSessionRecord gameSessionRecord; @@ -834,7 +842,8 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } else { final Bundle bundle = ScreenshotHelper.HardwareBitmapBundler.hardwareBitmapToBundle( bitmap); - final RunningTaskInfo runningTaskInfo = getRunningTaskInfoForTask(taskId); + final RunningTaskInfo runningTaskInfo = + mGameTaskInfoProvider.getRunningTaskInfo(taskId); if (runningTaskInfo == null) { Slog.w(TAG, "Could not get running task info for id: " + taskId); callback.complete(GameScreenshotResult.createInternalErrorResult()); diff --git a/services/core/java/com/android/server/app/GameTaskInfo.java b/services/core/java/com/android/server/app/GameTaskInfo.java new file mode 100644 index 000000000000..7548dbd06c5c --- /dev/null +++ b/services/core/java/com/android/server/app/GameTaskInfo.java @@ -0,0 +1,66 @@ +/* + * 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.app; + +import android.content.ComponentName; + +import java.util.Objects; + +final class GameTaskInfo { + final int mTaskId; + final boolean mIsGameTask; + final ComponentName mComponentName; + + GameTaskInfo(int taskId, boolean isGameTask, ComponentName componentName) { + mTaskId = taskId; + mIsGameTask = isGameTask; + mComponentName = componentName; + } + + @Override + public String toString() { + return "GameTaskInfo{" + + "mTaskId=" + + mTaskId + + ", mIsGameTask=" + + mIsGameTask + + ", mComponentName=" + + mComponentName + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof GameTaskInfo)) { + return false; + } + + GameTaskInfo that = (GameTaskInfo) o; + return mTaskId == that.mTaskId + && mIsGameTask == that.mIsGameTask + && mComponentName.equals(that.mComponentName); + } + + @Override + public int hashCode() { + return Objects.hash(mTaskId, mIsGameTask, mComponentName); + } +} diff --git a/services/core/java/com/android/server/app/GameTaskInfoProvider.java b/services/core/java/com/android/server/app/GameTaskInfoProvider.java new file mode 100644 index 000000000000..f078d98e5950 --- /dev/null +++ b/services/core/java/com/android/server/app/GameTaskInfoProvider.java @@ -0,0 +1,121 @@ +/* + * 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.app; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager.RunningTaskInfo; +import android.app.IActivityTaskManager; +import android.content.ComponentName; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.LruCache; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.util.List; + +final class GameTaskInfoProvider { + private static final String TAG = "GameTaskInfoProvider"; + private static final int TASK_INFO_CACHE_MAX_SIZE = 50; + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private final LruCache<Integer, GameTaskInfo> mGameTaskInfoCache = new LruCache<>( + TASK_INFO_CACHE_MAX_SIZE); + + private final UserHandle mUserHandle; + private final IActivityTaskManager mActivityTaskManager; + private final GameClassifier mGameClassifier; + + GameTaskInfoProvider(@NonNull UserHandle userHandle, + @NonNull IActivityTaskManager activityTaskManager, + @NonNull GameClassifier gameClassifier) { + mUserHandle = userHandle; + mActivityTaskManager = activityTaskManager; + mGameClassifier = gameClassifier; + } + + @Nullable + GameTaskInfo get(int taskId) { + synchronized (mLock) { + final GameTaskInfo cachedTaskInfo = mGameTaskInfoCache.get(taskId); + if (cachedTaskInfo != null) { + return cachedTaskInfo; + } + } + + final RunningTaskInfo runningTaskInfo = getRunningTaskInfo(taskId); + if (runningTaskInfo == null || runningTaskInfo.baseActivity == null) { + return null; + } + + return generateGameInfo(taskId, runningTaskInfo.baseActivity); + } + + GameTaskInfo get(int taskId, @NonNull ComponentName componentName) { + synchronized (mLock) { + final GameTaskInfo cachedTaskInfo = mGameTaskInfoCache.get(taskId); + if (cachedTaskInfo != null) { + if (cachedTaskInfo.mComponentName.equals(componentName)) { + Slog.w(TAG, "Found cached task info for taskId " + taskId + + " but cached component name " + cachedTaskInfo.mComponentName + + " does not match " + componentName); + } else { + return cachedTaskInfo; + } + } + } + + return generateGameInfo(taskId, componentName); + } + + @Nullable + RunningTaskInfo getRunningTaskInfo(int taskId) { + List<RunningTaskInfo> runningTaskInfos; + try { + runningTaskInfos = mActivityTaskManager.getTasks( + /* maxNum= */ Integer.MAX_VALUE, + /* filterOnlyVisibleRecents= */ false, + /* keepIntentExtra= */ false); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to fetch running tasks"); + return null; + } + + for (RunningTaskInfo taskInfo : runningTaskInfos) { + if (taskInfo.taskId == taskId) { + return taskInfo; + } + } + + return null; + } + + private GameTaskInfo generateGameInfo(int taskId, @NonNull ComponentName componentName) { + final GameTaskInfo gameTaskInfo = new GameTaskInfo(taskId, + mGameClassifier.isGame(componentName.getPackageName(), mUserHandle), componentName); + + synchronized (mLock) { + mGameTaskInfoCache.put(taskId, gameTaskInfo); + } + + return gameTaskInfo; + } +} diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index e14527098a72..dbe4fb8c8795 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -862,8 +862,8 @@ public class AudioDeviceInventory { } /*package*/ void disconnectLeAudio(int device) { - if (device != AudioSystem.DEVICE_OUT_BLE_HEADSET || - device != AudioSystem.DEVICE_OUT_BLE_BROADCAST) { + if (device != AudioSystem.DEVICE_OUT_BLE_HEADSET + && device != AudioSystem.DEVICE_OUT_BLE_BROADCAST) { Log.e(TAG, "disconnectLeAudio: Can't disconnect not LE Audio device " + device); return; } @@ -879,6 +879,8 @@ public class AudioDeviceInventory { new MediaMetrics.Item(mMetricsId + "disconnectLeAudio") .record(); if (toRemove.size() > 0) { + final int delay = checkSendBecomingNoisyIntentInt(device, 0, + AudioSystem.DEVICE_NONE); toRemove.stream().forEach(deviceAddress -> makeLeAudioDeviceUnavailable(deviceAddress, device) ); diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index d10ed55281ef..0aa9a2bc4990 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -462,6 +462,7 @@ public class BtHelper { mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEADSET); mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEARING_AID); mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.LE_AUDIO); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.LE_AUDIO_BROADCAST); } // @GuardedBy("AudioDeviceBroker.mSetModeLock") @@ -687,6 +688,7 @@ public class BtHelper { case BluetoothProfile.HEADSET: case BluetoothProfile.HEARING_AID: case BluetoothProfile.LE_AUDIO: + case BluetoothProfile.LE_AUDIO_BROADCAST: mDeviceBroker.postBtProfileDisconnected(profile); break; diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 698f41f23e98..80ff8349a153 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1389,12 +1389,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Brightness throttling is needed, so do so quickly. // Later, when throttling is removed, we let other mechanisms decide on speed. slowChange = false; - updateScreenBrightnessSetting = true; } mAppliedThrottling = true; } else if (mAppliedThrottling) { mAppliedThrottling = false; - updateScreenBrightnessSetting = true; } if (updateScreenBrightnessSetting) { diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 549b566eeac2..1ea949ede03d 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -174,6 +174,7 @@ import android.net.ConnectivityManager.NetworkCallback; import android.net.INetworkManagementEventObserver; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; +import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkIdentity; @@ -236,6 +237,7 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.SparseLongArray; +import android.util.SparseSetArray; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; @@ -624,6 +626,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Map from network ID to last observed roaming state */ @GuardedBy("mNetworkPoliciesSecondLock") private final SparseBooleanArray mNetworkRoaming = new SparseBooleanArray(); + /** Map from network ID to the last ifaces on it */ + @GuardedBy("mNetworkPoliciesSecondLock") + private SparseSetArray<String> mNetworkToIfaces = new SparseSetArray<>(); /** Map from netId to subId as of last update */ @GuardedBy("mNetworkPoliciesSecondLock") @@ -1328,11 +1333,28 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return changed; } + @GuardedBy("mNetworkPoliciesSecondLock") + private boolean updateNetworkToIfacesNL(int netId, @NonNull ArraySet<String> newIfaces) { + // TODO: Add a facility SparseSetArray.contains(key) to return whether the key exists. + final ArraySet<String> lastIfaces = mNetworkToIfaces.get(netId); + final boolean changed = lastIfaces == null ? true : !lastIfaces.equals(newIfaces); + + if (changed) { + // Changed on the same network should remove last ifaces and add new ifaces. + // TODO: Add a facility SparseSetArray.put(key, value) for replacing the + // value for a given key. + mNetworkToIfaces.remove(netId); + for (String iface : newIfaces) { + mNetworkToIfaces.add(netId, iface); + } + } + return changed; + } + private final NetworkCallback mNetworkCallback = new NetworkCallback() { @Override - public void onCapabilitiesChanged(Network network, - NetworkCapabilities networkCapabilities) { - if (network == null || networkCapabilities == null) return; + public void onCapabilitiesChanged(@NonNull Network network, + @NonNull NetworkCapabilities networkCapabilities) { synchronized (mNetworkPoliciesSecondLock) { final boolean newMetered = !networkCapabilities @@ -1351,6 +1373,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } } + + @Override + public void onLinkPropertiesChanged(@NonNull Network network, @NonNull LinkProperties lp) { + synchronized (mNetworkPoliciesSecondLock) { + final ArraySet<String> newIfaces = new ArraySet<>(lp.getAllInterfaceNames()); + final boolean ifacesChanged = updateNetworkToIfacesNL(network.getNetId(), + newIfaces); + if (ifacesChanged) { + updateNetworkRulesNL(); + } + } + } + + @Override + public void onLost(@NonNull Network network) { + synchronized (mNetworkPoliciesSecondLock) { + mNetworkToIfaces.remove(network.getNetId()); + } + } }; /** diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 21ee4c21ceeb..6078bfc95488 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -252,6 +252,7 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.StatsEvent; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; @@ -284,6 +285,7 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.TriPredicate; +import com.android.internal.widget.LockPatternUtils; import com.android.server.DeviceIdleInternal; import com.android.server.EventLogTags; import com.android.server.IoThread; @@ -1923,6 +1925,54 @@ public class NotificationManagerService extends SystemService { private SettingsObserver mSettingsObserver; protected ZenModeHelper mZenModeHelper; + protected class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker { + + SparseBooleanArray mUserInLockDownMode = new SparseBooleanArray(); + boolean mIsInLockDownMode = false; + + StrongAuthTracker(Context context) { + super(context); + } + + private boolean containsFlag(int haystack, int needle) { + return (haystack & needle) != 0; + } + + public boolean isInLockDownMode() { + return mIsInLockDownMode; + } + + @Override + public synchronized void onStrongAuthRequiredChanged(int userId) { + boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId), + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mUserInLockDownMode.put(userId, userInLockDownModeNext); + boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1; + + if (mIsInLockDownMode == isInLockDownModeNext) { + return; + } + + if (isInLockDownModeNext) { + cancelNotificationsWhenEnterLockDownMode(); + } + + // When the mIsInLockDownMode is true, both notifyPostedLocked and + // notifyRemovedLocked will be dismissed. So we shall call + // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode + // as true and call postNotificationsWhenExitLockDownMode after we set + // mIsInLockDownMode as false. + mIsInLockDownMode = isInLockDownModeNext; + + if (!isInLockDownModeNext) { + postNotificationsWhenExitLockDownMode(); + } + } + } + + private LockPatternUtils mLockPatternUtils; + private StrongAuthTracker mStrongAuthTracker; + public NotificationManagerService(Context context) { this(context, new NotificationRecordLoggerImpl(), @@ -1952,6 +2002,11 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting + void setStrongAuthTracker(StrongAuthTracker strongAuthTracker) { + mStrongAuthTracker = strongAuthTracker; + } + + @VisibleForTesting void setKeyguardManager(KeyguardManager keyguardManager) { mKeyguardManager = keyguardManager; } @@ -2145,6 +2200,8 @@ public class NotificationManagerService extends SystemService { mPlatformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + mLockPatternUtils = new LockPatternUtils(getContext()); + mStrongAuthTracker = new StrongAuthTracker(getContext()); mUiHandler = new Handler(UiThread.get().getLooper()); String[] extractorNames; try { @@ -2641,6 +2698,7 @@ public class NotificationManagerService extends SystemService { bubbsExtractor.setShortcutHelper(mShortcutHelper); } registerNotificationPreferencesPullers(); + mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { // This observer will force an update when observe is called, causing us to // bind to listener services. @@ -9537,6 +9595,29 @@ public class NotificationManagerService extends SystemService { } } + private void cancelNotificationsWhenEnterLockDownMode() { + synchronized (mNotificationLock) { + int numNotifications = mNotificationList.size(); + for (int i = 0; i < numNotifications; i++) { + NotificationRecord rec = mNotificationList.get(i); + mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL, + rec.getStats()); + } + + } + } + + private void postNotificationsWhenExitLockDownMode() { + synchronized (mNotificationLock) { + int numNotifications = mNotificationList.size(); + for (int i = 0; i < numNotifications; i++) { + NotificationRecord rec = mNotificationList.get(i); + mListeners.notifyPostedLocked(rec, rec); + } + + } + } + private void updateNotificationPulse() { synchronized (mNotificationLock) { updateLightsLocked(); @@ -9753,6 +9834,10 @@ public class NotificationManagerService extends SystemService { rankings.toArray(new NotificationListenerService.Ranking[0])); } + boolean isInLockDownMode() { + return mStrongAuthTracker.isInLockDownMode(); + } + boolean hasCompanionDevice(ManagedServiceInfo info) { if (mCompanionManager == null) { mCompanionManager = getCompanionManager(); @@ -10804,8 +10889,12 @@ public class NotificationManagerService extends SystemService { * targetting <= O_MR1 */ @GuardedBy("mNotificationLock") - private void notifyPostedLocked(NotificationRecord r, NotificationRecord old, + void notifyPostedLocked(NotificationRecord r, NotificationRecord old, boolean notifyAllListeners) { + if (isInLockDownMode()) { + return; + } + try { // Lazily initialized snapshots of the notification. StatusBarNotification sbn = r.getSbn(); @@ -10903,6 +10992,10 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") public void notifyRemovedLocked(NotificationRecord r, int reason, NotificationStats notificationStats) { + if (isInLockDownMode()) { + return; + } + final StatusBarNotification sbn = r.getSbn(); // make a copy in case changes are made to the underlying Notification object @@ -10948,6 +11041,10 @@ public class NotificationManagerService extends SystemService { */ @GuardedBy("mNotificationLock") public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) { + if (isInLockDownMode()) { + return; + } + boolean isHiddenRankingUpdate = changedHiddenNotifications != null && changedHiddenNotifications.size() > 0; // TODO (b/73052211): if the ranking update changed the notification type, diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 8bd1da9fbe9b..2d8d4f588192 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -2545,7 +2545,6 @@ final class InstallPackageHelper { ArrayList<String>[] components; int size = 0; int[] uids; - Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mPm.mLock) { final SparseArray<ArrayMap<String, ArrayList<String>>> userIdToPackagesToComponents = @@ -2584,7 +2583,6 @@ final class InstallPackageHelper { mPm.sendPackageChangedBroadcast(snapshot, packages[i], true /* dontKillApp */, components[i], uids[i], null /* reason */); } - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } void handlePackagePostInstall(PackageInstalledInfo res, InstallArgs installArgs, diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java index e8faca9765f8..0dfa31c5f1fc 100644 --- a/services/core/java/com/android/server/pm/PackageHandler.java +++ b/services/core/java/com/android/server/pm/PackageHandler.java @@ -75,7 +75,7 @@ final class PackageHandler extends Handler { try { doHandleMessage(msg); } finally { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); } } @@ -136,19 +136,13 @@ final class PackageHandler extends Handler { } } break; case WRITE_SETTINGS: { - Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); mPm.writeSettings(); - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; case WRITE_PACKAGE_RESTRICTIONS: { - Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); mPm.writePendingRestrictions(); - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; case WRITE_PACKAGE_LIST: { - Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); mPm.writePackageList(msg.arg1); - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } break; case CHECK_PENDING_VERIFICATION: { final int verificationId = msg.arg1; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 88564aa2e935..2cef35fcf0f6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1413,7 +1413,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService t.traceBegin("create package manager"); final PackageManagerTracedLock lock = new PackageManagerTracedLock(); final Object installLock = new Object(); - HandlerThread backgroundThread = new HandlerThread("PackageManagerBg"); + + HandlerThread backgroundThread = new ServiceThread("PackageManagerBg", + Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); backgroundThread.start(); Handler backgroundHandler = new Handler(backgroundThread.getLooper()); @@ -1467,7 +1469,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService (i, pm) -> domainVerificationService, (i, pm) -> { HandlerThread thread = new ServiceThread(TAG, - Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); + Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/); thread.start(); return new PackageHandler(thread.getLooper(), pm); }, diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 78a600e34dae..6b10d4c17527 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -159,13 +159,14 @@ class PackageManagerShellCommand extends ShellCommand { private static final Map<String, Integer> SUPPORTED_PERMISSION_FLAGS = new ArrayMap<>(); private static final List<String> SUPPORTED_PERMISSION_FLAGS_LIST; static { + SUPPORTED_PERMISSION_FLAGS_LIST = List.of("review-required", "revoked-compat", + "revoke-when-requested", "user-fixed", "user-set"); SUPPORTED_PERMISSION_FLAGS.put("user-set", FLAG_PERMISSION_USER_SET); SUPPORTED_PERMISSION_FLAGS.put("user-fixed", FLAG_PERMISSION_USER_FIXED); SUPPORTED_PERMISSION_FLAGS.put("revoked-compat", FLAG_PERMISSION_REVOKED_COMPAT); SUPPORTED_PERMISSION_FLAGS.put("review-required", FLAG_PERMISSION_REVIEW_REQUIRED); SUPPORTED_PERMISSION_FLAGS.put("revoke-when-requested", FLAG_PERMISSION_REVOKE_WHEN_REQUESTED); - SUPPORTED_PERMISSION_FLAGS_LIST = new ArrayList<>(SUPPORTED_PERMISSION_FLAGS.keySet()); } final IPackageManager mInterface; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index a1b4b30c18cd..ba4d09f28d05 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -164,6 +164,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.Random; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; @@ -633,8 +634,8 @@ public final class Settings implements Watchable, Snappable { runtimePermissionsPersistence, new Consumer<Integer>() { @Override public void accept(Integer userId) { - mRuntimePermissionsPersistence.writeStateForUser(userId, - mPermissionDataProvider, mPackages, mSharedUsers, mHandler, mLock); + mRuntimePermissionsPersistence.writeStateForUser(userId, mPermissionDataProvider, + mPackages, mSharedUsers, mHandler, mLock, /*sync=*/false); } }); mPermissionDataProvider = permissionDataProvider; @@ -5292,7 +5293,7 @@ public final class Settings implements Watchable, Snappable { public void writePermissionStateForUserLPr(int userId, boolean sync) { if (sync) { mRuntimePermissionsPersistence.writeStateForUser(userId, mPermissionDataProvider, - mPackages, mSharedUsers, /*handler=*/null, mLock); + mPackages, mSharedUsers, /*handler=*/null, mLock, /*sync=*/true); } else { mRuntimePermissionsPersistence.writeStateForUserAsync(userId); } @@ -5370,12 +5371,17 @@ public final class Settings implements Watchable, Snappable { } private static final class RuntimePermissionPersistence { - private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 200; + // 200-400ms delay to avoid monopolizing PMS lock when written for multiple users. + private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 300; + private static final double WRITE_PERMISSIONS_DELAY_JITTER = 0.3; + private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000; private static final int UPGRADE_VERSION = -1; private static final int INITIAL_VERSION = 0; + private static final Random sRandom = new Random(); + private String mExtendedFingerprint; @GuardedBy("mPersistenceLock") @@ -5397,6 +5403,11 @@ public final class Settings implements Watchable, Snappable { private final SparseLongArray mLastNotWrittenMutationTimesMillis = new SparseLongArray(); @GuardedBy("mLock") + // Tracking the mutations that haven't yet been written to legacy state. + // This avoids unnecessary work when writing settings for multiple users. + private boolean mIsLegacyPermissionStateStale = false; + + @GuardedBy("mLock") // The mapping keys are user ids. private final SparseIntArray mVersions = new SparseIntArray(); @@ -5472,9 +5483,22 @@ public final class Settings implements Watchable, Snappable { return PackagePartitions.FINGERPRINT + "?pc_version=" + version; } + private static long uniformRandom(double low, double high) { + double mag = high - low; + return (long) (sRandom.nextDouble() * mag + low); + } + + private static long nextWritePermissionDelayMillis() { + final long delay = WRITE_PERMISSIONS_DELAY_MILLIS; + final double jitter = WRITE_PERMISSIONS_DELAY_JITTER; + return delay + uniformRandom(-jitter * delay, jitter * delay); + } + public void writeStateForUserAsync(int userId) { synchronized (mLock) { + mIsLegacyPermissionStateStale = true; final long currentTimeMillis = SystemClock.uptimeMillis(); + final long writePermissionDelayMillis = nextWritePermissionDelayMillis(); if (mWriteScheduled.get(userId)) { mAsyncHandler.removeMessages(userId); @@ -5493,7 +5517,7 @@ public final class Settings implements Watchable, Snappable { // Hold off a bit more as settings are frequently changing. final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis + MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0); - final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS, + final long writeDelayMillis = Math.min(writePermissionDelayMillis, maxDelayMillis); Message message = mAsyncHandler.obtainMessage(userId); @@ -5501,7 +5525,7 @@ public final class Settings implements Watchable, Snappable { } else { mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis); Message message = mAsyncHandler.obtainMessage(userId); - mAsyncHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS); + mAsyncHandler.sendMessageDelayed(message, writePermissionDelayMillis); mWriteScheduled.put(userId, true); } } @@ -5511,21 +5535,27 @@ public final class Settings implements Watchable, Snappable { legacyPermissionDataProvider, @NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates, @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers, - @Nullable Handler pmHandler, @NonNull Object pmLock) { + @Nullable Handler pmHandler, @NonNull Object pmLock, + boolean sync) { final int version; final String fingerprint; + final boolean isLegacyPermissionStateStale; synchronized (mLock) { mAsyncHandler.removeMessages(userId); mWriteScheduled.delete(userId); version = mVersions.get(userId, INITIAL_VERSION); fingerprint = mFingerprints.get(userId); + isLegacyPermissionStateStale = mIsLegacyPermissionStateStale; + mIsLegacyPermissionStateStale = false; } Runnable writer = () -> { final RuntimePermissionsState runtimePermissions; synchronized (pmLock) { - legacyPermissionDataProvider.writeLegacyPermissionStateTEMP(); + if (sync || isLegacyPermissionStateStale) { + legacyPermissionDataProvider.writeLegacyPermissionStateTEMP(); + } Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions = new ArrayMap<>(); diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 0311524cd768..284c29ecfbe9 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -21,6 +21,7 @@ import static android.os.Process.FIRST_APPLICATION_UID; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.DownloadManager; import android.app.SearchManager; @@ -1093,6 +1094,14 @@ final class DefaultPermissionGrantPolicy { } } + public void grantDefaultPermissionsToCarrierServiceApp(@NonNull String packageName, + @UserIdInt int userId) { + Log.i(TAG, "Grant permissions to Carrier Service app " + packageName + " for user:" + + userId); + grantPermissionsToPackage(NO_PM_CACHE, packageName, userId, /* ignoreSystemPackage */ false, + /* whitelistRestricted */ true, NOTIFICATION_PERMISSIONS); + } + private String getDefaultSystemHandlerActivityPackage(PackageManagerWrapper pm, String intentAction, int userId) { return getDefaultSystemHandlerActivityPackage(pm, new Intent(intentAction), userId); diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java index ea554d3d7996..360a04f7e9bc 100644 --- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java @@ -18,6 +18,7 @@ package com.android.server.pm.permission; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.content.Context; @@ -248,6 +249,15 @@ public class LegacyPermissionManagerService extends ILegacyPermissionManager.Stu } @Override + public void grantDefaultPermissionsToCarrierServiceApp(@NonNull String packageName, + @UserIdInt int userId) { + PackageManagerServiceUtils.enforceSystemOrRoot( + "grantDefaultPermissionsForCarrierServiceApp"); + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToCarrierServiceApp(packageName, userId)); + } + + @Override public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); PackageManagerServiceUtils.enforceSystemOrPhoneCaller( diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index 092f3bec4012..d9e74f8a6afd 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -767,8 +767,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; flagValues &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; flagValues &= ~PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; - // REVIEW_REQUIRED can only be set by non-system apps for POST_NOTIFICATIONS, or by the - // shell or root UID. + // REVIEW_REQUIRED can be set on any permission by the shell or the root uid, or by + // any app for the POST_NOTIFICATIONS permission specifically. if (!POST_NOTIFICATIONS.equals(permName) && callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) { flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 89ac9e773906..14abc9aabc29 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -66,11 +66,13 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.permission.LegacyPermissionManager; import android.permission.PermissionControllerManager; import android.permission.PermissionManager; import android.provider.Settings; import android.provider.Telephony; import android.telecom.TelecomManager; +import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -106,6 +108,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.ExecutionException; /** @@ -163,6 +166,7 @@ public final class PermissionPolicyService extends SystemService { private PackageManagerInternal mPackageManagerInternal; private PermissionManagerServiceInternal mPermissionManagerInternal; private NotificationManagerInternal mNotificationManager; + private TelephonyManager mTelephonyManager; private final KeyguardManager mKeyguardManager; private final PackageManager mPackageManager; private final Handler mHandler; @@ -384,6 +388,13 @@ public final class PermissionPolicyService extends SystemService { public void onBootPhase(int phase) { if (DEBUG) Slog.i(LOG_TAG, "onBootPhase(" + phase + ")"); + if (phase == PHASE_DEVICE_SPECIFIC_SERVICES_READY) { + registerCarrierPrivilegesCallbacks(); + IntentFilter filter = + new IntentFilter(TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED); + mContext.registerReceiver(mSimConfigBroadcastReceiver, filter); + } + if (phase == PHASE_ACTIVITY_MANAGER_READY) { final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class); @@ -408,6 +419,94 @@ public final class PermissionPolicyService extends SystemService { } + private void initTelephonyManagerIfNeeded() { + if (mTelephonyManager == null) { + mTelephonyManager = TelephonyManager.from(mContext); + } + } + + private void registerCarrierPrivilegesCallbacks() { + initTelephonyManagerIfNeeded(); + if (mTelephonyManager == null) { + return; + } + + int numPhones = mTelephonyManager.getActiveModemCount(); + for (int i = 0; i < numPhones; i++) { + PhoneCarrierPrivilegesCallback callback = new PhoneCarrierPrivilegesCallback(i); + mPhoneCarrierPrivilegesCallbacks.add(callback); + mTelephonyManager.registerCarrierPrivilegesCallback(i, mContext.getMainExecutor(), + callback); + } + } + + private void unregisterCarrierPrivilegesCallback() { + initTelephonyManagerIfNeeded(); + if (mTelephonyManager == null) { + return; + } + + for (int i = 0; i < mPhoneCarrierPrivilegesCallbacks.size(); i++) { + PhoneCarrierPrivilegesCallback callback = mPhoneCarrierPrivilegesCallbacks.get(i); + if (callback != null) { + mTelephonyManager.unregisterCarrierPrivilegesCallback(callback); + } + } + mPhoneCarrierPrivilegesCallbacks.clear(); + } + + private final class PhoneCarrierPrivilegesCallback + implements TelephonyManager.CarrierPrivilegesCallback { + private int mPhoneId; + + PhoneCarrierPrivilegesCallback(int phoneId) { + mPhoneId = phoneId; + } + @Override + public void onCarrierPrivilegesChanged( + @NonNull Set<String> privilegedPackageNames, + @NonNull Set<Integer> privilegedUids) { + initTelephonyManagerIfNeeded(); + if (mTelephonyManager == null) { + Log.e(LOG_TAG, "Cannot grant default permissions to Carrier Service app. " + + "TelephonyManager is null"); + return; + } + + String servicePkg = mTelephonyManager.getCarrierServicePackageNameForLogicalSlot( + mPhoneId); + if (servicePkg == null) { + return; + } + int[] users = LocalServices.getService(UserManagerInternal.class).getUserIds(); + LegacyPermissionManager legacyPermManager = + mContext.getSystemService(LegacyPermissionManager.class); + for (int i = 0; i < users.length; i++) { + try { + mPackageManager.getPackageInfoAsUser(servicePkg, 0, users[i]); + legacyPermManager.grantDefaultPermissionsToCarrierServiceApp( + servicePkg, users[i]); + } catch (PackageManager.NameNotFoundException e) { + // Do nothing if the package does not exist for the specified user + } + } + } + } + + private final ArrayList<PhoneCarrierPrivilegesCallback> mPhoneCarrierPrivilegesCallbacks = + new ArrayList<>(); + + private final BroadcastReceiver mSimConfigBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED.equals(intent.getAction())) { + return; + } + unregisterCarrierPrivilegesCallback(); + registerCarrierPrivilegesCallbacks(); + } + }; + /** * @return Whether the user is started but not yet stopped */ diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java index ae23b9e46d23..5db4a7b4a6f6 100644 --- a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java +++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java @@ -268,9 +268,17 @@ final class SpeechRecognitionManagerServiceImpl extends } private boolean componentMapsToRecognitionService(@NonNull ComponentName serviceComponent) { - List<ResolveInfo> resolveInfos = - getContext().getPackageManager().queryIntentServicesAsUser( - new Intent(RecognitionService.SERVICE_INTERFACE), 0, getUserId()); + List<ResolveInfo> resolveInfos; + + final long identityToken = Binder.clearCallingIdentity(); + try { + resolveInfos = + getContext().getPackageManager().queryIntentServicesAsUser( + new Intent(RecognitionService.SERVICE_INTERFACE), 0, getUserId()); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + if (resolveInfos == null) { return false; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b6a1784839de..b2f9b52cad8d 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -661,10 +661,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** * The activity is opaque and fills the entire space of this task. - * @see WindowContainer#fillsParent() + * @see #occludesParent() */ private boolean mOccludesParent; + /** + * Unlike {@link #mOccludesParent} which can be changed at runtime. This is a static attribute + * from the style of activity. Because we don't want {@link WindowContainer#getOrientation()} + * to be affected by the temporal state of {@link ActivityClientController#convertToTranslucent} + * when running ANIM_SCENE_TRANSITION. + * @see WindowContainer#fillsParent() + */ + private final boolean mFillsParent; + // The input dispatching timeout for this application token in milliseconds. long mInputDispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; @@ -1956,8 +1965,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // This style is propagated to the main window attributes with // FLAG_SHOW_WALLPAPER from PhoneWindow#generateLayout. || ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false); + mFillsParent = mOccludesParent; noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false); } else { + mFillsParent = mOccludesParent = true; noDisplay = false; } @@ -2852,7 +2863,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override boolean fillsParent() { - return occludesParent(true /* includingFinishing */); + return mFillsParent; } /** Returns true if this activity is not finishing, is opaque and fills the entire space of diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 68a09a6d4b9b..4f015d8d77a0 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -683,6 +683,9 @@ public class AppTransition implements Dump { } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) { a = mTransitionAnimation.loadAppTransitionAnimation(mNextAppTransitionPackage, enter ? mNextAppTransitionEnter : mNextAppTransitionExit); + if (mNextAppTransitionBackgroundColor != 0) { + a.setBackdropColor(mNextAppTransitionBackgroundColor); + } ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s " + "isEntrance=%b Callers=%s", @@ -842,10 +845,6 @@ public class AppTransition implements Dump { } setAppTransitionFinishedCallbackIfNeeded(a); - if (mNextAppTransitionBackgroundColor != 0) { - a.setBackdropColor(mNextAppTransitionBackgroundColor); - } - return a; } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index ef89eeb7e43d..62998cb6bc40 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -138,8 +138,8 @@ import android.window.ClientWindowFrames; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.policy.ForceShowNavBarSettingsObserver; import com.android.internal.policy.GestureNavigationSettingsObserver; -import com.android.internal.policy.KidsModeSettingsObserver; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.policy.SystemBarUtils; import com.android.internal.protolog.common.ProtoLog; @@ -374,7 +374,7 @@ public class DisplayPolicy { private final WindowManagerInternal.AppTransitionListener mAppTransitionListener; - private final KidsModeSettingsObserver mKidsModeSettingsObserver; + private final ForceShowNavBarSettingsObserver mForceShowNavBarSettingsObserver; private boolean mForceShowNavigationBarEnabled; private class PolicyHandler extends Handler { @@ -649,17 +649,17 @@ public class DisplayPolicy { }); mHandler.post(mGestureNavigationSettingsObserver::register); - mKidsModeSettingsObserver = new KidsModeSettingsObserver( + mForceShowNavBarSettingsObserver = new ForceShowNavBarSettingsObserver( mHandler, mContext); - mKidsModeSettingsObserver.setOnChangeRunnable(() -> { + mForceShowNavBarSettingsObserver.setOnChangeRunnable(() -> { synchronized (mLock) { mForceShowNavigationBarEnabled = - mKidsModeSettingsObserver.isEnabled(); + mForceShowNavBarSettingsObserver.isEnabled(); updateSystemBarAttributes(); } }); - mForceShowNavigationBarEnabled = mKidsModeSettingsObserver.isEnabled(); - mHandler.post(mKidsModeSettingsObserver::register); + mForceShowNavigationBarEnabled = mForceShowNavBarSettingsObserver.isEnabled(); + mHandler.post(mForceShowNavBarSettingsObserver::register); } /** @@ -2844,7 +2844,7 @@ public class DisplayPolicy { void release() { mDisplayContent.mTransitionController.unregisterLegacyListener(mAppTransitionListener); mHandler.post(mGestureNavigationSettingsObserver::unregister); - mHandler.post(mKidsModeSettingsObserver::unregister); + mHandler.post(mForceShowNavBarSettingsObserver::unregister); mImmersiveModeConfirmation.release(); } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 5aacb094207e..f833773cd5c6 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -702,17 +702,17 @@ public class DisplayRotation { } boolean canRotateSeamlessly(int oldRotation, int newRotation) { + // If the navigation bar can't change sides, then it will jump when we change orientations + // and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation + // where the navbar is low-profile enough that this isn't very noticeable. + if (mAllowSeamlessRotationDespiteNavBarMoving || mDisplayPolicy.navigationBarCanMove()) { + return true; + } // For the upside down rotation we don't rotate seamlessly as the navigation bar moves // position. Note most apps (using orientation:sensor or user as opposed to fullSensor) // will not enter the reverse portrait orientation, so actually the orientation won't change // at all. - if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) { - return false; - } - // If the navigation bar can't change sides, then it will jump when we change orientations - // and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation - // where the navbar is low-profile enough that this isn't very noticeable. - return mAllowSeamlessRotationDespiteNavBarMoving || mDisplayPolicy.navigationBarCanMove(); + return oldRotation != Surface.ROTATION_180 && newRotation != Surface.ROTATION_180; } void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) { @@ -1224,16 +1224,8 @@ public class DisplayRotation { || orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) { // Otherwise, use sensor only if requested by the application or enabled // by default for USER or UNSPECIFIED modes. Does not apply to NOSENSOR. - if (mAllowAllRotations == ALLOW_ALL_ROTATIONS_UNDEFINED) { - // Can't read this during init() because the context doesn't have display metrics at - // that time so we cannot determine tablet vs. phone then. - mAllowAllRotations = mContext.getResources().getBoolean( - R.bool.config_allowAllRotations) - ? ALLOW_ALL_ROTATIONS_ENABLED - : ALLOW_ALL_ROTATIONS_DISABLED; - } if (sensorRotation != Surface.ROTATION_180 - || mAllowAllRotations == ALLOW_ALL_ROTATIONS_ENABLED + || getAllowAllRotations() == ALLOW_ALL_ROTATIONS_ENABLED || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) { preferredRotation = sensorRotation; @@ -1322,6 +1314,19 @@ public class DisplayRotation { } } + private int getAllowAllRotations() { + if (mAllowAllRotations == ALLOW_ALL_ROTATIONS_UNDEFINED) { + // Can't read this during init() because the context doesn't have display metrics at + // that time so we cannot determine tablet vs. phone then. + mAllowAllRotations = mContext.getResources().getBoolean( + R.bool.config_allowAllRotations) + ? ALLOW_ALL_ROTATIONS_ENABLED + : ALLOW_ALL_ROTATIONS_DISABLED; + } + + return mAllowAllRotations; + } + private boolean isLandscapeOrSeascape(int rotation) { return rotation == mLandscapeRotation || rotation == mSeascapeRotation; } @@ -1349,6 +1354,11 @@ public class DisplayRotation { case ActivityInfo.SCREEN_ORIENTATION_USER: case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED: + // When all rotations enabled it works with any of the 4 rotations + if (getAllowAllRotations() == ALLOW_ALL_ROTATIONS_ENABLED) { + return preferredRotation >= 0; + } + // Works with any rotation except upside down. return (preferredRotation >= 0) && (preferredRotation != Surface.ROTATION_180); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f97f768872fd..17e1dd26a602 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1724,8 +1724,8 @@ class Task extends TaskFragment { /** Returns {@code true} if this task is currently in split-screen. */ boolean inSplitScreen() { return getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW - && getRootTask() != null - && getRootTask().getAdjacentTaskFragment() != null; + && getCreatedByOrganizerTask() != null + && getCreatedByOrganizerTask().getAdjacentTaskFragment() != null; } private boolean supportsSplitScreenWindowingModeInner(@Nullable TaskDisplayArea tda) { diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 73a755dd3123..1176182ede50 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -963,7 +963,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } else if (candidateTask != null) { final int position = onTop ? POSITION_TOP : POSITION_BOTTOM; final Task launchRootTask = getLaunchRootTask(resolvedWindowingMode, activityType, - options, sourceTask, launchFlags); + options, sourceTask, launchFlags, candidateTask); if (launchRootTask != null) { if (candidateTask.getParent() == null) { launchRootTask.addChild(candidateTask, position); @@ -1117,6 +1117,13 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { @Nullable Task getLaunchRootTask(int windowingMode, int activityType, @Nullable ActivityOptions options, @Nullable Task sourceTask, int launchFlags) { + return getLaunchRootTask(windowingMode, activityType, options, sourceTask, launchFlags, + null /* candidateTask */); + } + + @Nullable + Task getLaunchRootTask(int windowingMode, int activityType, @Nullable ActivityOptions options, + @Nullable Task sourceTask, int launchFlags, @Nullable Task candidateTask) { // Try to use the launch root task in options if available. if (options != null) { final Task launchRootTask = Task.fromWindowContainerToken(options.getLaunchRootTask()); @@ -1157,9 +1164,19 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { } // For a better split UX, If a task is launching from a created-by-organizer task, it should - // be launched into the same created-by-organizer task as well. - if (sourceTask != null) { - return sourceTask.getCreatedByOrganizerTask(); + // be launched into the same created-by-organizer task as well. Unless, the candidate task + // is already positioned in the split. + Task preferredRootInSplit = sourceTask != null && sourceTask.inSplitScreen() + ? sourceTask.getCreatedByOrganizerTask() : null; + if (preferredRootInSplit != null) { + if (candidateTask != null) { + final Task candidateRoot = candidateTask.getCreatedByOrganizerTask(); + if (candidateRoot != null && candidateRoot != preferredRootInSplit + && preferredRootInSplit == candidateRoot.getAdjacentTaskFragment()) { + preferredRootInSplit = candidateRoot; + } + } + return preferredRootInSplit; } return null; diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 1beb32cfdd52..56e96fa1fe58 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -591,8 +591,9 @@ class TaskFragment extends WindowContainer<WindowContainer> { * @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord) */ boolean isAllowedToBeEmbeddedInTrustedMode() { - final Predicate<ActivityRecord> callback = this::isAllowedToEmbedActivityInTrustedMode; - return forAllActivities(callback); + // Traverse all activities to see if any of them are not in the trusted mode. + final Predicate<ActivityRecord> callback = r -> !isAllowedToEmbedActivityInTrustedMode(r); + return !forAllActivities(callback); } /** @@ -2155,7 +2156,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) { return applicationType; } - return getTopChild().getActivityType(); + final ActivityRecord activity = getTopNonFinishingActivity(); + return activity != null ? activity.getActivityType() : getTopChild().getActivityType(); } @Override diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index eff75bc0622b..a9b0f0dc28ad 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -423,7 +423,7 @@ public class WindowManagerService extends IWindowManager.Stub "persist.wm.enable_remote_keyguard_animation"; private static final int sEnableRemoteKeyguardAnimation = - SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 1); + SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 2); /** * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index efed92ded095..137a3ac81b9b 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -30,6 +30,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; @@ -775,6 +776,29 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } break; } + case HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT: { + final TaskFragment tf = mLaunchTaskFragments.get(hop.getContainer()); + if (tf == null || !tf.isAttached()) { + Slog.e(TAG, "Attempt to operate on detached container: " + tf); + break; + } + final ActivityRecord curFocus = tf.getDisplayContent().mFocusedApp; + if (curFocus != null && curFocus.getTaskFragment() == tf) { + Slog.d(TAG, "The requested TaskFragment already has the focus."); + break; + } + if (curFocus != null && curFocus.getTask() != tf.getTask()) { + Slog.d(TAG, "The Task of the requested TaskFragment doesn't have focus."); + break; + } + final ActivityRecord targetFocus = tf.getTopResumedActivity(); + if (targetFocus == null) { + Slog.d(TAG, "There is no resumed activity in the requested TaskFragment."); + break; + } + tf.getDisplayContent().setFocusedApp(targetFocus); + break; + } default: { // The other operations may change task order so they are skipped while in lock // task mode. The above operations are still allowed because they don't move @@ -1318,6 +1342,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS: + case HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT: // We are allowing organizer to start/reparent activity to a TaskFragment it // created, or set two TaskFragments adjacent to each other. Nothing to check // here because the TaskFragment may not be created yet, but will be created in @@ -1413,7 +1438,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } final WindowConfiguration requestedWindowConfig = requestedConfig.windowConfiguration; final WindowConfiguration parentWindowConfig = parentConfig.windowConfiguration; - if (!parentWindowConfig.getBounds().contains(requestedWindowConfig.getBounds())) { + if (!requestedWindowConfig.getBounds().isEmpty() + && !parentWindowConfig.getBounds().contains(requestedWindowConfig.getBounds())) { String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " trying to apply bounds outside of parent for non-trusted host," @@ -1422,6 +1448,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub throw new SecurityException(msg); } if (requestedWindowConfig.getAppBounds() != null + && !requestedWindowConfig.getAppBounds().isEmpty() && parentWindowConfig.getAppBounds() != null && !parentWindowConfig.getAppBounds().contains( requestedWindowConfig.getAppBounds())) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java index 9a0b5c7ef5ae..48a436f15803 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java @@ -69,6 +69,7 @@ class DevicePolicyData { private static final String TAG_PASSWORD_VALIDITY = "password-validity"; private static final String TAG_PASSWORD_TOKEN_HANDLE = "password-token"; private static final String TAG_PROTECTED_PACKAGES = "protected-packages"; + private static final String TAG_BYPASS_ROLE_QUALIFICATIONS = "bypass-role-qualifications"; private static final String ATTR_VALUE = "value"; private static final String ATTR_ALIAS = "alias"; private static final String ATTR_ID = "id"; @@ -107,6 +108,8 @@ class DevicePolicyData { int mPasswordOwner = -1; long mLastMaximumTimeToLock = -1; boolean mUserSetupComplete = false; + boolean mBypassDevicePolicyManagementRoleQualifications = false; + String mCurrentRoleHolder; boolean mPaired = false; int mUserProvisioningState; int mPermissionPolicy; @@ -374,6 +377,12 @@ class DevicePolicyData { out.endTag(null, TAG_APPS_SUSPENDED); } + if (policyData.mBypassDevicePolicyManagementRoleQualifications) { + out.startTag(null, TAG_BYPASS_ROLE_QUALIFICATIONS); + out.attribute(null, ATTR_VALUE, policyData.mCurrentRoleHolder); + out.endTag(null, TAG_BYPASS_ROLE_QUALIFICATIONS); + } + out.endTag(null, "policies"); out.endDocument(); @@ -558,6 +567,9 @@ class DevicePolicyData { } else if (TAG_APPS_SUSPENDED.equals(tag)) { policy.mAppsSuspended = parser.getAttributeBoolean(null, ATTR_VALUE, false); + } else if (TAG_BYPASS_ROLE_QUALIFICATIONS.equals(tag)) { + policy.mBypassDevicePolicyManagementRoleQualifications = true; + policy.mCurrentRoleHolder = parser.getAttributeValue(null, ATTR_VALUE); } else { Slogf.w(TAG, "Unknown tag: %s", tag); XmlUtils.skipCurrentTag(parser); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e2d8a63fbfda..d1fac871fa2e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -138,7 +138,6 @@ import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE; import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK; import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; -import static android.provider.Settings.Global.BYPASS_DEVICE_POLICY_MANAGEMENT_ROLE_QUALIFICATIONS; import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER; import static android.provider.Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED; import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; @@ -222,6 +221,7 @@ import android.app.admin.UnsafeStateException; import android.app.admin.WifiSsidPolicy; import android.app.backup.IBackupManager; import android.app.compat.CompatChanges; +import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; @@ -255,6 +255,7 @@ import android.content.pm.ParceledListSlice; import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.pm.Signature; import android.content.pm.StringParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Resources; @@ -409,6 +410,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.Predicate; @@ -756,6 +758,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private @UserIdInt int mNetworkLoggingNotificationUserId = UserHandle.USER_NULL; private final DeviceManagementResourcesProvider mDeviceManagementResourcesProvider; + private final DevicePolicyManagementRoleObserver mDevicePolicyManagementRoleObserver; private static final boolean ENABLE_LOCK_GUARD = true; @@ -1823,6 +1826,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mBugreportCollectionManager = new RemoteBugreportManager(this, mInjector); mDeviceManagementResourcesProvider = mInjector.getDeviceManagementResourcesProvider(); + mDevicePolicyManagementRoleObserver = new DevicePolicyManagementRoleObserver(mContext); + mDevicePolicyManagementRoleObserver.register(); // "Lite" interface is available even when the device doesn't have the feature LocalServices.addService(DevicePolicyManagerLiteInternal.class, mLocalService); @@ -18663,16 +18668,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( android.Manifest.permission.MANAGE_ROLE_HOLDERS)); return mInjector.binderWithCleanCallingIdentity(() -> { - if (mInjector.settingsGlobalGetInt( - BYPASS_DEVICE_POLICY_MANAGEMENT_ROLE_QUALIFICATIONS, /* def= */ 0) == 1) { + if (getUserData( + UserHandle.USER_SYSTEM).mBypassDevicePolicyManagementRoleQualifications) { return true; } - if (shouldAllowBypassingDevicePolicyManagementRoleQualificationInternal()) { - mInjector.settingsGlobalPutInt( - BYPASS_DEVICE_POLICY_MANAGEMENT_ROLE_QUALIFICATIONS, /* value= */ 1); - return true; - } - return false; + return shouldAllowBypassingDevicePolicyManagementRoleQualificationInternal(); }); } @@ -18685,6 +18685,142 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return accounts.length == 0; } + private void setBypassDevicePolicyManagementRoleQualificationStateInternal( + String currentRoleHolder, boolean allowBypass) { + boolean stateChanged = false; + DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); + if (policy.mBypassDevicePolicyManagementRoleQualifications != allowBypass) { + policy.mBypassDevicePolicyManagementRoleQualifications = allowBypass; + stateChanged = true; + } + if (!Objects.equals(currentRoleHolder, policy.mCurrentRoleHolder)) { + policy.mCurrentRoleHolder = currentRoleHolder; + stateChanged = true; + } + if (stateChanged) { + synchronized (getLockObject()) { + saveSettingsLocked(UserHandle.USER_SYSTEM); + } + } + } + + private final class DevicePolicyManagementRoleObserver implements OnRoleHoldersChangedListener { + private RoleManager mRm; + private final Executor mExecutor; + private final Context mContext; + + DevicePolicyManagementRoleObserver(@NonNull Context context) { + mContext = context; + mExecutor = mContext.getMainExecutor(); + mRm = mContext.getSystemService(RoleManager.class); + } + + public void register() { + mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.SYSTEM); + } + + @Override + public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { + if (!RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT.equals(roleName)) { + return; + } + String newRoleHolder = getRoleHolder(); + if (isDefaultRoleHolder(newRoleHolder)) { + Slogf.i(LOG_TAG, + "onRoleHoldersChanged: Default role holder is set, returning early"); + return; + } + if (newRoleHolder == null) { + Slogf.i(LOG_TAG, + "onRoleHoldersChanged: New role holder is null, returning early"); + return; + } + if (shouldAllowBypassingDevicePolicyManagementRoleQualificationInternal()) { + Slogf.w(LOG_TAG, + "onRoleHoldersChanged: Updating current role holder to " + newRoleHolder); + setBypassDevicePolicyManagementRoleQualificationStateInternal( + newRoleHolder, /* allowBypass= */ true); + return; + } + DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); + if (!newRoleHolder.equals(policy.mCurrentRoleHolder)) { + Slogf.w(LOG_TAG, + "onRoleHoldersChanged: You can't set a different role holder, role " + + "is getting revoked from " + newRoleHolder); + setBypassDevicePolicyManagementRoleQualificationStateInternal( + /* currentRoleHolder= */ null, /* allowBypass= */ false); + mRm.removeRoleHolderAsUser( + RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, + newRoleHolder, + /* flags= */ 0, + user, + mExecutor, + successful -> {}); + } + } + + private String getRoleHolder() { + return DevicePolicyManagerService.this.getDevicePolicyManagementRoleHolderPackageName( + mContext); + } + + private boolean isDefaultRoleHolder(String packageName) { + String defaultRoleHolder = getDefaultRoleHolderPackageName(); + if (packageName == null || defaultRoleHolder == null) { + return false; + } + if (!defaultRoleHolder.equals(packageName)) { + return false; + } + return hasSigningCertificate( + packageName, getDefaultRoleHolderPackageSignature()); + } + + private boolean hasSigningCertificate(String packageName, String certificateString) { + if (packageName == null || certificateString == null) { + return false; + } + byte[] certificate; + try { + certificate = new Signature(certificateString).toByteArray(); + } catch (IllegalArgumentException e) { + Slogf.w(LOG_TAG, "Cannot parse signing certificate: " + certificateString, e); + return false; + } + PackageManager pm = mInjector.getPackageManager(); + return pm.hasSigningCertificate( + packageName, certificate, PackageManager.CERT_INPUT_SHA256); + } + + private String getDefaultRoleHolderPackageName() { + String[] info = getDefaultRoleHolderPackageNameAndSignature(); + if (info == null) { + return null; + } + return info[0]; + } + + private String getDefaultRoleHolderPackageSignature() { + String[] info = getDefaultRoleHolderPackageNameAndSignature(); + if (info == null || info.length < 2) { + return null; + } + return info[1]; + } + + private String[] getDefaultRoleHolderPackageNameAndSignature() { + String packageNameAndSignature = mContext.getString( + com.android.internal.R.string.config_devicePolicyManagement); + if (TextUtils.isEmpty(packageNameAndSignature)) { + return null; + } + if (packageNameAndSignature.contains(":")) { + return packageNameAndSignature.split(":"); + } + return new String[]{packageNameAndSignature}; + } + } + @Override public List<UserHandle> getPolicyManagedProfiles(@NonNull UserHandle user) { Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 670c1596e15e..08c68b9a469e 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -42,6 +42,8 @@ android_test { static_libs: [ "androidx.test.core", "androidx.test.runner", + "androidx.test.espresso.core", + "androidx.test.espresso.contrib", "androidx.test.ext.truth", "frameworks-base-testutils", "hamcrest-library", diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index 7714cf0ca094..07b763dcd85b 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -32,6 +32,8 @@ <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/> <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> + <uses-permission android:name="android.permission.MANAGE_GAME_ACTIVITY" /> + <uses-permission android:name="android.permission.SET_ALWAYS_FINISH" /> <!-- needed by MasterClearReceiverTest to display a system dialog --> <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/> @@ -39,6 +41,8 @@ <application android:testOnly="true" android:debuggable="true"> <uses-library android:name="android.test.runner" /> + <activity + android:name="android.service.games.GameSessionTrampolineActivityTest$TestActivity" /> </application> <instrumentation diff --git a/services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java b/services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java new file mode 100644 index 000000000000..d68b517ca8cd --- /dev/null +++ b/services/tests/mockingservicestests/src/android/service/games/GameSessionTrampolineActivityTest.java @@ -0,0 +1,212 @@ +/* + * 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.service.games; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.action.ViewActions.click; +import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.isClickable; +import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static androidx.test.ext.truth.content.IntentSubject.assertThat; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.google.common.truth.Truth.assertThat; + +import static org.hamcrest.Matchers.allOf; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.Intent; +import android.os.Bundle; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.testing.AndroidTestingRunner; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.test.espresso.NoActivityResumedException; +import androidx.test.filters.SmallTest; + +import com.android.internal.infra.AndroidFuture; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.TimeUnit; + +/** + * Unit tests for the {@link GameSessionTrampolineActivity}. + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +@Presubmit +public class GameSessionTrampolineActivityTest { + + @Before + public void setUp() { + setAlwaysFinishActivities(false); + } + + @After + public void tearDown() { + setAlwaysFinishActivities(false); + } + + @Test + public void launch_launchesTargetActivity() { + AndroidFuture<GameSessionActivityResult> unusedResultFuture = + startTestActivityViaGameSessionTrampolineActivity(); + + TestActivityPage.assertPageIsLaunched(); + } + + @Test + public void launch_targetActivityFinishesSuccessfully_futureCompletedWithSameResults() { + AndroidFuture<GameSessionActivityResult> resultFuture = + startTestActivityViaGameSessionTrampolineActivity(); + + TestActivityPage.assertPageIsLaunched(); + TestActivityPage.clickFinish(); + + GameSessionActivityResult expectedResult = + new GameSessionActivityResult(Activity.RESULT_OK, TestActivity.RESULT_INTENT); + + assertEquals(resultFuture, expectedResult); + + TestActivityPage.assertPageIsNotLaunched(); + } + + @Test + public void launch_trampolineActivityProcessDeath_futureCompletedWithSameResults() { + setAlwaysFinishActivities(true); + + AndroidFuture<GameSessionActivityResult> resultFuture = + startTestActivityViaGameSessionTrampolineActivity(); + + TestActivityPage.assertPageIsLaunched(); + TestActivityPage.clickFinish(); + + GameSessionActivityResult expectedResult = + new GameSessionActivityResult(Activity.RESULT_OK, TestActivity.RESULT_INTENT); + + assertEquals(resultFuture, expectedResult); + + TestActivityPage.assertPageIsNotLaunched(); + } + + private static void assertEquals( + AndroidFuture<GameSessionActivityResult> actualFuture, + GameSessionActivityResult expected) { + try { + assertEquals(actualFuture.get(20, TimeUnit.SECONDS), expected); + } catch (Exception ex) { + throw new IllegalStateException(ex); + } + } + + private static void assertEquals( + GameSessionActivityResult actual, + GameSessionActivityResult expected) { + assertThat(actual.getResultCode()).isEqualTo(expected.getResultCode()); + assertThat(actual.getData()).filtersEquallyTo(actual.getData()); + } + + private static void setAlwaysFinishActivities(boolean isEnabled) { + try { + ActivityManager.getService().setAlwaysFinish(isEnabled); + } catch (RemoteException ex) { + throw new IllegalStateException(ex); + } + } + + private static AndroidFuture<GameSessionActivityResult> + startTestActivityViaGameSessionTrampolineActivity() { + Intent testActivityIntent = new Intent(); + testActivityIntent.setClass(getInstrumentation().getTargetContext(), TestActivity.class); + + return startGameSessionTrampolineActivity(testActivityIntent); + } + + private static AndroidFuture<GameSessionActivityResult> startGameSessionTrampolineActivity( + Intent targetIntent) { + AndroidFuture<GameSessionActivityResult> resultFuture = new AndroidFuture<>(); + Intent trampolineActivityIntent = GameSessionTrampolineActivity.createIntent(targetIntent, + null, resultFuture); + trampolineActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getInstrumentation().getTargetContext().startActivity(trampolineActivityIntent); + getInstrumentation().waitForIdleSync(); + + return resultFuture; + } + + + private static class TestActivityPage { + private TestActivityPage() {} + + public static void assertPageIsLaunched() { + onView(withText(TestActivity.PAGE_TITLE_TEXT)).check(matches(isDisplayed())); + } + + public static void assertPageIsNotLaunched() { + try { + onView(withText(TestActivity.PAGE_TITLE_TEXT)).check(doesNotExist()); + } catch (NoActivityResumedException ex) { + // Do nothing + } + } + + public static void clickFinish() { + onView(allOf(withText(TestActivity.FINISH_BUTTON_TEXT), isClickable())).perform( + click()); + getInstrumentation().waitForIdleSync(); + } + } + + public static class TestActivity extends Activity { + private static final String PAGE_TITLE_TEXT = "GameSessionTestActivity"; + private static final String FINISH_BUTTON_TEXT = "Finish Test Activity"; + private static final Intent RESULT_INTENT = new Intent("com.test.action.VIEW"); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout contentLayout = new LinearLayout(this); + contentLayout.setOrientation(LinearLayout.VERTICAL); + + TextView titleTextView = new TextView(this); + titleTextView.setText(PAGE_TITLE_TEXT); + contentLayout.addView(titleTextView); + + Button finishActivityButton = new Button(this); + finishActivityButton.setText(FINISH_BUTTON_TEXT); + finishActivityButton.setOnClickListener((unused) -> { + setResult(Activity.RESULT_OK, RESULT_INTENT); + finish(); + }); + + + contentLayout.addView(finishActivityButton); + setContentView(contentLayout); + } + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java index 319a769bb1de..5b551b1c183c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java @@ -216,11 +216,12 @@ public final class GameServiceProviderInstanceImplTest { mRunningTaskInfos); + final UserHandle userHandle = new UserHandle(USER_ID); mGameServiceProviderInstance = new GameServiceProviderInstanceImpl( - new UserHandle(USER_ID), + userHandle, ConcurrentUtils.DIRECT_EXECUTOR, mMockContext, - mFakeGameClassifier, + new GameTaskInfoProvider(userHandle, mMockActivityTaskManager, mFakeGameClassifier), mMockActivityManager, mMockActivityManagerInternal, mMockActivityTaskManager, @@ -788,6 +789,36 @@ public final class GameServiceProviderInstanceImplTest { } @Test + public void gameTaskFocusedWithCreateAfterRemoved_gameSessionRecreated() throws Exception { + mGameServiceProviderInstance.start(); + + startTask(10, GAME_A_MAIN_ACTIVITY); + mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY); + mFakeGameService.requestCreateGameSession(10); + + FakeGameSession gameSession10 = new FakeGameSession(); + SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class); + mFakeGameSessionService.removePendingFutureForTaskId(10) + .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10)); + + stopTask(10); + + assertThat(gameSession10.mIsDestroyed).isTrue(); + + // If the game task is restored via the Recents UI, the task will be running again but + // we would not expect any call to TaskStackListener#onTaskCreated. + addRunningTaskInfo(10, GAME_A_MAIN_ACTIVITY); + + // We now receive a task focused event for the task. This will occur if the game task is + // restored via the Recents UI. + dispatchTaskFocused(10, /*focused=*/ true); + mFakeGameService.requestCreateGameSession(10); + + // Verify that a new pending game session is created for the game's taskId. + assertNotNull(mFakeGameSessionService.removePendingFutureForTaskId(10)); + } + + @Test public void gameTaskRemoved_removesTaskOverlay() throws Exception { mGameServiceProviderInstance.start(); @@ -1144,13 +1175,18 @@ public final class GameServiceProviderInstanceImplTest { } private void startTask(int taskId, ComponentName componentName) { + addRunningTaskInfo(taskId, componentName); + + dispatchTaskCreated(taskId, componentName); + } + + private void addRunningTaskInfo(int taskId, ComponentName componentName) { RunningTaskInfo runningTaskInfo = new RunningTaskInfo(); runningTaskInfo.taskId = taskId; + runningTaskInfo.baseActivity = componentName; runningTaskInfo.displayId = 1; runningTaskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 500, 800)); mRunningTaskInfos.add(runningTaskInfo); - - dispatchTaskCreated(taskId, componentName); } private void stopTask(int taskId) { diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 449177ef9b7d..0afb1829f9d4 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -102,6 +102,7 @@ <uses-permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /> <uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" /> + <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" /> <queries> <package android:name="com.android.servicestests.apps.suspendtestapp" /> diff --git a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java index 0e84e044e44b..42c4129513d0 100644 --- a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java @@ -16,6 +16,10 @@ package com.android.server; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.app.job.JobScheduler; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -43,13 +47,15 @@ public class BinaryTransparencyServiceTest { @Before public void setUp() { - mContext = ApplicationProvider.getApplicationContext(); + mContext = spy(ApplicationProvider.getApplicationContext()); mBinaryTransparencyService = new BinaryTransparencyService(mContext); mTestInterface = mBinaryTransparencyService.new BinaryTransparencyServiceImpl(); } private void prepSignedInfo() { // simulate what happens on boot completed phase + // but we avoid calling JobScheduler.schedule by returning a null. + doReturn(null).when(mContext).getSystemService(JobScheduler.class); mBinaryTransparencyService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index 2cf67f83578b..e991ec6879ae 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -237,6 +237,8 @@ public class DpmMockContext extends MockContext { return mMockSystemServices.devicePolicyManager; case Context.LOCATION_SERVICE: return mMockSystemServices.locationManager; + case Context.ROLE_SERVICE: + return mMockSystemServices.roleManager; } throw new UnsupportedOperationException(); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 34c9f7c2ef87..884ffce155d7 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -33,6 +33,7 @@ import android.app.IActivityTaskManager; import android.app.NotificationManager; import android.app.admin.DevicePolicyManager; import android.app.backup.IBackupManager; +import android.app.role.RoleManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -131,6 +132,7 @@ public class MockSystemServices { public final VpnManager vpnManager; public final DevicePolicyManager devicePolicyManager; public final LocationManager locationManager; + public final RoleManager roleManager; /** Note this is a partial mock, not a real mock. */ public final PackageManager packageManager; public final BuildMock buildMock = new BuildMock(); @@ -181,6 +183,7 @@ public class MockSystemServices { vpnManager = mock(VpnManager.class); devicePolicyManager = mock(DevicePolicyManager.class); locationManager = mock(LocationManager.class); + roleManager = realContext.getSystemService(RoleManager.class); // Package manager is huge, so we use a partial mock instead. packageManager = spy(realContext.getPackageManager()); diff --git a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING new file mode 100644 index 000000000000..9f1a209d2ee1 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING @@ -0,0 +1,21 @@ +{ + "presubmit": [ + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.display." + }, + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +} diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index 767857bf2de8..e8e3a8f84f21 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -33,6 +33,7 @@ <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" /> <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/> <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> + <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" /> <application android:debuggable="true"> <uses-library android:name="android.test.runner" /> diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index f4b9e258f7e0..76d4059eb436 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -30,8 +30,11 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -46,6 +49,8 @@ import android.os.Bundle; import android.os.UserHandle; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerService; +import android.service.notification.NotificationStats; +import android.service.notification.StatusBarNotification; import android.testing.TestableContext; import android.util.ArraySet; import android.util.Pair; @@ -59,11 +64,13 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.reflection.FieldSetter; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.util.List; public class NotificationListenersTest extends UiServiceTestCase { @@ -388,4 +395,66 @@ public class NotificationListenersTest extends UiServiceTestCase { verify(mContext).sendBroadcastAsUser( any(), eq(UserHandle.of(userId)), nullable(String.class)); } + + @Test + public void testNotifyPostedLockedInLockdownMode() { + NotificationRecord r = mock(NotificationRecord.class); + NotificationRecord old = mock(NotificationRecord.class); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + mListeners.notifyPostedLocked(r, old, true); + mListeners.notifyPostedLocked(r, old, false); + verify(r, atLeast(2)).getSbn(); + + // in the lockdown mode + reset(r); + reset(old); + when(mNm.isInLockDownMode()).thenReturn(true); + mListeners.notifyPostedLocked(r, old, true); + mListeners.notifyPostedLocked(r, old, false); + verify(r, never()).getSbn(); + } + + @Test + public void testnotifyRankingUpdateLockedInLockdownMode() { + List chn = mock(List.class); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + mListeners.notifyRankingUpdateLocked(chn); + verify(chn, atLeast(1)).size(); + + // in the lockdown mode + reset(chn); + when(mNm.isInLockDownMode()).thenReturn(true); + mListeners.notifyRankingUpdateLocked(chn); + verify(chn, never()).size(); + } + + @Test + public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException { + NotificationRecord r = mock(NotificationRecord.class); + NotificationStats rs = mock(NotificationStats.class); + StatusBarNotification sbn = mock(StatusBarNotification.class); + FieldSetter.setField(mNm, + NotificationManagerService.class.getDeclaredField("mHandler"), + mock(NotificationManagerService.WorkerHandler.class)); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + when(r.getSbn()).thenReturn(sbn); + mListeners.notifyRemovedLocked(r, 0, rs); + mListeners.notifyRemovedLocked(r, 0, rs); + verify(r, atLeast(2)).getSbn(); + + // in the lockdown mode + reset(r); + reset(rs); + when(mNm.isInLockDownMode()).thenReturn(true); + when(r.getSbn()).thenReturn(sbn); + mListeners.notifyRemovedLocked(r, 0, rs); + mListeners.notifyRemovedLocked(r, 0, rs); + verify(r, never()).getSbn(); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index b987c692bddb..348e015500fe 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -63,10 +63,13 @@ import static android.service.notification.Adjustment.KEY_USER_SENTIMENT; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING; +import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; + import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; @@ -352,6 +355,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { MultiRateLimiter mToastRateLimiter; BroadcastReceiver mPackageIntentReceiver; NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake(); + TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker; private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( 1 << 30); @Mock @@ -508,6 +512,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setAudioManager(mAudioManager); + mStrongAuthTracker = mService.new StrongAuthTrackerFake(mContext); + mService.setStrongAuthTracker(mStrongAuthTracker); + mShortcutHelper = mService.getShortcutHelper(); mShortcutHelper.setLauncherApps(mLauncherApps); mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal); @@ -9247,4 +9254,44 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // make sure the summary was removed and not re-posted assertThat(mService.getNotificationRecordCount()).isEqualTo(0); } + + @Test + public void testStrongAuthTracker_isInLockDownMode() { + mStrongAuthTracker.setGetStrongAuthForUserReturnValue( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertTrue(mStrongAuthTracker.isInLockDownMode()); + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertFalse(mStrongAuthTracker.isInLockDownMode()); + } + + @Test + public void testCancelAndPostNotificationsWhenEnterAndExitLockDownMode() { + // post 2 notifications from 2 packages + NotificationRecord pkgA = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgA); + NotificationRecord pkgB = new NotificationRecord(mContext, + generateSbn("b", 1001, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgB); + + // when entering the lockdown mode, cancel the 2 notifications. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertTrue(mStrongAuthTracker.isInLockDownMode()); + + // the notifyRemovedLocked function is called twice due to REASON_CANCEL_ALL. + ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class); + verify(mListeners, times(2)).notifyRemovedLocked(any(), captor.capture(), any()); + assertEquals(REASON_CANCEL_ALL, captor.getValue().intValue()); + + // exit lockdown mode. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + + // the notifyPostedLocked function is called twice. + verify(mListeners, times(2)).notifyPostedLocked(any(), any()); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java index bde048569e53..4ed7d35a097f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java @@ -113,4 +113,20 @@ public class TestableNotificationManagerService extends NotificationManagerServi protected void doChannelWarningToast(int uid, CharSequence toastText) { mChannelToastsSent.add(uid); } + + public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker { + private int mGetStrongAuthForUserReturnValue = 0; + StrongAuthTrackerFake(Context context) { + super(context); + } + + public void setGetStrongAuthForUserReturnValue(int val) { + mGetStrongAuthForUserReturnValue = val; + } + + @Override + public int getStrongAuthForUser(int userId) { + return mGetStrongAuthForUserReturnValue; + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 819087a74fd4..533540e2568d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -757,6 +757,8 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord activity = createActivityWithTask(); ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(activity.getTask()).build(); topActivity.setOccludesParent(false); + // The requested occluding state doesn't affect whether it fills parent. + assertTrue(topActivity.fillsParent()); activity.setState(STOPPED, "Testing"); activity.setVisibility(true); activity.makeActiveIfNeeded(null /* activeActivity */); @@ -1218,7 +1220,7 @@ public class ActivityRecordTests extends WindowTestsBase { task.setPausingActivity(currentTop); currentTop.finishing = true; currentTop.setState(PAUSED, "test"); - currentTop.completeFinishing("completePauseLocked"); + currentTop.completeFinishing(false /* updateVisibility */, "completePause"); // Current top becomes stopping because it is visible and the next is invisible. assertEquals(STOPPING, currentTop.getState()); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 33b70249dabe..67f02c7fab55 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -46,7 +46,6 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -89,14 +88,6 @@ public class AppTransitionControllerTest extends WindowTestsBase { mAppTransitionController = new AppTransitionController(mWm, mDisplayContent); } - @Override - ActivityRecord createActivityRecord(DisplayContent dc, int windowingMode, int activityType) { - final ActivityRecord r = super.createActivityRecord(dc, windowingMode, activityType); - // Ensure that ActivityRecord#setOccludesParent takes effect. - doCallRealMethod().when(r).fillsParent(); - return r; - } - @Test public void testSkipOccludedActivityCloseTransition() { final ActivityRecord behind = createActivityRecord(mDisplayContent, @@ -135,7 +126,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); final ActivityRecord translucentOpening = createActivityRecord(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - translucentOpening.setOccludesParent(false); + doReturn(false).when(translucentOpening).fillsParent(); translucentOpening.setVisible(false); mDisplayContent.prepareAppTransition(TRANSIT_OPEN); mDisplayContent.mOpeningApps.add(behind); @@ -153,7 +144,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); final ActivityRecord translucentClosing = createActivityRecord(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - translucentClosing.setOccludesParent(false); + doReturn(false).when(translucentClosing).fillsParent(); mDisplayContent.prepareAppTransition(TRANSIT_CLOSE); mDisplayContent.mClosingApps.add(translucentClosing); assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index 25cff61c3b78..e4eb98e79576 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -40,7 +40,9 @@ 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.assertTrue; +import static org.mockito.Mockito.clearInvocations; +import android.app.WindowConfiguration; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -515,6 +517,69 @@ public class DisplayRotationTests { } @Test + public void testAllowAllRotations_allowsUpsideDownSuggestion() + throws Exception { + mBuilder.build(); + mTarget.updateOrientation(SCREEN_ORIENTATION_UNSPECIFIED, true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations)) + .thenReturn(true); + freezeRotation(Surface.ROTATION_0); + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180)); + assertTrue(waitForUiHandler()); + + verify(mMockStatusBarManagerInternal) + .onProposedRotationChanged(Surface.ROTATION_180, true); + } + + @Test + public void testDoNotAllowAllRotations_doesNotAllowUpsideDownSuggestion() + throws Exception { + mBuilder.build(); + mTarget.updateOrientation(SCREEN_ORIENTATION_UNSPECIFIED, true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations)) + .thenReturn(false); + freezeRotation(Surface.ROTATION_0); + enableOrientationSensor(); + + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180)); + assertTrue(waitForUiHandler()); + + verify(mMockStatusBarManagerInternal) + .onProposedRotationChanged(Surface.ROTATION_180, false); + } + + @Test + public void testAllowAllRotations_allowAllRotationsBecomesDisabled_forbidsUpsideDownSuggestion() + throws Exception { + mBuilder.build(); + mTarget.updateOrientation(SCREEN_ORIENTATION_UNSPECIFIED, true); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations)) + .thenReturn(true); + freezeRotation(Surface.ROTATION_0); + enableOrientationSensor(); + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0)); + assertTrue(waitForUiHandler()); + + // Change resource to disallow all rotations. + // Reset "allowAllRotations". + mTarget.applyCurrentRotation(Surface.ROTATION_0); + clearInvocations(mMockStatusBarManagerInternal); + when(mMockRes.getBoolean(com.android.internal.R.bool.config_allowAllRotations)) + .thenReturn(false); + mTarget.resetAllowAllRotations(); + mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180)); + assertTrue(waitForUiHandler()); + + verify(mMockStatusBarManagerInternal) + .onProposedRotationChanged(Surface.ROTATION_180, false); + } + + @Test public void testReturnsCompatibleRotation_SensorEnabled_RotationThawed() throws Exception { mBuilder.build(); configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); @@ -721,14 +786,20 @@ public class DisplayRotationTests { doReturn(true).when(mMockDisplayPolicy).navigationBarCanMove(); doReturn(win).when(mMockDisplayPolicy).getTopFullscreenOpaqueWindow(); mMockDisplayContent.mCurrentFocus = win; - mTarget.mUpsideDownRotation = Surface.ROTATION_180; + // This should not affect the condition of shouldRotateSeamlessly. + mTarget.mUpsideDownRotation = Surface.ROTATION_90; doReturn(true).when(win.mActivityRecord).matchParentBounds(); // The focused fullscreen opaque window without override bounds should be able to be // rotated seamlessly. assertTrue(mTarget.shouldRotateSeamlessly( Surface.ROTATION_0, Surface.ROTATION_90, false /* forceUpdate */)); + // Reject any 180 degree because non-movable navbar will be placed in a different position. + doReturn(false).when(mMockDisplayPolicy).navigationBarCanMove(); + assertFalse(mTarget.shouldRotateSeamlessly( + Surface.ROTATION_90, Surface.ROTATION_180, false /* forceUpdate */)); + doReturn(true).when(mMockDisplayPolicy).navigationBarCanMove(); doReturn(false).when(win.mActivityRecord).matchParentBounds(); // No seamless rotation if the window may be positioned with offset after rotation. assertFalse(mTarget.shouldRotateSeamlessly( @@ -935,6 +1006,8 @@ public class DisplayRotationTests { .thenReturn(WmDisplayCutout.NO_CUTOUT); when(mMockDisplayContent.getDefaultTaskDisplayArea()) .thenReturn(mock(TaskDisplayArea.class)); + when(mMockDisplayContent.getWindowConfiguration()) + .thenReturn(new WindowConfiguration()); mMockDisplayPolicy = mock(DisplayPolicy.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index 80f6bceb884c..e5e0145095c1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -741,4 +741,35 @@ public class TaskDisplayAreaTests extends WindowTestsBase { assertEquals(isAssistantOnTop ? topPosition : topPosition - 4, getTaskIndexOf(taskDisplayArea, assistRootTask)); } + + /** + * This test verifies proper launch root based on source and candidate task for split screen. + * If a task is launching from a created-by-organizer task, it should be launched into the + * same created-by-organizer task as well. Unless, the candidate task is already positioned in + * the split. + */ + @Test + public void getLaunchRootTaskInSplit() { + final Task rootTask = createTask( + mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + rootTask.mCreatedByOrganizer = true; + final Task adjacentRootTask = createTask( + mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); + adjacentRootTask.mCreatedByOrganizer = true; + final Task candidateTask = createTaskInRootTask(rootTask, 0 /* userId*/); + final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); + adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */); + + // Verify the launch root with candidate task + Task actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED, + ACTIVITY_TYPE_STANDARD, null /* options */, adjacentRootTask /* sourceTask */, + 0 /* launchFlags */, candidateTask); + assertSame(rootTask, actualRootTask.getRootTask()); + + // Verify the launch root task without candidate task + actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED, + ACTIVITY_TYPE_STANDARD, null /* options */, adjacentRootTask /* sourceTask */, + 0 /* launchFlags */); + assertSame(adjacentRootTask, actualRootTask.getRootTask()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 4425962eb8eb..7a704742fba2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -522,6 +522,55 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test + public void testApplyTransaction_requestFocusOnTaskFragment() { + mOrganizer.applyTransaction(mTransaction); + mController.registerOrganizer(mIOrganizer); + final Task task = createTask(mDisplayContent); + final IBinder token0 = new Binder(); + final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(token0) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + final IBinder token1 = new Binder(); + final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(token1) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + mAtm.mWindowOrganizerController.mLaunchTaskFragments.put(token0, tf0); + mAtm.mWindowOrganizerController.mLaunchTaskFragments.put(token1, tf1); + final ActivityRecord activity0 = tf0.getTopMostActivity(); + final ActivityRecord activity1 = tf1.getTopMostActivity(); + + // No effect if the current focus is in a different Task. + final ActivityRecord activityInOtherTask = createActivityRecord(mDefaultDisplay); + mDisplayContent.setFocusedApp(activityInOtherTask); + mTransaction.requestFocusOnTaskFragment(token0); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + assertEquals(activityInOtherTask, mDisplayContent.mFocusedApp); + + // No effect if there is no resumed activity in the request TaskFragment. + activity0.setState(ActivityRecord.State.PAUSED, "test"); + activity1.setState(ActivityRecord.State.RESUMED, "test"); + mDisplayContent.setFocusedApp(activity1); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + assertEquals(activity1, mDisplayContent.mFocusedApp); + + // Set focus to the request TaskFragment when the current focus is in the same Task, and it + // has a resumed activity. + activity0.setState(ActivityRecord.State.RESUMED, "test"); + mDisplayContent.setFocusedApp(activity1); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + assertEquals(activity0, mDisplayContent.mFocusedApp); + } + + @Test public void testTaskFragmentInPip_startActivityInTaskFragment() { setupTaskFragmentInPip(); final ActivityRecord activity = mTaskFragment.getTopMostActivity(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index 3c14777cd7d1..b2043c38d00a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -403,4 +403,29 @@ public class TaskFragmentTest extends WindowTestsBase { assertFalse(activity0.hasOverlayOverUntrustedModeEmbedded()); assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); } + + @Test + public void testIsAllowedToBeEmbeddedInTrustedMode() { + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setCreateParentTask() + .createActivityCount(2) + .build(); + final ActivityRecord activity0 = taskFragment.getBottomMostActivity(); + final ActivityRecord activity1 = taskFragment.getTopMostActivity(); + + // Allowed if all children activities are allowed. + doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0); + doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1); + + assertTrue(taskFragment.isAllowedToBeEmbeddedInTrustedMode()); + + // Disallowed if any child activity is not allowed. + doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0); + + assertFalse(taskFragment.isAllowedToBeEmbeddedInTrustedMode()); + + doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1); + + assertFalse(taskFragment.isAllowedToBeEmbeddedInTrustedMode()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index c672b9173570..7507df6eba74 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1156,10 +1156,6 @@ class WindowTestsBase extends SystemServiceTestsBase { spyOn(activity); if (mTask != null) { - // fullscreen value is normally read from resources in ctor, so for testing we need - // to set it somewhere else since we can't mock resources. - doReturn(true).when(activity).occludesParent(); - doReturn(true).when(activity).fillsParent(); mTask.addChild(activity); if (mOnTop) { // Move the task to front after activity is added. diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 8cbbe947d32d..f31cdcba5830 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -1824,6 +1824,32 @@ public class VoiceInteractionManagerService extends SystemService { } } + public void setSessionWindowVisible(IBinder token, boolean visible) { + synchronized (this) { + if (mImpl == null) { + Slog.w(TAG, "setSessionWindowVisible called without running voice interaction " + + "service"); + return; + } + if (mImpl.mActiveSession == null || token != mImpl.mActiveSession.mToken) { + Slog.w(TAG, "setSessionWindowVisible does not match active session"); + return; + } + final long caller = Binder.clearCallingIdentity(); + try { + mVoiceInteractionSessionListeners.broadcast(listener -> { + try { + listener.onVoiceSessionWindowVisibilityChanged(visible); + } catch (RemoteException e) { + Slog.e(TAG, "Error delivering window visibility event to listener.", e); + } + }); + } finally { + Binder.restoreCallingIdentity(caller); + } + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index c56cc62abe44..235ed842b749 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1293,8 +1293,8 @@ public class ApnSetting implements Parcelable { && Objects.equals(this.mOperatorNumeric, other.mOperatorNumeric) && Objects.equals(this.mProtocol, other.mProtocol) && Objects.equals(this.mRoamingProtocol, other.mRoamingProtocol) - && xorEqualsInt(this.mMtuV4, other.mMtuV4) - && xorEqualsInt(this.mMtuV6, other.mMtuV6) + && mtuUnsetOrEquals(this.mMtuV4, other.mMtuV4) + && mtuUnsetOrEquals(this.mMtuV6, other.mMtuV6) && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled) && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask) && Objects.equals(this.mLingeringNetworkTypeBitmask, @@ -1322,7 +1322,12 @@ public class ApnSetting implements Parcelable { // Equal or one is not specified. private boolean xorEqualsInt(int first, int second) { return first == UNSPECIFIED_INT || second == UNSPECIFIED_INT - || Objects.equals(first, second); + || first == second; + } + + // Equal or one is not specified. Specific to MTU where <= 0 indicates unset. + private boolean mtuUnsetOrEquals(int first, int second) { + return first <= 0 || second <= 0 || first == second; } private String nullToEmpty(String stringValue) { diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java index 90fd08bae4ef..47f87d6d75ff 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java @@ -95,6 +95,8 @@ public final class NotificationTest { PackageManager pm = mContext.getPackageManager(); // Do not run on Automotive. assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)); + // Do not run on TV. Direct Reply isn't supported on TV. + assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)); } @After |