diff options
59 files changed, 16 insertions, 12887 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java b/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java index 36174c6aad3d..3c2184218259 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java @@ -19,8 +19,6 @@ package android.app.job; import android.annotation.SystemApi; import android.app.JobSchedulerImpl; import android.app.SystemServiceRegistry; -import android.app.tare.EconomyManager; -import android.app.tare.IEconomyManager; import android.content.Context; import android.os.DeviceIdleManager; import android.os.IDeviceIdleController; @@ -58,8 +56,5 @@ public class JobSchedulerFrameworkInitializer { SystemServiceRegistry.registerContextAwareService( Context.POWER_EXEMPTION_SERVICE, PowerExemptionManager.class, PowerExemptionManager::new); - SystemServiceRegistry.registerStaticService( - Context.RESOURCE_ECONOMY_SERVICE, EconomyManager.class, - (b) -> new EconomyManager(IEconomyManager.Stub.asInterface(b))); } } diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java deleted file mode 100644 index 0bea028e6f50..000000000000 --- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.tare; - -import android.annotation.IntDef; -import android.annotation.Nullable; -import android.annotation.SystemService; -import android.annotation.TestApi; -import android.content.Context; -import android.os.RemoteException; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Provides access to the resource economy service. - * - * @hide - */ -@TestApi -@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; - } - } - - /** @hide */ - @TestApi - public static final int ENABLED_MODE_OFF = 0; - /** @hide */ - public static final int ENABLED_MODE_ON = 1; - /** - * Go through the motions, tracking events, updating balances and other TARE state values, - * but don't use TARE to affect actual device behavior. - * @hide - */ - @TestApi - public static final int ENABLED_MODE_SHADOW = 2; - - /** @hide */ - @IntDef(prefix = {"ENABLED_MODE_"}, value = { - ENABLED_MODE_OFF, - ENABLED_MODE_ON, - ENABLED_MODE_SHADOW, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface EnabledMode { - } - - /** @hide */ - public static String enabledModeToString(@EnabledMode int mode) { - switch (mode) { - case ENABLED_MODE_OFF: return "ENABLED_MODE_OFF"; - case ENABLED_MODE_ON: return "ENABLED_MODE_ON"; - case ENABLED_MODE_SHADOW: return "ENABLED_MODE_SHADOW"; - default: return "ENABLED_MODE_" + mode; - } - } - - /** @hide */ - @TestApi - public static final String KEY_ENABLE_TARE_MODE = "enable_tare_mode"; - /** @hide */ - public static final String KEY_ENABLE_POLICY_ALARM = "enable_policy_alarm"; - /** @hide */ - public static final String KEY_ENABLE_POLICY_JOB_SCHEDULER = "enable_policy_job"; - /** @hide */ - public static final int DEFAULT_ENABLE_TARE_MODE = ENABLED_MODE_OFF; - /** @hide */ - public static final boolean DEFAULT_ENABLE_POLICY_ALARM = true; - /** @hide */ - public static final boolean DEFAULT_ENABLE_POLICY_JOB_SCHEDULER = true; - - // Keys for AlarmManager TARE factors - /** @hide */ - public static final String KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED = - "am_min_satiated_balance_exempted"; - /** @hide */ - public static final String KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP = - "am_min_satiated_balance_headless_system_app"; - /** @hide */ - public static final String KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP = - "am_min_satiated_balance_other_app"; - /** @hide */ - public static final String KEY_AM_MAX_SATIATED_BALANCE = "am_max_satiated_balance"; - /** @hide */ - public static final String KEY_AM_INITIAL_CONSUMPTION_LIMIT = "am_initial_consumption_limit"; - /** @hide */ - public static final String KEY_AM_MIN_CONSUMPTION_LIMIT = "am_minimum_consumption_limit"; - /** @hide */ - public static final String KEY_AM_MAX_CONSUMPTION_LIMIT = "am_maximum_consumption_limit"; - // TODO: Add AlarmManager modifier keys - /** @hide */ - public static final String KEY_AM_REWARD_TOP_ACTIVITY_INSTANT = - "am_reward_top_activity_instant"; - /** @hide */ - public static final String KEY_AM_REWARD_TOP_ACTIVITY_ONGOING = - "am_reward_top_activity_ongoing"; - /** @hide */ - public static final String KEY_AM_REWARD_TOP_ACTIVITY_MAX = "am_reward_top_activity_max"; - /** @hide */ - public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT = - "am_reward_notification_seen_instant"; - /** @hide */ - public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING = - "am_reward_notification_seen_ongoing"; - /** @hide */ - public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_MAX = - "am_reward_notification_seen_max"; - /** @hide */ - public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_INSTANT = - "am_reward_notification_seen_within_15_instant"; - /** @hide */ - public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_ONGOING = - "am_reward_notification_seen_within_15_ongoing"; - /** @hide */ - public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_MAX = - "am_reward_notification_seen_within_15_max"; - /** @hide */ - public static final String KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT = - "am_reward_notification_interaction_instant"; - /** @hide */ - public static final String KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING = - "am_reward_notification_interaction_ongoing"; - /** @hide */ - public static final String KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX = - "am_reward_notification_interaction_max"; - /** @hide */ - public static final String KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT = - "am_reward_widget_interaction_instant"; - /** @hide */ - public static final String KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING = - "am_reward_widget_interaction_ongoing"; - /** @hide */ - public static final String KEY_AM_REWARD_WIDGET_INTERACTION_MAX = - "am_reward_widget_interaction_max"; - /** @hide */ - public static final String KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT = - "am_reward_other_user_interaction_instant"; - /** @hide */ - public static final String KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING = - "am_reward_other_user_interaction_ongoing"; - /** @hide */ - public static final String KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX = - "am_reward_other_user_interaction_max"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP = - "am_action_alarm_allow_while_idle_exact_wakeup_ctp"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP = - "am_action_alarm_allow_while_idle_inexact_wakeup_ctp"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP = - "am_action_alarm_exact_wakeup_ctp"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP = - "am_action_alarm_inexact_wakeup_ctp"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP = - "am_action_alarm_allow_while_idle_exact_nonwakeup_ctp"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP = - "am_action_alarm_exact_nonwakeup_ctp"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP = - "am_action_alarm_allow_while_idle_inexact_nonwakeup_ctp"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP = - "am_action_alarm_inexact_nonwakeup_ctp"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP = - "am_action_alarm_alarmclock_ctp"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE = - "am_action_alarm_allow_while_idle_exact_wakeup_base_price"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE = - "am_action_alarm_allow_while_idle_inexact_wakeup_base_price"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE = - "am_action_alarm_exact_wakeup_base_price"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE = - "am_action_alarm_inexact_wakeup_base_price"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE = - "am_action_alarm_allow_while_idle_exact_nonwakeup_base_price"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE = - "am_action_alarm_exact_nonwakeup_base_price"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE = - "am_action_alarm_allow_while_idle_inexact_nonwakeup_base_price"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE = - "am_action_alarm_inexact_nonwakeup_base_price"; - /** @hide */ - public static final String KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE = - "am_action_alarm_alarmclock_base_price"; - -// Keys for JobScheduler TARE factors - /** @hide */ - public static final String KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED = - "js_min_satiated_balance_exempted"; - /** @hide */ - public static final String KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP = - "js_min_satiated_balance_headless_system_app"; - /** @hide */ - public static final String KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP = - "js_min_satiated_balance_other_app"; - /** @hide */ - public static final String KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER = - "js_min_satiated_balance_increment_updater"; - /** @hide */ - public static final String KEY_JS_MAX_SATIATED_BALANCE = - "js_max_satiated_balance"; - /** @hide */ - public static final String KEY_JS_INITIAL_CONSUMPTION_LIMIT = "js_initial_consumption_limit"; - /** @hide */ - public static final String KEY_JS_MIN_CONSUMPTION_LIMIT = "js_minimum_consumption_limit"; - /** @hide */ - public static final String KEY_JS_MAX_CONSUMPTION_LIMIT = "js_maximum_consumption_limit"; - // TODO: Add JobScheduler modifier keys - /** @hide */ - public static final String KEY_JS_REWARD_APP_INSTALL_INSTANT = - "js_reward_app_install_instant"; - /** @hide */ - public static final String KEY_JS_REWARD_APP_INSTALL_ONGOING = - "js_reward_app_install_ongoing"; - /** @hide */ - public static final String KEY_JS_REWARD_APP_INSTALL_MAX = - "js_reward_app_install_max"; - /** @hide */ - public static final String KEY_JS_REWARD_TOP_ACTIVITY_INSTANT = - "js_reward_top_activity_instant"; - /** @hide */ - public static final String KEY_JS_REWARD_TOP_ACTIVITY_ONGOING = - "js_reward_top_activity_ongoing"; - /** @hide */ - public static final String KEY_JS_REWARD_TOP_ACTIVITY_MAX = - "js_reward_top_activity_max"; - /** @hide */ - public static final String KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT = - "js_reward_notification_seen_instant"; - /** @hide */ - public static final String KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING = - "js_reward_notification_seen_ongoing"; - /** @hide */ - public static final String KEY_JS_REWARD_NOTIFICATION_SEEN_MAX = - "js_reward_notification_seen_max"; - /** @hide */ - public static final String KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT = - "js_reward_notification_interaction_instant"; - /** @hide */ - public static final String KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING = - "js_reward_notification_interaction_ongoing"; - /** @hide */ - public static final String KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX = - "js_reward_notification_interaction_max"; - /** @hide */ - public static final String KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT = - "js_reward_widget_interaction_instant"; - /** @hide */ - public static final String KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING = - "js_reward_widget_interaction_ongoing"; - /** @hide */ - public static final String KEY_JS_REWARD_WIDGET_INTERACTION_MAX = - "js_reward_widget_interaction_max"; - /** @hide */ - public static final String KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT = - "js_reward_other_user_interaction_instant"; - /** @hide */ - public static final String KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING = - "js_reward_other_user_interaction_ongoing"; - /** @hide */ - public static final String KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX = - "js_reward_other_user_interaction_max"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_MAX_START_CTP = "js_action_job_max_start_ctp"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_MAX_RUNNING_CTP = "js_action_job_max_running_ctp"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_HIGH_START_CTP = "js_action_job_high_start_ctp"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP = - "js_action_job_high_running_ctp"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_DEFAULT_START_CTP = - "js_action_job_default_start_ctp"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP = - "js_action_job_default_running_ctp"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_LOW_START_CTP = "js_action_job_low_start_ctp"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_LOW_RUNNING_CTP = "js_action_job_low_running_ctp"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_MIN_START_CTP = "js_action_job_min_start_ctp"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_MIN_RUNNING_CTP = "js_action_job_min_running_ctp"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP = - "js_action_job_timeout_penalty_ctp"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE = - "js_action_job_max_start_base_price"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE = - "js_action_job_max_running_base_price"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE = - "js_action_job_high_start_base_price"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE = - "js_action_job_high_running_base_price"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE = - "js_action_job_default_start_base_price"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE = - "js_action_job_default_running_base_price"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE = - "js_action_job_low_start_base_price"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE = - "js_action_job_low_running_base_price"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE = - "js_action_job_min_start_base_price"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE = - "js_action_job_min_running_base_price"; - /** @hide */ - public static final String KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE = - "js_action_job_timeout_penalty_base_price"; - - // Default values AlarmManager factors - /** @hide */ - public static final long DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES = arcToCake(500); - /** @hide */ - public static final long DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES = - arcToCake(256); - /** @hide */ - public static final long DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES = arcToCake(160); - /** @hide */ - public static final long DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES = arcToCake(960); - /** @hide */ - public static final long DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES = arcToCake(2880); - /** @hide */ - public static final long DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES = arcToCake(1440); - /** @hide */ - public static final long DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES = arcToCake(15_000); - // TODO: add AlarmManager modifier default values - /** @hide */ - public static final long DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT_CAKES = arcToCake(0); - /** @hide */ - // 10 megacakes = .01 ARC - public static final long DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING_CAKES = 10_000_000; - /** @hide */ - public static final long DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX_CAKES = arcToCake(500); - /** @hide */ - public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES = arcToCake(3); - /** @hide */ - public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES = arcToCake(0); - /** @hide */ - public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX_CAKES = arcToCake(60); - /** @hide */ - public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_INSTANT_CAKES = - arcToCake(5); - /** @hide */ - public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_ONGOING_CAKES = - arcToCake(0); - /** @hide */ - public static final long DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_MAX_CAKES = - arcToCake(500); - /** @hide */ - public static final long DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES = - arcToCake(5); - /** @hide */ - public static final long DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES = - arcToCake(0); - /** @hide */ - public static final long DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES = arcToCake(500); - /** @hide */ - public static final long DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT_CAKES = arcToCake(10); - /** @hide */ - public static final long DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING_CAKES = arcToCake(0); - /** @hide */ - public static final long DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX_CAKES = arcToCake(500); - /** @hide */ - public static final long DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES = arcToCake(10); - /** @hide */ - public static final long DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES = arcToCake(0); - /** @hide */ - public static final long DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX_CAKES = arcToCake(500); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES = - arcToCake(3); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES = - arcToCake(3); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES = arcToCake(3); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES = arcToCake(3); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES = - arcToCake(1); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES = arcToCake(1); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP_CAKES = - arcToCake(1); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP_CAKES = arcToCake(1); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES = arcToCake(5); - /** @hide */ - public static final long - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES = arcToCake(5); - /** @hide */ - public static final long - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE_CAKES = arcToCake(4); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES = arcToCake(4); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES = arcToCake(3); - /** @hide */ - public static final long - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE_CAKES = - arcToCake(3); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES = - arcToCake(2); - /** @hide */ - public static final long - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES = - arcToCake(2); - /** @hide */ - public static final long DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE_CAKES = - arcToCake(1); - /** @hide */ - 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 long DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES = arcToCake(15000); - /** @hide */ - public static final long DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES = - arcToCake(7500); - /** @hide */ - public static final long DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES = arcToCake(2000); - /** @hide */ - public static final long DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES = arcToCake(60000); - /** @hide */ - public static final long DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES = arcToCake(29_000); - /** @hide */ - public static final long DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES = arcToCake(17_000); - /** @hide */ - // TODO: set maximum limit based on device type (phone vs tablet vs etc) + battery size - public static final long DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES = arcToCake(250_000); - // TODO: add JobScheduler modifier default values - /** @hide */ - public static final long DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES = arcToCake(408); - /** @hide */ - public static final long DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES = arcToCake(0); - /** @hide */ - public static final long DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES = arcToCake(4000); - /** @hide */ - public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES = arcToCake(0); - /** @hide */ - public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES = CAKE_IN_ARC / 2; - /** @hide */ - public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES = arcToCake(15000); - /** @hide */ - public static final long DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES = arcToCake(1); - /** @hide */ - public static final long DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES = arcToCake(0); - /** @hide */ - public static final long DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES = arcToCake(10); - /** @hide */ - public static final long DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES = - arcToCake(5); - /** @hide */ - public static final long DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES = - arcToCake(0); - /** @hide */ - public static final long DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES = arcToCake(5000); - /** @hide */ - public static final long DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES = arcToCake(10); - /** @hide */ - public static final long DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES = arcToCake(0); - /** @hide */ - public static final long DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX_CAKES = arcToCake(5000); - /** @hide */ - public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES = arcToCake(10); - /** @hide */ - public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES = arcToCake(0); - /** @hide */ - public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES = arcToCake(5000); - /** - * How many credits to increase the updating app's min satiated balance by for each app that it - * is responsible for updating. - * @hide - */ - public static final long DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES = - // Research indicates that the median time between popular app updates is 13-14 days, - // so adjust by 14 to amortize over that time. - DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES / 14; - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES = arcToCake(3); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES = arcToCake(2); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES = arcToCake(3); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES = arcToCake(2); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES = arcToCake(3); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES = arcToCake(2); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES = arcToCake(3); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES = arcToCake(2); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES = arcToCake(3); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES = arcToCake(2); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES = arcToCake(30); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE_CAKES = arcToCake(10); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE_CAKES = arcToCake(5); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE_CAKES = arcToCake(8); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE_CAKES = arcToCake(4); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE_CAKES = arcToCake(6); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE_CAKES = arcToCake(3); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE_CAKES = arcToCake(4); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE_CAKES = arcToCake(2); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE_CAKES = arcToCake(2); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE_CAKES = arcToCake(1); - /** @hide */ - public static final long DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES = arcToCake(60); - - //////// APIs below //////// - - private final IEconomyManager mService; - - /** @hide */ - public EconomyManager(IEconomyManager service) { - mService = service; - } - - /** - * Returns the current enabled status of TARE. - * @hide - */ - @EnabledMode - @TestApi - public int getEnabledMode() { - try { - return mService.getEnabledMode(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } -} diff --git a/apex/jobscheduler/framework/java/android/app/tare/IEconomyManager.aidl b/apex/jobscheduler/framework/java/android/app/tare/IEconomyManager.aidl deleted file mode 100644 index 2be0db7a4c9f..000000000000 --- a/apex/jobscheduler/framework/java/android/app/tare/IEconomyManager.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.tare; - - /** - * IPC interface that supports the app-facing {@link #EconomyManager} api. - * {@hide} - */ -interface IEconomyManager { - int getEnabledMode(); -} diff --git a/apex/jobscheduler/framework/java/android/app/tare/OWNERS b/apex/jobscheduler/framework/java/android/app/tare/OWNERS deleted file mode 100644 index 217a5edff08b..000000000000 --- a/apex/jobscheduler/framework/java/android/app/tare/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS
\ No newline at end of file diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java index 6f8014faf91a..7958d81ca163 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java @@ -45,7 +45,7 @@ import java.util.Date; */ class Alarm { @VisibleForTesting - public static final int NUM_POLICIES = 5; + public static final int NUM_POLICIES = 4; /** * Index used to store the time the alarm was requested to expire. To be used with * {@link #setPolicyElapsed(int, long)}. @@ -69,12 +69,6 @@ class Alarm { public static final int BATTERY_SAVER_POLICY_INDEX = 3; /** - * Index used to store the earliest time the alarm can expire based on TARE policy. - * To be used with {@link #setPolicyElapsed(int, long)}. - */ - public static final int TARE_POLICY_INDEX = 4; - - /** * Reason to use for inexact alarms. */ static final int EXACT_ALLOW_REASON_NOT_APPLICABLE = -1; @@ -278,8 +272,6 @@ class Alarm { return "device_idle"; case BATTERY_SAVER_POLICY_INDEX: return "battery_saver"; - case TARE_POLICY_INDEX: - return "tare"; default: return "--unknown(" + index + ")--"; } diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 154b2d763af8..f9c8e0b551bd 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -57,7 +57,6 @@ import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION; import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_POLICY_PERMISSION; import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PRIORITIZED; import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX; -import static com.android.server.alarm.Alarm.TARE_POLICY_INDEX; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED; import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_ALARM_CANCELLED; import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_DATA_CLEARED; @@ -88,7 +87,6 @@ import android.app.IAlarmManager; import android.app.PendingIntent; import android.app.compat.CompatChanges; import android.app.role.RoleManager; -import android.app.tare.EconomyManager; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; @@ -137,7 +135,6 @@ import android.util.Log; import android.util.LongArrayQueue; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseArrayMap; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.SparseLongArray; @@ -171,8 +168,6 @@ import com.android.server.SystemTimeZone.TimeZoneConfidence; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.tare.AlarmManagerEconomicPolicy; -import com.android.server.tare.EconomyManagerInternal; import com.android.server.usage.AppStandbyInternal; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; @@ -222,7 +217,6 @@ public class AlarmManagerService extends SystemService { static final boolean DEBUG_WAKELOCK = localLOGV || false; static final boolean DEBUG_BG_LIMIT = localLOGV || false; static final boolean DEBUG_STANDBY = localLOGV || false; - static final boolean DEBUG_TARE = localLOGV || false; static final boolean RECORD_ALARMS_IN_HISTORY = true; static final boolean RECORD_DEVICE_IDLE_ALARMS = false; @@ -259,7 +253,6 @@ public class AlarmManagerService extends SystemService { DeviceIdleInternal mLocalDeviceIdleController; private UsageStatsManagerInternal mUsageStatsManagerInternal; private ActivityManagerInternal mActivityManagerInternal; - private final EconomyManagerInternal mEconomyManagerInternal; private PackageManagerInternal mPackageManagerInternal; private BatteryStatsInternal mBatteryStatsInternal; private RoleManager mRoleManager; @@ -280,14 +273,6 @@ public class AlarmManagerService extends SystemService { @GuardedBy("mLock") SparseIntArray mLastOpScheduleExactAlarm = new SparseIntArray(); - /** - * Local cache of the ability of each userId-pkg to afford the various bills we're tracking for - * them. - */ - @GuardedBy("mLock") - private final SparseArrayMap<String, ArrayMap<EconomyManagerInternal.ActionBill, Boolean>> - mAffordabilityCache = new SparseArrayMap<>(); - // List of alarms per uid deferred due to user applied background restrictions on the source app SparseArray<ArrayList<Alarm>> mPendingBackgroundAlarms = new SparseArray<>(); @@ -361,13 +346,11 @@ public class AlarmManagerService extends SystemService { interface Stats { int REORDER_ALARMS_FOR_STANDBY = 0; int HAS_SCHEDULE_EXACT_ALARM = 1; - int REORDER_ALARMS_FOR_TARE = 2; } private final StatLogger mStatLogger = new StatLogger("Alarm manager stats", new String[]{ "REORDER_ALARMS_FOR_STANDBY", "HAS_SCHEDULE_EXACT_ALARM", - "REORDER_ALARMS_FOR_TARE", }); BroadcastOptions mOptsWithFgs = makeBasicAlarmBroadcastOptions(); @@ -686,8 +669,7 @@ public class AlarmManagerService extends SystemService { * holding the AlarmManagerService.mLock lock. */ @VisibleForTesting - final class Constants implements DeviceConfig.OnPropertiesChangedListener, - EconomyManagerInternal.TareStateChangeListener { + final class Constants implements DeviceConfig.OnPropertiesChangedListener { // Key names stored in the settings value. @VisibleForTesting static final String KEY_MIN_FUTURITY = "min_futurity"; @@ -858,9 +840,6 @@ public class AlarmManagerService extends SystemService { */ public long MAX_DEVICE_IDLE_FUZZ = DEFAULT_MAX_DEVICE_IDLE_FUZZ; - public int USE_TARE_POLICY = EconomyManager.DEFAULT_ENABLE_POLICY_ALARM - ? EconomyManager.DEFAULT_ENABLE_TARE_MODE : EconomyManager.ENABLED_MODE_OFF; - /** * The amount of temporary reserve quota to give apps on receiving the * {@link AppIdleStateChangeListener#triggerTemporaryQuotaBump(String, int)} callback @@ -900,13 +879,7 @@ public class AlarmManagerService extends SystemService { public void start() { mInjector.registerDeviceConfigListener(this); - final EconomyManagerInternal economyManagerInternal = - LocalServices.getService(EconomyManagerInternal.class); - economyManagerInternal.registerTareStateChangeListener(this, - AlarmManagerEconomicPolicy.POLICY_ALARM); onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER)); - updateTareSettings( - economyManagerInternal.getEnabledMode(AlarmManagerEconomicPolicy.POLICY_ALARM)); } @SuppressLint("MissingPermission") @@ -1064,42 +1037,6 @@ public class AlarmManagerService extends SystemService { } } - @Override - public void onTareEnabledModeChanged(@EconomyManager.EnabledMode int enabledMode) { - updateTareSettings(enabledMode); - } - - private void updateTareSettings(int enabledMode) { - synchronized (mLock) { - if (USE_TARE_POLICY != enabledMode) { - USE_TARE_POLICY = enabledMode; - final boolean changed = mAlarmStore.updateAlarmDeliveries(alarm -> { - final boolean standbyChanged = adjustDeliveryTimeBasedOnBucketLocked(alarm); - final boolean tareChanged = adjustDeliveryTimeBasedOnTareLocked(alarm); - if (USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) { - // Only register listeners if we're going to be acting on the policy. - registerTareListener(alarm); - } else { - mEconomyManagerInternal.unregisterAffordabilityChangeListener( - UserHandle.getUserId(alarm.uid), alarm.sourcePackage, - mAffordabilityChangeListener, - TareBill.getAppropriateBill(alarm)); - } - return standbyChanged || tareChanged; - }); - if (USE_TARE_POLICY != EconomyManager.ENABLED_MODE_ON) { - // Remove the cached values so we don't accidentally use them when TARE is - // re-enabled. - mAffordabilityCache.clear(); - } - if (changed) { - rescheduleKernelAlarmsLocked(); - updateNextAlarmClockLocked(); - } - } - } - } - private void updateDeviceIdleFuzzBoundaries() { final DeviceConfig.Properties properties = DeviceConfig.getProperties( DeviceConfig.NAMESPACE_ALARM_MANAGER, @@ -1255,10 +1192,6 @@ public class AlarmManagerService extends SystemService { TimeUtils.formatDuration(MAX_DEVICE_IDLE_FUZZ, pw); pw.println(); - pw.print(Settings.Global.ENABLE_TARE, - EconomyManager.enabledModeToString(USE_TARE_POLICY)); - pw.println(); - pw.print(KEY_TEMPORARY_QUOTA_BUMP, TEMPORARY_QUOTA_BUMP); pw.println(); @@ -1394,7 +1327,6 @@ public class AlarmManagerService extends SystemService { AlarmManagerService(Context context, Injector injector) { super(context); mInjector = injector; - mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class); } public AlarmManagerService(Context context) { @@ -1501,29 +1433,6 @@ public class AlarmManagerService extends SystemService { return changed; } - /** - * Recalculates alarm send times based on TARE wealth. - * - * @param targetPackages [Package, User] pairs for which alarms need to be re-evaluated, - * null indicates all - * @return True if there was any reordering done to the current list. - */ - boolean reorderAlarmsBasedOnTare(ArraySet<UserPackage> targetPackages) { - final long start = mStatLogger.getTime(); - - final boolean changed = mAlarmStore.updateAlarmDeliveries(a -> { - final UserPackage userPackage = - UserPackage.of(UserHandle.getUserId(a.creatorUid), a.sourcePackage); - if (targetPackages != null && !targetPackages.contains(userPackage)) { - return false; - } - return adjustDeliveryTimeBasedOnTareLocked(a); - }); - - mStatLogger.logDurationStat(Stats.REORDER_ALARMS_FOR_TARE, start); - return changed; - } - private boolean restoreRequestedTime(Alarm a) { return a.setPolicyElapsed(REQUESTER_POLICY_INDEX, convertToElapsed(a.origWhen, a.type)); } @@ -2581,8 +2490,7 @@ public class AlarmManagerService extends SystemService { */ private boolean adjustDeliveryTimeBasedOnBucketLocked(Alarm alarm) { final long nowElapsed = mInjector.getElapsedRealtimeMillis(); - if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON - || isExemptFromAppStandby(alarm) || mAppStandbyParole) { + if (isExemptFromAppStandby(alarm) || mAppStandbyParole) { return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed); } @@ -2633,60 +2541,6 @@ public class AlarmManagerService extends SystemService { return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed); } - /** - * Adjusts the alarm's policy time for TARE. - * - * @param alarm The alarm to update. - * @return {@code true} if the actual delivery time of the given alarm was updated due to - * adjustments made in this call. - */ - private boolean adjustDeliveryTimeBasedOnTareLocked(Alarm alarm) { - final long nowElapsed = mInjector.getElapsedRealtimeMillis(); - if (mConstants.USE_TARE_POLICY != EconomyManager.ENABLED_MODE_ON - || isExemptFromTare(alarm) || hasEnoughWealthLocked(alarm)) { - return alarm.setPolicyElapsed(TARE_POLICY_INDEX, nowElapsed); - } - - // Not enough wealth. Just keep deferring indefinitely till the quota changes. - return alarm.setPolicyElapsed(TARE_POLICY_INDEX, nowElapsed + INDEFINITE_DELAY); - } - - private void registerTareListener(Alarm alarm) { - if (mConstants.USE_TARE_POLICY != EconomyManager.ENABLED_MODE_ON) { - // Only register listeners if we're going to be acting on the policy. - return; - } - mEconomyManagerInternal.registerAffordabilityChangeListener( - UserHandle.getUserId(alarm.creatorUid), alarm.sourcePackage, - mAffordabilityChangeListener, TareBill.getAppropriateBill(alarm)); - } - - /** Unregister the TARE listener associated with the alarm if it's no longer needed. */ - @GuardedBy("mLock") - private void maybeUnregisterTareListenerLocked(Alarm alarm) { - if (mConstants.USE_TARE_POLICY != EconomyManager.ENABLED_MODE_ON) { - return; - } - final EconomyManagerInternal.ActionBill bill = TareBill.getAppropriateBill(alarm); - final Predicate<Alarm> isSameAlarmTypeForSameApp = (a) -> - alarm.creatorUid == a.creatorUid - && alarm.sourcePackage.equals(a.sourcePackage) - && bill.equals(TareBill.getAppropriateBill(a)); - if (mAlarmStore.getCount(isSameAlarmTypeForSameApp) == 0) { - final int userId = UserHandle.getUserId(alarm.creatorUid); - mEconomyManagerInternal.unregisterAffordabilityChangeListener( - userId, alarm.sourcePackage, - mAffordabilityChangeListener, bill); - // Remove the cached value so we don't accidentally use it when the app - // schedules a new alarm. - ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability = - mAffordabilityCache.get(userId, alarm.sourcePackage); - if (actionAffordability != null) { - actionAffordability.remove(bill); - } - } - } - @GuardedBy("mLock") private void setImplLocked(Alarm a) { if ((a.flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) { @@ -2734,8 +2588,6 @@ public class AlarmManagerService extends SystemService { } adjustDeliveryTimeBasedOnBatterySaver(a); adjustDeliveryTimeBasedOnBucketLocked(a); - adjustDeliveryTimeBasedOnTareLocked(a); - registerTareListener(a); mAlarmStore.add(a); rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); @@ -3168,37 +3020,8 @@ public class AlarmManagerService extends SystemService { pw.println(); pw.println(); - if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) { - pw.println("TARE details:"); - pw.increaseIndent(); - - pw.println("Affordability cache:"); - pw.increaseIndent(); - mAffordabilityCache.forEach((userId, pkgName, billMap) -> { - final int numBills = billMap.size(); - if (numBills > 0) { - pw.print(userId); - pw.print(":"); - pw.print(pkgName); - pw.println(":"); - - pw.increaseIndent(); - for (int i = 0; i < numBills; ++i) { - pw.print(TareBill.getName(billMap.keyAt(i))); - pw.print(": "); - pw.println(billMap.valueAt(i)); - } - pw.decreaseIndent(); - } - }); - pw.decreaseIndent(); - - pw.decreaseIndent(); - pw.println(); - } else { - pw.println("App Standby Parole: " + mAppStandbyParole); - pw.println(); - } + pw.println("App Standby Parole: " + mAppStandbyParole); + pw.println(); if (mAppStateTracker != null) { mAppStateTracker.dump(pw); @@ -4136,7 +3959,6 @@ public class AlarmManagerService extends SystemService { mRemovalHistory.put(removed.uid, bufferForUid); } bufferForUid.append(new RemovedAlarm(removed, reason, nowRtc, nowElapsed)); - maybeUnregisterTareListenerLocked(removed); } if (removedFromStore) { @@ -4496,11 +4318,6 @@ public class AlarmManagerService extends SystemService { alarm.uid, alarm.statsTag); } mDeliveryTracker.deliverLocked(alarm, nowELAPSED); - reportAlarmEventToTare(alarm); - if (alarm.repeatInterval <= 0) { - // Don't bother trying to unregister for a repeating alarm. - maybeUnregisterTareListenerLocked(alarm); - } } catch (RuntimeException e) { Slog.w(TAG, "Failure sending alarm.", e); } @@ -4509,52 +4326,6 @@ public class AlarmManagerService extends SystemService { } } - private void reportAlarmEventToTare(Alarm alarm) { - // Don't bother reporting events if TARE is completely off. - if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_OFF) { - return; - } - final boolean allowWhileIdle = - (alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_ALLOW_WHILE_IDLE)) != 0; - final int action; - if (alarm.alarmClock != null) { - action = AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK; - } else if (alarm.wakeup) { - if (alarm.windowLength == 0) { - if (allowWhileIdle) { - action = AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE; - } else { - action = AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT; - } - } else { - if (allowWhileIdle) { - action = - AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE; - } else { - action = AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT; - } - } - } else { - if (alarm.windowLength == 0) { - if (allowWhileIdle) { - action = AlarmManagerEconomicPolicy - .ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE; - } else { - action = AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT; - } - } else { - if (allowWhileIdle) { - action = AlarmManagerEconomicPolicy - .ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE; - } else { - action = AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT; - } - } - } - mEconomyManagerInternal.noteInstantaneousEvent( - UserHandle.getUserId(alarm.creatorUid), alarm.sourcePackage, action, null); - } - @VisibleForTesting static boolean isExemptFromAppStandby(Alarm a) { return a.alarmClock != null || UserHandle.isCore(a.creatorUid) @@ -4562,12 +4333,6 @@ public class AlarmManagerService extends SystemService { } @VisibleForTesting - static boolean isExemptFromTare(Alarm a) { - return a.alarmClock != null || UserHandle.isCore(a.creatorUid) - || (a.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0; - } - - @VisibleForTesting static class Injector { private long mNativeData; private Context mContext; @@ -4814,13 +4579,7 @@ public class AlarmManagerService extends SystemService { wakeupUids.add(a.uid); increment(wakeupCountsPerUid, a.uid); } - if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) { - if (!isExemptFromTare(a)) { - triggerPackages.add(UserPackage.of( - UserHandle.getUserId(a.creatorUid), - a.sourcePackage)); - } - } else if (!isExemptFromAppStandby(a)) { + if (!isExemptFromAppStandby(a)) { triggerPackages.add(UserPackage.of( UserHandle.getUserId(a.creatorUid), a.sourcePackage)); } @@ -4831,11 +4590,7 @@ public class AlarmManagerService extends SystemService { } deliverAlarmsLocked(triggerList, nowELAPSED); mTemporaryQuotaReserve.cleanUpExpiredQuotas(nowELAPSED); - if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) { - reorderAlarmsBasedOnTare(triggerPackages); - } else { - reorderAlarmsBasedOnStandbyBuckets(triggerPackages); - } + reorderAlarmsBasedOnStandbyBuckets(triggerPackages); rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); logAlarmBatchDelivered( @@ -4914,32 +4669,6 @@ public class AlarmManagerService extends SystemService { return alarm.creatorUid; } - @GuardedBy("mLock") - private boolean canAffordBillLocked(@NonNull Alarm alarm, - @NonNull EconomyManagerInternal.ActionBill bill) { - final int userId = UserHandle.getUserId(alarm.creatorUid); - final String pkgName = alarm.sourcePackage; - ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability = - mAffordabilityCache.get(userId, pkgName); - if (actionAffordability == null) { - actionAffordability = new ArrayMap<>(); - mAffordabilityCache.add(userId, pkgName, actionAffordability); - } - - if (actionAffordability.containsKey(bill)) { - return actionAffordability.get(bill); - } - - final boolean canAfford = mEconomyManagerInternal.canPayFor(userId, pkgName, bill); - actionAffordability.put(bill, canAfford); - return canAfford; - } - - @GuardedBy("mLock") - private boolean hasEnoughWealthLocked(@NonNull Alarm alarm) { - return canAffordBillLocked(alarm, TareBill.getAppropriateBill(alarm)); - } - private Bundle getAlarmOperationBundle(Alarm alarm) { if (alarm.mIdleOptions != null) { return alarm.mIdleOptions; @@ -4965,7 +4694,6 @@ public class AlarmManagerService extends SystemService { // Unused id 9 // Unused id 10 public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11; - public static final int TARE_AFFORDABILITY_CHANGED = 12; public static final int CHECK_EXACT_ALARM_PERMISSION_ON_UPDATE = 13; public static final int TEMPORARY_QUOTA_CHANGED = 14; public static final int REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED = 15; @@ -5046,20 +4774,6 @@ public class AlarmManagerService extends SystemService { } break; - case TARE_AFFORDABILITY_CHANGED: - synchronized (mLock) { - final int userId = msg.arg1; - final String packageName = (String) msg.obj; - - final ArraySet<UserPackage> filterPackages = new ArraySet<>(); - filterPackages.add(UserPackage.of(userId, packageName)); - if (reorderAlarmsBasedOnTare(filterPackages)) { - rescheduleKernelAlarmsLocked(); - updateNextAlarmClockLocked(); - } - } - break; - case REMOVE_FOR_CANCELED: final PendingIntent operation = (PendingIntent) msg.obj; synchronized (mLock) { @@ -5366,33 +5080,6 @@ public class AlarmManagerService extends SystemService { } } - private final EconomyManagerInternal.AffordabilityChangeListener mAffordabilityChangeListener = - new EconomyManagerInternal.AffordabilityChangeListener() { - @Override - public void onAffordabilityChanged(int userId, @NonNull String packageName, - @NonNull EconomyManagerInternal.ActionBill bill, boolean canAfford) { - if (DEBUG_TARE) { - Slog.d(TAG, - userId + ":" + packageName + " affordability for " - + TareBill.getName(bill) + " changed to " + canAfford); - } - - synchronized (mLock) { - ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability = - mAffordabilityCache.get(userId, packageName); - if (actionAffordability == null) { - actionAffordability = new ArrayMap<>(); - mAffordabilityCache.add(userId, packageName, actionAffordability); - } - actionAffordability.put(bill, canAfford); - } - - mHandler.obtainMessage(AlarmHandler.TARE_AFFORDABILITY_CHANGED, userId, - canAfford ? 1 : 0, packageName) - .sendToTarget(); - } - }; - private final Listener mForceAppStandbyListener = new Listener() { @Override diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/TareBill.java b/apex/jobscheduler/service/java/com/android/server/alarm/TareBill.java deleted file mode 100644 index a348136b0dcd..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/alarm/TareBill.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.alarm; - -import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE; -import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; - -import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK; -import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT; -import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE; -import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT; -import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE; -import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT; -import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE; -import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT; -import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE; - -import android.annotation.NonNull; - -import com.android.server.tare.EconomyManagerInternal; -import com.android.server.tare.EconomyManagerInternal.ActionBill; - -import java.util.List; - -/** - * Container to maintain alarm TARE {@link ActionBill ActionBills} and their related methods. - */ -final class TareBill { - /** - * Bill to use for AlarmClocks. - */ - static final ActionBill ALARM_CLOCK = new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction(ACTION_ALARM_CLOCK, 1, 0))); - /** - * Bills to use for various alarm types. - */ - static final ActionBill NONWAKEUP_INEXACT_ALARM = new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction(ACTION_ALARM_NONWAKEUP_INEXACT, 1, 0))); - static final ActionBill NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM = new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE, 1, 0))); - static final ActionBill NONWAKEUP_EXACT_ALARM = new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction(ACTION_ALARM_NONWAKEUP_EXACT, 1, 0))); - static final ActionBill NONWAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM = new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE, 1, 0))); - static final ActionBill WAKEUP_INEXACT_ALARM = new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction(ACTION_ALARM_WAKEUP_INEXACT, 1, 0))); - static final ActionBill WAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM = new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE, 1, 0))); - static final ActionBill WAKEUP_EXACT_ALARM = new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction(ACTION_ALARM_WAKEUP_EXACT, 1, 0))); - static final ActionBill WAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM = new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, 1, 0))); - - @NonNull - static ActionBill getAppropriateBill(@NonNull Alarm alarm) { - if (alarm.alarmClock != null) { - return ALARM_CLOCK; - } - - final boolean allowWhileIdle = - (alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_ALLOW_WHILE_IDLE)) != 0; - final boolean isExact = alarm.windowLength == 0; - - if (alarm.wakeup) { - if (isExact) { - if (allowWhileIdle) { - return WAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM; - } - return WAKEUP_EXACT_ALARM; - } - // Inexact - if (allowWhileIdle) { - return WAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM; - } - return WAKEUP_INEXACT_ALARM; - } - - // Nonwakeup - if (isExact) { - if (allowWhileIdle) { - return NONWAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM; - } - return NONWAKEUP_EXACT_ALARM; - - } - if (allowWhileIdle) { - return NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM; - } - return NONWAKEUP_INEXACT_ALARM; - } - - @NonNull - static String getName(@NonNull ActionBill bill) { - if (bill.equals(ALARM_CLOCK)) { - return "ALARM_CLOCK_BILL"; - } - if (bill.equals(NONWAKEUP_INEXACT_ALARM)) { - return "NONWAKEUP_INEXACT_ALARM_BILL"; - } - if (bill.equals(NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM)) { - return "NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM_BILL"; - } - if (bill.equals(NONWAKEUP_EXACT_ALARM)) { - return "NONWAKEUP_EXACT_ALARM_BILL"; - } - if (bill.equals(NONWAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM)) { - return "NONWAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM_BILL"; - } - if (bill.equals(WAKEUP_INEXACT_ALARM)) { - return "WAKEUP_INEXACT_ALARM_BILL"; - } - if (bill.equals(WAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM)) { - return "WAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM_BILL"; - } - if (bill.equals(WAKEUP_EXACT_ALARM)) { - return "WAKEUP_EXACT_ALARM_BILL"; - } - if (bill.equals(WAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM)) { - return "WAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM_BILL"; - } - return "UNKNOWN_BILL (" + bill.toString() + ")"; - } - - private TareBill() { - } -} 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 88a3c6f97eb6..10162fd070bb 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -45,7 +45,6 @@ import android.app.job.JobService; import android.app.job.JobSnapshot; import android.app.job.JobWorkItem; import android.app.job.UserVisibleJobSummary; -import android.app.tare.EconomyManager; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.compat.annotation.ChangeId; @@ -85,7 +84,6 @@ import android.os.UserHandle; import android.os.WorkSource; import android.os.storage.StorageManagerInternal; import android.provider.DeviceConfig; -import android.provider.Settings; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -130,13 +128,10 @@ import com.android.server.job.controllers.QuotaController; import com.android.server.job.controllers.RestrictingController; import com.android.server.job.controllers.StateController; import com.android.server.job.controllers.StorageController; -import com.android.server.job.controllers.TareController; import com.android.server.job.controllers.TimeController; import com.android.server.job.restrictions.JobRestriction; import com.android.server.job.restrictions.ThermalStatusRestriction; import com.android.server.pm.UserManagerInternal; -import com.android.server.tare.EconomyManagerInternal; -import com.android.server.tare.JobSchedulerEconomicPolicy; import com.android.server.usage.AppStandbyInternal; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import com.android.server.utils.quota.Categorizer; @@ -310,8 +305,6 @@ public class JobSchedulerService extends com.android.server.SystemService private final PrefetchController mPrefetchController; /** Needed to get remaining quota time. */ private final QuotaController mQuotaController; - /** Needed to get max execution time and expedited-job allowance. */ - private final TareController mTareController; /** * List of restrictions. * Note: do not add to or remove from this list at runtime except in the constructor, because we @@ -485,8 +478,7 @@ public class JobSchedulerService extends com.android.server.SystemService // (ScheduledJobStateChanged and JobStatusDumpProto). public static final int EXEMPTED_INDEX = 6; - private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener, - EconomyManagerInternal.TareStateChangeListener { + private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener { @Nullable @GuardedBy("mLock") private DeviceConfig.Properties mLastPropertiesPulled; @@ -516,16 +508,6 @@ public class JobSchedulerService extends com.android.server.SystemService public void start() { DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER, AppSchedulingModuleThread.getExecutor(), this); - final EconomyManagerInternal economyManagerInternal = - LocalServices.getService(EconomyManagerInternal.class); - economyManagerInternal - .registerTareStateChangeListener(this, JobSchedulerEconomicPolicy.POLICY_JOB); - // Load all the constants. - synchronized (mLock) { - mConstants.updateTareSettingsLocked( - economyManagerInternal.getEnabledMode( - JobSchedulerEconomicPolicy.POLICY_JOB)); - } onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER)); } @@ -645,16 +627,6 @@ public class JobSchedulerService extends com.android.server.SystemService mHandler.sendEmptyMessage(MSG_CHECK_JOB); } - @Override - public void onTareEnabledModeChanged(@EconomyManager.EnabledMode int enabledMode) { - if (mConstants.updateTareSettingsLocked(enabledMode)) { - for (int controller = 0; controller < mControllers.size(); controller++) { - final StateController sc = mControllers.get(controller); - sc.onConstantsUpdatedLocked(); - } - onControllerStateChanged(null); - } - } } @VisibleForTesting @@ -1051,12 +1023,6 @@ public class JobSchedulerService extends com.android.server.SystemService */ public int MAX_NUM_PERSISTED_JOB_WORK_ITEMS = DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS; - /** - * If true, use TARE policy for job limiting. If false, use quotas. - */ - public boolean USE_TARE_POLICY = EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER - && EconomyManager.DEFAULT_ENABLE_TARE_MODE == EconomyManager.ENABLED_MODE_ON; - public Constants() { copyTransportBatchThresholdDefaults(); } @@ -1300,16 +1266,6 @@ public class JobSchedulerService extends com.android.server.SystemService DEFAULT_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS); } - private boolean updateTareSettingsLocked(@EconomyManager.EnabledMode int enabledMode) { - boolean changed = false; - final boolean useTare = enabledMode == EconomyManager.ENABLED_MODE_ON; - if (USE_TARE_POLICY != useTare) { - USE_TARE_POLICY = useTare; - changed = true; - } - return changed; - } - void dump(IndentingPrintWriter pw) { pw.println("Settings:"); pw.increaseIndent(); @@ -1383,8 +1339,6 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS, MAX_NUM_PERSISTED_JOB_WORK_ITEMS) .println(); - pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY).println(); - pw.decreaseIndent(); } @@ -1869,9 +1823,7 @@ public class JobSchedulerService extends com.android.server.SystemService // Return failure early if expedited job quota used up. if (jobStatus.isRequestedExpeditedJob()) { - if ((mConstants.USE_TARE_POLICY && !mTareController.canScheduleEJ(jobStatus)) - || (!mConstants.USE_TARE_POLICY - && !mQuotaController.isWithinEJQuotaLocked(jobStatus))) { + if (!mQuotaController.isWithinEJQuotaLocked(jobStatus)) { Counter.logIncrementWithUid( "job_scheduler.value_cntr_w_uid_schedule_failure_ej_out_of_quota", callingUid); @@ -2726,9 +2678,6 @@ public class JobSchedulerService extends com.android.server.SystemService new QuotaController(this, backgroundJobsController, mConnectivityController); mControllers.add(mQuotaController); mControllers.add(new ComponentController(this)); - mTareController = - new TareController(this, backgroundJobsController, mConnectivityController); - mControllers.add(mTareController); startControllerTrackingAsync(); @@ -4243,10 +4192,7 @@ public class JobSchedulerService extends com.android.server.SystemService job.getTimeoutBlamePackageName(), timeoutTag) ? normalUpperLimitMs : mConstants.RUNTIME_MIN_GUARANTEE_MS; - return Math.min(upperLimitMs, - mConstants.USE_TARE_POLICY - ? mTareController.getMaxJobExecutionTimeMsLocked(job) - : mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + return Math.min(upperLimitMs, mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } } @@ -5747,11 +5693,6 @@ public class JobSchedulerService extends com.android.server.SystemService } @VisibleForTesting - protected TareController getTareController() { - return mTareController; - } - - @VisibleForTesting protected void waitOnAsyncLoadingForTesting() throws Exception { mStartControllerTrackingLatch.await(); // Ignore the job store loading for testing. diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 8ab7d2fae49f..39d50f53d2c4 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -16,8 +16,6 @@ package com.android.server.job; -import static android.app.job.JobInfo.getPriorityString; - import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.safelyScaleBytesToKBForHistogram; @@ -73,9 +71,6 @@ import com.android.modules.expresslog.Histogram; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.job.controllers.JobStatus; -import com.android.server.tare.EconomicPolicy; -import com.android.server.tare.EconomyManagerInternal; -import com.android.server.tare.JobSchedulerEconomicPolicy; import java.util.Objects; @@ -159,7 +154,6 @@ public final class JobServiceContext implements ServiceConnection { private final Object mLock; private final ActivityManagerInternal mActivityManagerInternal; private final IBatteryStats mBatteryStats; - private final EconomyManagerInternal mEconomyManagerInternal; private final JobPackageTracker mJobPackageTracker; private final PowerManager mPowerManager; private final UsageStatsManagerInternal mUsageStatsManagerInternal; @@ -324,7 +318,6 @@ public final class JobServiceContext implements ServiceConnection { mService = service; mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mBatteryStats = batteryStats; - mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class); mJobPackageTracker = tracker; mCallbackHandler = new JobServiceHandler(looper); mJobConcurrencyManager = concurrencyManager; @@ -414,11 +407,6 @@ public final class JobServiceContext implements ServiceConnection { mWakeLock.setReferenceCounted(false); mWakeLock.acquire(); - // Note the start when we try to bind so that the app is charged for some processing - // even if binding fails. - mEconomyManagerInternal.noteInstantaneousEvent( - job.getSourceUserId(), job.getSourcePackageName(), - getStartActionId(job), String.valueOf(job.getJobId())); mVerb = VERB_BINDING; scheduleOpTimeOutLocked(); // Use FLAG_FROM_BACKGROUND to avoid resetting the bad-app tracking. @@ -619,25 +607,6 @@ public final class JobServiceContext implements ServiceConnection { return result == PermissionChecker.PERMISSION_GRANTED; } - @EconomicPolicy.AppAction - private static int getStartActionId(@NonNull JobStatus job) { - switch (job.getEffectivePriority()) { - case JobInfo.PRIORITY_MAX: - return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START; - case JobInfo.PRIORITY_HIGH: - return JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START; - case JobInfo.PRIORITY_LOW: - return JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START; - case JobInfo.PRIORITY_MIN: - return JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START; - default: - Slog.wtf(TAG, "Unknown priority: " + getPriorityString(job.getEffectivePriority())); - // Intentional fallthrough - case JobInfo.PRIORITY_DEFAULT: - return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START; - } - } - /** * Used externally to query the running job. Will return null if there is no job running. */ @@ -1649,12 +1618,6 @@ public final class JobServiceContext implements ServiceConnection { } catch (RemoteException e) { // Whatever. } - if (loggingStopReason == JobParameters.STOP_REASON_TIMEOUT) { - mEconomyManagerInternal.noteInstantaneousEvent( - mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(), - JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, - String.valueOf(mRunningJob.getJobId())); - } mNotificationCoordinator.removeNotificationAssociation(this, reschedulingStopReason, completedJob); if (mWakeLock != null) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 3219f7e5ce20..7a3961036570 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -1001,8 +1001,7 @@ public final class ConnectivityController extends RestrictingController implemen NetworkCapabilities capabilities, Constants constants) { // A restricted job that's out of quota MUST use an unmetered network. if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX - && (!jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA) - || !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_TARE_WEALTH))) { + && (!jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA))) { final NetworkCapabilities.Builder builder = copyCapabilities(jobStatus.getJob().getRequiredNetwork()); builder.addCapability(NET_CAPABILITY_NOT_METERED); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index d643768185e3..998554324da1 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -116,7 +116,6 @@ public final class JobStatus { public static final int CONSTRAINT_TIMING_DELAY = 1 << 31; public static final int CONSTRAINT_DEADLINE = 1 << 30; public static final int CONSTRAINT_CONNECTIVITY = 1 << 28; - static final int CONSTRAINT_TARE_WEALTH = 1 << 27; // Implicit constraint public static final int CONSTRAINT_CONTENT_TRIGGER = 1 << 26; static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint @@ -128,7 +127,6 @@ public final class JobStatus { | CONSTRAINT_BACKGROUND_NOT_RESTRICTED | CONSTRAINT_DEVICE_NOT_DOZING | CONSTRAINT_FLEXIBLE - | CONSTRAINT_TARE_WEALTH | CONSTRAINT_WITHIN_QUOTA; // The following set of dynamic constraints are for specific use cases (as explained in their @@ -196,7 +194,6 @@ public final class JobStatus { private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER | CONSTRAINT_DEADLINE | CONSTRAINT_PREFETCH - | CONSTRAINT_TARE_WEALTH | CONSTRAINT_TIMING_DELAY | CONSTRAINT_WITHIN_QUOTA; @@ -532,10 +529,6 @@ public final class JobStatus { * Whether or not this job is approved to be treated as expedited per quota policy. */ private boolean mExpeditedQuotaApproved; - /** - * Whether or not this job is approved to be treated as expedited per TARE policy. - */ - private boolean mExpeditedTareApproved; /** * Summary describing this job. Lazily created in {@link #getUserVisibleJobSummary()} @@ -568,9 +561,6 @@ public final class JobStatus { /** The job is within its quota based on its standby bucket. */ private boolean mReadyWithinQuota; - /** The job has enough credits to run based on TARE. */ - private boolean mReadyTareWealth; - /** The job's dynamic requirements have been satisfied. */ private boolean mReadyDynamicSatisfied; @@ -1719,7 +1709,7 @@ public final class JobStatus { * treated as an expedited job. */ public boolean shouldTreatAsExpeditedJob() { - return mExpeditedQuotaApproved && mExpeditedTareApproved && isRequestedExpeditedJob(); + return mExpeditedQuotaApproved && isRequestedExpeditedJob(); } /** @@ -1868,16 +1858,6 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setTareWealthConstraintSatisfied(final long nowElapsed, boolean state) { - if (setConstraintSatisfied(CONSTRAINT_TARE_WEALTH, nowElapsed, state)) { - // The constraint was changed. Update the ready flag. - mReadyTareWealth = state; - return true; - } - return false; - } - - /** @return true if the constraint was changed, false otherwise. */ boolean setFlexibilityConstraintSatisfied(final long nowElapsed, boolean state) { return setConstraintSatisfied(CONSTRAINT_FLEXIBLE, nowElapsed, state); } @@ -1904,28 +1884,6 @@ public final class JobStatus { return true; } - /** - * Sets whether or not this job is approved to be treated as an expedited job based on TARE - * policy. - * - * @return true if the approval bit was changed, false otherwise. - */ - boolean setExpeditedJobTareApproved(final long nowElapsed, boolean state) { - if (mExpeditedTareApproved == state) { - return false; - } - final boolean wasReady = !state && isReady(); - mExpeditedTareApproved = state; - updateExpeditedDependencies(); - final boolean isReady = isReady(); - if (wasReady && !isReady) { - mReasonReadyToUnready = JobParameters.STOP_REASON_QUOTA; - } else if (!wasReady && isReady) { - mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED; - } - return true; - } - private void updateExpeditedDependencies() { // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag. // Making it also track requested-expedited jobs would add unnecessary hops since the @@ -2041,7 +1999,6 @@ public final class JobStatus { case CONSTRAINT_PREFETCH: return JobParameters.STOP_REASON_ESTIMATED_APP_LAUNCH_TIME_CHANGED; - case CONSTRAINT_TARE_WEALTH: case CONSTRAINT_WITHIN_QUOTA: return JobParameters.STOP_REASON_QUOTA; @@ -2130,9 +2087,6 @@ public final class JobStatus { if ((CONSTRAINT_STORAGE_NOT_LOW & unsatisfiedConstraints) != 0) { return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_STORAGE_NOT_LOW; } - if ((CONSTRAINT_TARE_WEALTH & unsatisfiedConstraints) != 0) { - return JobScheduler.PENDING_JOB_REASON_QUOTA; - } if ((CONSTRAINT_TIMING_DELAY & unsatisfiedConstraints) != 0) { return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY; } @@ -2207,11 +2161,6 @@ public final class JobStatus { Slog.wtf(TAG, "Tried to set quota as a dynamic constraint"); constraints &= ~CONSTRAINT_WITHIN_QUOTA; } - if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) { - // Quota should never be used as a dynamic constraint. - Slog.wtf(TAG, "Tried to set TARE as a dynamic constraint"); - constraints &= ~CONSTRAINT_TARE_WEALTH; - } // Connectivity and content trigger are special since they're only valid to add if the // job has requested network or specific content URIs. Adding these constraints to jobs @@ -2280,10 +2229,6 @@ public final class JobStatus { oldValue = mReadyNotDozing; mReadyNotDozing = value; break; - case CONSTRAINT_TARE_WEALTH: - oldValue = mReadyTareWealth; - mReadyTareWealth = value; - break; case CONSTRAINT_WITHIN_QUOTA: oldValue = mReadyWithinQuota; mReadyWithinQuota = value; @@ -2321,9 +2266,6 @@ public final class JobStatus { case CONSTRAINT_DEVICE_NOT_DOZING: mReadyNotDozing = oldValue; break; - case CONSTRAINT_TARE_WEALTH: - mReadyTareWealth = oldValue; - break; case CONSTRAINT_WITHIN_QUOTA: mReadyWithinQuota = oldValue; break; @@ -2341,7 +2283,7 @@ public final class JobStatus { // sessions (exempt from dynamic restrictions), we need the additional check to ensure // that NEVER jobs don't run. // TODO: cleanup quota and standby bucket management so we don't need the additional checks - if (((!mReadyWithinQuota || !mReadyTareWealth) + if (((!mReadyWithinQuota) && !mReadyDynamicSatisfied && !shouldTreatAsExpeditedJob()) || getEffectiveStandbyBucket() == NEVER_INDEX) { return false; @@ -2577,9 +2519,6 @@ public final class JobStatus { if ((constraints & CONSTRAINT_PREFETCH) != 0) { pw.print(" PREFETCH"); } - if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) { - pw.print(" TARE_WEALTH"); - } if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) { pw.print(" WITHIN_QUOTA"); } @@ -2615,8 +2554,6 @@ public final class JobStatus { return JobServerProtoEnums.CONSTRAINT_PREFETCH; case CONSTRAINT_STORAGE_NOT_LOW: return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW; - case CONSTRAINT_TARE_WEALTH: - return JobServerProtoEnums.CONSTRAINT_TARE_WEALTH; case CONSTRAINT_TIMING_DELAY: return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY; case CONSTRAINT_WITHIN_QUOTA: @@ -2869,7 +2806,7 @@ public final class JobStatus { pw.println(); pw.print("Unsatisfied constraints:"); dumpConstraints(pw, - ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA | CONSTRAINT_TARE_WEALTH) + ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints)); pw.println(); if (hasFlexibilityConstraint()) { @@ -2937,8 +2874,6 @@ public final class JobStatus { if ((getFlags() & JobInfo.FLAG_EXPEDITED) != 0) { pw.print("expeditedQuotaApproved: "); pw.print(mExpeditedQuotaApproved); - pw.print(" expeditedTareApproved: "); - pw.print(mExpeditedTareApproved); pw.print(" (started as EJ: "); pw.print(startedAsExpeditedJob); pw.println(")"); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 8ddbf691359f..3c9648b20003 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -328,9 +328,6 @@ public final class QuotaController extends StateController { private final BackgroundJobsController mBackgroundJobsController; private final ConnectivityController mConnectivityController; - @GuardedBy("mLock") - private boolean mIsEnabled; - /** How much time each app will have to run jobs within their standby bucket window. */ private final long[] mAllowedTimePerPeriodMs = new long[]{ QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, @@ -546,7 +543,6 @@ public final class QuotaController extends StateController { mQcConstants = new QcConstants(); mBackgroundJobsController = backgroundJobsController; mConnectivityController = connectivityController; - mIsEnabled = !mConstants.USE_TARE_POLICY; mInQuotaAlarmQueue = new InQuotaAlarmQueue(mContext, AppSchedulingModuleThread.get().getLooper()); @@ -835,9 +831,6 @@ public final class QuotaController extends StateController { /** @return true if the job is within expedited job quota. */ @GuardedBy("mLock") public boolean isWithinEJQuotaLocked(@NonNull final JobStatus jobStatus) { - if (!mIsEnabled) { - return true; - } if (isQuotaFreeLocked(jobStatus.getEffectiveStandbyBucket())) { return true; } @@ -882,9 +875,6 @@ public final class QuotaController extends StateController { @VisibleForTesting boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) { - if (!mIsEnabled) { - return true; - } final int standbyBucket = jobStatus.getEffectiveStandbyBucket(); // A job is within quota if one of the following is true: // 1. it was started while the app was in the TOP state @@ -912,9 +902,6 @@ public final class QuotaController extends StateController { @GuardedBy("mLock") boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName, final int standbyBucket) { - if (!mIsEnabled) { - return true; - } if (standbyBucket == NEVER_INDEX) return false; if (isQuotaFreeLocked(standbyBucket)) return true; @@ -2948,8 +2935,7 @@ public final class QuotaController extends StateController { @Override public void onConstantsUpdatedLocked() { - if (mQcConstants.mShouldReevaluateConstraints || mIsEnabled == mConstants.USE_TARE_POLICY) { - mIsEnabled = !mConstants.USE_TARE_POLICY; + if (mQcConstants.mShouldReevaluateConstraints) { // Update job bookkeeping out of band. AppSchedulingModuleThread.getHandler().post(() -> { synchronized (mLock) { @@ -4454,7 +4440,6 @@ public final class QuotaController extends StateController { @Override public void dumpControllerStateLocked(final IndentingPrintWriter pw, final Predicate<JobStatus> predicate) { - pw.println("Is enabled: " + mIsEnabled); pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis()); pw.println(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java deleted file mode 100644 index 7408088b8efc..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java +++ /dev/null @@ -1,764 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.job.controllers; - -import static android.app.job.JobInfo.getPriorityString; - -import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; - -import android.annotation.NonNull; -import android.app.job.JobInfo; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.IndentingPrintWriter; -import android.util.Log; -import android.util.Slog; -import android.util.SparseArrayMap; - -import com.android.internal.annotations.GuardedBy; -import com.android.server.AppSchedulingModuleThread; -import com.android.server.LocalServices; -import com.android.server.job.JobSchedulerService; -import com.android.server.tare.EconomicPolicy; -import com.android.server.tare.EconomyManagerInternal; -import com.android.server.tare.EconomyManagerInternal.ActionBill; -import com.android.server.tare.JobSchedulerEconomicPolicy; - -import java.util.List; -import java.util.function.Predicate; - -/** - * Controller that interfaces with Tare ({@link EconomyManagerInternal} and manages each job's - * ability to run per TARE policies. - * - * @see JobSchedulerEconomicPolicy - */ -public class TareController extends StateController { - private static final String TAG = "JobScheduler.TARE"; - private static final boolean DEBUG = JobSchedulerService.DEBUG - || Log.isLoggable(TAG, Log.DEBUG); - - /** - * Bill to use while we're waiting to start a min priority job. If a job isn't running yet, - * don't consider it eligible to run unless it can pay for a job start and at least some - * period of execution time. We don't want min priority jobs to use up all available credits, - * so we make sure to only run them while there are enough credits to run higher priority jobs. - */ - private static final ActionBill BILL_JOB_START_MIN = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START, 1, 0), - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 120_000L) - )); - - /** - * Bill to use when a min priority job is currently running. We don't want min priority jobs - * to use up remaining credits, so we make sure to only run them while there are enough - * credits to run higher priority jobs. We stop the job when the app's credits get too low. - */ - private static final ActionBill BILL_JOB_RUNNING_MIN = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 60_000L) - )); - - /** - * Bill to use while we're waiting to start a low priority job. If a job isn't running yet, - * don't consider it eligible to run unless it can pay for a job start and at least some - * period of execution time. We don't want low priority jobs to use up all available credits, - * so we make sure to only run them while there are enough credits to run higher priority jobs. - */ - private static final ActionBill BILL_JOB_START_LOW = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START, 1, 0), - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 60_000L) - )); - - /** - * Bill to use when a low priority job is currently running. We don't want low priority jobs - * to use up all available credits, so we make sure to only run them while there are enough - * credits to run higher priority jobs. We stop the job when the app's credits get too low. - */ - private static final ActionBill BILL_JOB_RUNNING_LOW = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 30_000L) - )); - - /** - * Bill to use while we're waiting to start a job. If a job isn't running yet, don't consider it - * eligible to run unless it can pay for a job start and at least some period of execution time. - */ - private static final ActionBill BILL_JOB_START_DEFAULT = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START, 1, 0), - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 30_000L) - )); - - /** - * Bill to use when a default priority job is currently running. We want to track and make - * sure the app can continue to pay for 1 more second of execution time. We stop the job when - * the app can no longer pay for that time. - */ - private static final ActionBill BILL_JOB_RUNNING_DEFAULT = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 1_000L) - )); - - /** - * Bill to use while we're waiting to start a job. If a job isn't running yet, don't consider it - * eligible to run unless it can pay for a job start and at least some period of execution time. - */ - private static final ActionBill BILL_JOB_START_HIGH = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 1, 0), - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, 0, 30_000L) - )); - - /** - * Bill to use when a high priority job is currently running. We want to track and make sure - * the app can continue to pay for 1 more second of execution time. We stop the job when the - * app can no longer pay for that time. - */ - private static final ActionBill BILL_JOB_RUNNING_HIGH = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, 0, 1_000L) - )); - - - /** - * Bill to use while we're waiting to start a max priority job. This should only be used for - * requested-EJs that aren't allowed to run as EJs. If a job isn't running yet, don't consider - * it eligible to run unless it can pay for a job start and at least some period of execution - * time. - */ - private static final ActionBill BILL_JOB_START_MAX = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0), - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 30_000L) - )); - - /** - * Bill to use when a max priority job is currently running. This should only be used for - * requested-EJs that aren't allowed to run as EJs. We want to track and make sure - * the app can continue to pay for 1 more second of execution time. We stop the job when the - * app can no longer pay for that time. - */ - private static final ActionBill BILL_JOB_RUNNING_MAX = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1_000L) - )); - - /** - * Bill to use while we're waiting to start a job. If a job isn't running yet, don't consider it - * eligible to run unless it can pay for a job start and at least some period of execution time. - */ - private static final ActionBill BILL_JOB_START_MAX_EXPEDITED = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0), - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 30_000L) - )); - - /** - * Bill to use when a max priority EJ is currently running (as an EJ). We want to track and - * make sure the app can continue to pay for 1 more second of execution time. We stop the job - * when the app can no longer pay for that time. - */ - private static final ActionBill BILL_JOB_RUNNING_MAX_EXPEDITED = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1_000L) - )); - - /** - * Bill to use while we're waiting to start a job. If a job isn't running yet, don't consider it - * eligible to run unless it can pay for a job start and at least some period of execution time. - */ - private static final ActionBill BILL_JOB_START_HIGH_EXPEDITED = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 1, 0), - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, 0, 30_000L) - )); - - /** - * Bill to use when a high priority EJ is currently running (as an EJ). We want to track and - * make sure the app can continue to pay for 1 more second of execution time. We stop the job - * when the app can no longer pay for that time. - */ - private static final ActionBill BILL_JOB_RUNNING_HIGH_EXPEDITED = - new ActionBill(List.of( - new EconomyManagerInternal.AnticipatedAction( - JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, 0, 1_000L) - )); - - private final EconomyManagerInternal mEconomyManagerInternal; - - private final BackgroundJobsController mBackgroundJobsController; - private final ConnectivityController mConnectivityController; - - /** - * Local cache of the ability of each userId-pkg to afford the various bills we're tracking for - * them. - */ - @GuardedBy("mLock") - private final SparseArrayMap<String, ArrayMap<ActionBill, Boolean>> mAffordabilityCache = - new SparseArrayMap<>(); - - /** - * List of all tracked jobs. Out SparseArrayMap is userId-sourcePkg. The inner mapping is the - * anticipated actions and all the jobs that are applicable to them. - */ - @GuardedBy("mLock") - private final SparseArrayMap<String, ArrayMap<ActionBill, ArraySet<JobStatus>>> - mRegisteredBillsAndJobs = new SparseArrayMap<>(); - - private final EconomyManagerInternal.AffordabilityChangeListener mAffordabilityChangeListener = - (userId, pkgName, bill, canAfford) -> { - final long nowElapsed = sElapsedRealtimeClock.millis(); - if (DEBUG) { - Slog.d(TAG, - userId + ":" + pkgName + " affordability for " + getBillName(bill) - + " changed to " + canAfford); - } - synchronized (mLock) { - ArrayMap<ActionBill, Boolean> actionAffordability = - mAffordabilityCache.get(userId, pkgName); - if (actionAffordability == null) { - actionAffordability = new ArrayMap<>(); - mAffordabilityCache.add(userId, pkgName, actionAffordability); - } - actionAffordability.put(bill, canAfford); - - final ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap = - mRegisteredBillsAndJobs.get(userId, pkgName); - if (billToJobMap != null) { - final ArraySet<JobStatus> jobs = billToJobMap.get(bill); - if (jobs != null) { - final ArraySet<JobStatus> changedJobs = new ArraySet<>(); - for (int i = 0; i < jobs.size(); ++i) { - final JobStatus job = jobs.valueAt(i); - // Use hasEnoughWealth if canAfford is false in case the job has - // other bills it can depend on (eg. EJs being demoted to - // regular jobs). - if (job.setTareWealthConstraintSatisfied(nowElapsed, - canAfford || hasEnoughWealthLocked(job))) { - changedJobs.add(job); - } - if (job.isRequestedExpeditedJob() - && setExpeditedTareApproved(job, nowElapsed, - canAffordExpeditedBillLocked(job))) { - changedJobs.add(job); - } - } - if (changedJobs.size() > 0) { - mStateChangedListener.onControllerStateChanged(changedJobs); - } - } - } - } - }; - - /** - * List of jobs that started while the UID was in the TOP state. There will usually be no more - * than {@value JobConcurrencyManager#MAX_STANDARD_JOB_CONCURRENCY} running at once, so an - * ArraySet is fine. - */ - @GuardedBy("mLock") - private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>(); - - @GuardedBy("mLock") - private boolean mIsEnabled; - - public TareController(JobSchedulerService service, - @NonNull BackgroundJobsController backgroundJobsController, - @NonNull ConnectivityController connectivityController) { - super(service); - mBackgroundJobsController = backgroundJobsController; - mConnectivityController = connectivityController; - mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class); - mIsEnabled = mConstants.USE_TARE_POLICY; - } - - @Override - @GuardedBy("mLock") - public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { - final long nowElapsed = sElapsedRealtimeClock.millis(); - if (jobStatus.shouldTreatAsUserInitiatedJob()) { - // User-initiated jobs should always be allowed to run. - jobStatus.setTareWealthConstraintSatisfied(nowElapsed, true); - return; - } - jobStatus.setTareWealthConstraintSatisfied(nowElapsed, hasEnoughWealthLocked(jobStatus)); - setExpeditedTareApproved(jobStatus, nowElapsed, - jobStatus.isRequestedExpeditedJob() && canAffordExpeditedBillLocked(jobStatus)); - - final ArraySet<ActionBill> bills = getPossibleStartBills(jobStatus); - for (int i = 0; i < bills.size(); ++i) { - addJobToBillList(jobStatus, bills.valueAt(i)); - } - } - - @Override - @GuardedBy("mLock") - public void prepareForExecutionLocked(JobStatus jobStatus) { - if (jobStatus.shouldTreatAsUserInitiatedJob()) { - // TODO(202954395): consider noting execution with the EconomyManager even though it - // won't affect this job - return; - } - final int userId = jobStatus.getSourceUserId(); - final String pkgName = jobStatus.getSourcePackageName(); - ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap = - mRegisteredBillsAndJobs.get(userId, pkgName); - if (billToJobMap == null) { - Slog.e(TAG, "Job is being prepared but doesn't have a pre-existing billToJobMap"); - } else { - for (int i = 0; i < billToJobMap.size(); ++i) { - removeJobFromBillList(jobStatus, billToJobMap.keyAt(i)); - } - } - - final int uid = jobStatus.getSourceUid(); - if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) { - if (DEBUG) { - Slog.d(TAG, jobStatus.toShortString() + " is top started job"); - } - mTopStartedJobs.add(jobStatus); - // Top jobs won't count towards quota so there's no need to involve the EconomyManager. - } else { - addJobToBillList(jobStatus, getRunningBill(jobStatus)); - mEconomyManagerInternal.noteOngoingEventStarted(userId, pkgName, - getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId())); - } - } - - @Override - @GuardedBy("mLock") - public void unprepareFromExecutionLocked(JobStatus jobStatus) { - if (jobStatus.shouldTreatAsUserInitiatedJob()) { - return; - } - final int userId = jobStatus.getSourceUserId(); - final String pkgName = jobStatus.getSourcePackageName(); - // If this method is called, then jobStatus.madeActive was never updated, so don't use it - // to determine if the EconomyManager was notified. - if (!mTopStartedJobs.remove(jobStatus)) { - // If the job was started while the app was top, then the EconomyManager wasn't notified - // of the job start. - mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName, - getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId())); - } - - final ArraySet<ActionBill> bills = getPossibleStartBills(jobStatus); - ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap = - mRegisteredBillsAndJobs.get(userId, pkgName); - if (billToJobMap == null) { - Slog.e(TAG, "Job was just unprepared but didn't have a pre-existing billToJobMap"); - } else { - for (int i = 0; i < billToJobMap.size(); ++i) { - removeJobFromBillList(jobStatus, billToJobMap.keyAt(i)); - } - } - for (int i = 0; i < bills.size(); ++i) { - addJobToBillList(jobStatus, bills.valueAt(i)); - } - } - - @Override - @GuardedBy("mLock") - public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob) { - if (jobStatus.shouldTreatAsUserInitiatedJob()) { - return; - } - final int userId = jobStatus.getSourceUserId(); - final String pkgName = jobStatus.getSourcePackageName(); - if (!mTopStartedJobs.remove(jobStatus) && jobStatus.madeActive > 0) { - // Only note the job stop if we previously told the EconomyManager that the job started. - // If the job was started while the app was top, then the EconomyManager wasn't notified - // of the job start. - mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName, - getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId())); - } - ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap = - mRegisteredBillsAndJobs.get(userId, pkgName); - if (billToJobMap != null) { - for (int i = 0; i < billToJobMap.size(); ++i) { - removeJobFromBillList(jobStatus, billToJobMap.keyAt(i)); - } - } - } - - @Override - @GuardedBy("mLock") - public void onConstantsUpdatedLocked() { - if (mIsEnabled != mConstants.USE_TARE_POLICY) { - mIsEnabled = mConstants.USE_TARE_POLICY; - // Update job bookkeeping out of band. - AppSchedulingModuleThread.getHandler().post(() -> { - synchronized (mLock) { - final long nowElapsed = sElapsedRealtimeClock.millis(); - mService.getJobStore().forEachJob((jobStatus) -> { - if (!mIsEnabled) { - jobStatus.setTareWealthConstraintSatisfied(nowElapsed, true); - setExpeditedTareApproved(jobStatus, nowElapsed, true); - } else { - jobStatus.setTareWealthConstraintSatisfied( - nowElapsed, hasEnoughWealthLocked(jobStatus)); - setExpeditedTareApproved(jobStatus, nowElapsed, - jobStatus.isRequestedExpeditedJob() - && canAffordExpeditedBillLocked(jobStatus)); - } - }); - } - }); - } - } - - @GuardedBy("mLock") - public boolean canScheduleEJ(@NonNull JobStatus jobStatus) { - if (!mIsEnabled) { - return true; - } - if (jobStatus.getEffectivePriority() == JobInfo.PRIORITY_MAX) { - return canAffordBillLocked(jobStatus, BILL_JOB_START_MAX_EXPEDITED); - } - return canAffordBillLocked(jobStatus, BILL_JOB_START_HIGH_EXPEDITED); - } - - /** @return true if the job was started while the app was in the TOP state. */ - @GuardedBy("mLock") - private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) { - return mTopStartedJobs.contains(jobStatus); - } - - @GuardedBy("mLock") - public long getMaxJobExecutionTimeMsLocked(@NonNull JobStatus jobStatus) { - if (!mIsEnabled) { - return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; - } - return mEconomyManagerInternal.getMaxDurationMs( - jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), - getRunningBill(jobStatus)); - } - - @GuardedBy("mLock") - private void addJobToBillList(@NonNull JobStatus jobStatus, @NonNull ActionBill bill) { - final int userId = jobStatus.getSourceUserId(); - final String pkgName = jobStatus.getSourcePackageName(); - ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap = - mRegisteredBillsAndJobs.get(userId, pkgName); - if (billToJobMap == null) { - billToJobMap = new ArrayMap<>(); - mRegisteredBillsAndJobs.add(userId, pkgName, billToJobMap); - } - ArraySet<JobStatus> jobs = billToJobMap.get(bill); - if (jobs == null) { - jobs = new ArraySet<>(); - billToJobMap.put(bill, jobs); - } - if (jobs.add(jobStatus)) { - mEconomyManagerInternal.registerAffordabilityChangeListener(userId, pkgName, - mAffordabilityChangeListener, bill); - } - } - - @GuardedBy("mLock") - private void removeJobFromBillList(@NonNull JobStatus jobStatus, @NonNull ActionBill bill) { - final int userId = jobStatus.getSourceUserId(); - final String pkgName = jobStatus.getSourcePackageName(); - final ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap = - mRegisteredBillsAndJobs.get(userId, pkgName); - if (billToJobMap != null) { - final ArraySet<JobStatus> jobs = billToJobMap.get(bill); - if (jobs == null || (jobs.remove(jobStatus) && jobs.size() == 0)) { - mEconomyManagerInternal.unregisterAffordabilityChangeListener( - userId, pkgName, mAffordabilityChangeListener, bill); - // Remove the cached value so we don't accidentally use it when the app - // schedules a new job. - final ArrayMap<ActionBill, Boolean> actionAffordability = - mAffordabilityCache.get(userId, pkgName); - if (actionAffordability != null) { - actionAffordability.remove(bill); - } - } - } - } - - @NonNull - private ArraySet<ActionBill> getPossibleStartBills(JobStatus jobStatus) { - // TODO: factor in network cost when available - final ArraySet<ActionBill> bills = new ArraySet<>(); - if (jobStatus.isRequestedExpeditedJob()) { - if (jobStatus.getEffectivePriority() == JobInfo.PRIORITY_MAX) { - bills.add(BILL_JOB_START_MAX_EXPEDITED); - } else { - bills.add(BILL_JOB_START_HIGH_EXPEDITED); - } - } - switch (jobStatus.getEffectivePriority()) { - case JobInfo.PRIORITY_MAX: - bills.add(BILL_JOB_START_MAX); - break; - case JobInfo.PRIORITY_HIGH: - bills.add(BILL_JOB_START_HIGH); - break; - case JobInfo.PRIORITY_DEFAULT: - bills.add(BILL_JOB_START_DEFAULT); - break; - case JobInfo.PRIORITY_LOW: - bills.add(BILL_JOB_START_LOW); - break; - case JobInfo.PRIORITY_MIN: - bills.add(BILL_JOB_START_MIN); - break; - default: - Slog.wtf(TAG, "Unexpected priority: " - + JobInfo.getPriorityString(jobStatus.getEffectivePriority())); - break; - } - return bills; - } - - @NonNull - private ActionBill getRunningBill(JobStatus jobStatus) { - // TODO: factor in network cost when available - if (jobStatus.shouldTreatAsExpeditedJob() || jobStatus.startedAsExpeditedJob) { - if (jobStatus.getEffectivePriority() == JobInfo.PRIORITY_MAX) { - return BILL_JOB_RUNNING_MAX_EXPEDITED; - } else { - return BILL_JOB_RUNNING_HIGH_EXPEDITED; - } - } - switch (jobStatus.getEffectivePriority()) { - case JobInfo.PRIORITY_MAX: - return BILL_JOB_RUNNING_MAX; - case JobInfo.PRIORITY_HIGH: - return BILL_JOB_RUNNING_HIGH; - case JobInfo.PRIORITY_LOW: - return BILL_JOB_RUNNING_LOW; - case JobInfo.PRIORITY_MIN: - return BILL_JOB_RUNNING_MIN; - default: - Slog.wtf(TAG, "Got unexpected priority: " + jobStatus.getEffectivePriority()); - // Intentional fallthrough - case JobInfo.PRIORITY_DEFAULT: - return BILL_JOB_RUNNING_DEFAULT; - } - } - - @EconomicPolicy.AppAction - private static int getRunningActionId(@NonNull JobStatus job) { - switch (job.getEffectivePriority()) { - case JobInfo.PRIORITY_MAX: - return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING; - case JobInfo.PRIORITY_HIGH: - return JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING; - case JobInfo.PRIORITY_LOW: - return JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING; - case JobInfo.PRIORITY_MIN: - return JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING; - default: - Slog.wtf(TAG, "Unknown priority: " + getPriorityString(job.getEffectivePriority())); - // Intentional fallthrough - case JobInfo.PRIORITY_DEFAULT: - return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING; - } - } - - @GuardedBy("mLock") - private boolean canAffordBillLocked(@NonNull JobStatus jobStatus, @NonNull ActionBill bill) { - if (!mIsEnabled) { - return true; - } - if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP - || isTopStartedJobLocked(jobStatus)) { - // Jobs for the top app should always be allowed to run, and any jobs started while - // the app is on top shouldn't consume any credits. - return true; - } - final int userId = jobStatus.getSourceUserId(); - final String pkgName = jobStatus.getSourcePackageName(); - ArrayMap<ActionBill, Boolean> actionAffordability = - mAffordabilityCache.get(userId, pkgName); - if (actionAffordability == null) { - actionAffordability = new ArrayMap<>(); - mAffordabilityCache.add(userId, pkgName, actionAffordability); - } - - if (actionAffordability.containsKey(bill)) { - return actionAffordability.get(bill); - } - - final boolean canAfford = mEconomyManagerInternal.canPayFor(userId, pkgName, bill); - actionAffordability.put(bill, canAfford); - return canAfford; - } - - @GuardedBy("mLock") - private boolean canAffordExpeditedBillLocked(@NonNull JobStatus jobStatus) { - if (!mIsEnabled) { - return true; - } - if (!jobStatus.isRequestedExpeditedJob()) { - return false; - } - if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP - || isTopStartedJobLocked(jobStatus)) { - // Jobs for the top app should always be allowed to run, and any jobs started while - // the app is on top shouldn't consume any credits. - return true; - } - if (mService.isCurrentlyRunningLocked(jobStatus)) { - return canAffordBillLocked(jobStatus, getRunningBill(jobStatus)); - } - - if (jobStatus.getEffectivePriority() == JobInfo.PRIORITY_MAX) { - return canAffordBillLocked(jobStatus, BILL_JOB_START_MAX_EXPEDITED); - } - return canAffordBillLocked(jobStatus, BILL_JOB_START_HIGH_EXPEDITED); - } - - @GuardedBy("mLock") - private boolean hasEnoughWealthLocked(@NonNull JobStatus jobStatus) { - if (!mIsEnabled) { - return true; - } - if (jobStatus.shouldTreatAsUserInitiatedJob()) { - // Always allow user-initiated jobs. - return true; - } - if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP - || isTopStartedJobLocked(jobStatus)) { - // Jobs for the top app should always be allowed to run, and any jobs started while - // the app is on top shouldn't consume any credits. - return true; - } - if (mService.isCurrentlyRunningLocked(jobStatus)) { - return canAffordBillLocked(jobStatus, getRunningBill(jobStatus)); - } - - final ArraySet<ActionBill> bills = getPossibleStartBills(jobStatus); - for (int i = 0; i < bills.size(); ++i) { - ActionBill bill = bills.valueAt(i); - if (canAffordBillLocked(jobStatus, bill)) { - return true; - } - } - return false; - } - - /** - * If the satisfaction changes, this will tell connectivity & background jobs controller to - * also re-evaluate their state. - */ - private boolean setExpeditedTareApproved(@NonNull JobStatus jobStatus, long nowElapsed, - boolean isApproved) { - if (jobStatus.setExpeditedJobTareApproved(nowElapsed, isApproved)) { - mBackgroundJobsController.evaluateStateLocked(jobStatus); - mConnectivityController.evaluateStateLocked(jobStatus); - if (isApproved && jobStatus.isReady()) { - mStateChangedListener.onRunJobNow(jobStatus); - } - return true; - } - return false; - } - - @NonNull - private String getBillName(@NonNull ActionBill bill) { - if (bill.equals(BILL_JOB_START_MAX_EXPEDITED)) { - return "EJ_MAX_START_BILL"; - } - if (bill.equals(BILL_JOB_RUNNING_MAX_EXPEDITED)) { - return "EJ_MAX_RUNNING_BILL"; - } - if (bill.equals(BILL_JOB_START_HIGH_EXPEDITED)) { - return "EJ_HIGH_START_BILL"; - } - if (bill.equals(BILL_JOB_RUNNING_HIGH_EXPEDITED)) { - return "EJ_HIGH_RUNNING_BILL"; - } - if (bill.equals(BILL_JOB_START_HIGH)) { - return "HIGH_START_BILL"; - } - if (bill.equals(BILL_JOB_RUNNING_HIGH)) { - return "HIGH_RUNNING_BILL"; - } - if (bill.equals(BILL_JOB_START_DEFAULT)) { - return "DEFAULT_START_BILL"; - } - if (bill.equals(BILL_JOB_RUNNING_DEFAULT)) { - return "DEFAULT_RUNNING_BILL"; - } - if (bill.equals(BILL_JOB_START_LOW)) { - return "LOW_START_BILL"; - } - if (bill.equals(BILL_JOB_RUNNING_LOW)) { - return "LOW_RUNNING_BILL"; - } - if (bill.equals(BILL_JOB_START_MIN)) { - return "MIN_START_BILL"; - } - if (bill.equals(BILL_JOB_RUNNING_MIN)) { - return "MIN_RUNNING_BILL"; - } - return "UNKNOWN_BILL (" + bill + ")"; - } - - @Override - public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { - pw.print("Is enabled: "); - pw.println(mIsEnabled); - - pw.println("Affordability cache:"); - pw.increaseIndent(); - mAffordabilityCache.forEach((userId, pkgName, billMap) -> { - final int numBills = billMap.size(); - if (numBills > 0) { - pw.print(userId); - pw.print(":"); - pw.print(pkgName); - pw.println(":"); - - pw.increaseIndent(); - for (int i = 0; i < numBills; ++i) { - pw.print(getBillName(billMap.keyAt(i))); - pw.print(": "); - pw.println(billMap.valueAt(i)); - } - pw.decreaseIndent(); - } - }); - pw.decreaseIndent(); - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java deleted file mode 100644 index 5c60562398ee..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java +++ /dev/null @@ -1,1362 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static android.app.tare.EconomyManager.ENABLED_MODE_OFF; -import static android.text.format.DateUtils.DAY_IN_MILLIS; - -import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME; -import static com.android.server.tare.EconomicPolicy.REGULATION_BG_RESTRICTED; -import static com.android.server.tare.EconomicPolicy.REGULATION_BG_UNRESTRICTED; -import static com.android.server.tare.EconomicPolicy.REGULATION_BIRTHRIGHT; -import static com.android.server.tare.EconomicPolicy.REGULATION_DEMOTION; -import static com.android.server.tare.EconomicPolicy.REGULATION_PROMOTION; -import static com.android.server.tare.EconomicPolicy.REGULATION_WEALTH_RECLAMATION; -import static com.android.server.tare.EconomicPolicy.TYPE_ACTION; -import static com.android.server.tare.EconomicPolicy.TYPE_REWARD; -import static com.android.server.tare.EconomicPolicy.eventToString; -import static com.android.server.tare.EconomicPolicy.getEventType; -import static com.android.server.tare.TareUtils.appToString; -import static com.android.server.tare.TareUtils.cakeToString; -import static com.android.server.tare.TareUtils.getCurrentTimeMillis; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.pm.UserPackage; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; -import android.os.UserHandle; -import android.util.ArraySet; -import android.util.IndentingPrintWriter; -import android.util.Log; -import android.util.Slog; -import android.util.SparseArrayMap; -import android.util.SparseSetArray; -import android.util.TimeUtils; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.LocalServices; -import com.android.server.pm.UserManagerInternal; -import com.android.server.usage.AppStandbyInternal; -import com.android.server.utils.AlarmQueue; - -import java.util.List; -import java.util.Objects; -import java.util.function.Consumer; - -/** - * Other half of the IRS. The agent handles the nitty gritty details, interacting directly with - * ledgers, carrying out specific events such as wealth reclamation, granting initial balances or - * replenishing balances, and tracking ongoing events. - */ -class Agent { - private static final String TAG = "TARE-" + Agent.class.getSimpleName(); - private static final boolean DEBUG = InternalResourceService.DEBUG - || Log.isLoggable(TAG, Log.DEBUG); - - private static final String ALARM_TAG_AFFORDABILITY_CHECK = "*tare.affordability_check*"; - - private final Object mLock; - private final AgentHandler mHandler; - private final Analyst mAnalyst; - private final InternalResourceService mIrs; - private final Scribe mScribe; - - private final AppStandbyInternal mAppStandbyInternal; - - @GuardedBy("mLock") - private final SparseArrayMap<String, SparseArrayMap<String, OngoingEvent>> - mCurrentOngoingEvents = new SparseArrayMap<>(); - - /** - * Set of {@link ActionAffordabilityNote ActionAffordabilityNotes} keyed by userId-pkgName. - * - * Note: it would be nice/better to sort by base price since that doesn't change and simply - * look at the change in the "insertion" of what would be affordable, but since CTP - * is factored into the final price, the sorting order (by modified price) could be different - * and that method wouldn't work >:( - */ - @GuardedBy("mLock") - private final SparseArrayMap<String, ArraySet<ActionAffordabilityNote>> - mActionAffordabilityNotes = new SparseArrayMap<>(); - - /** - * Queue to track and manage when apps will cross the closest affordability threshold (in - * both directions). - */ - @GuardedBy("mLock") - private final BalanceThresholdAlarmQueue mBalanceThresholdAlarmQueue; - - /** - * Check the affordability notes of all apps. - */ - private static final int MSG_CHECK_ALL_AFFORDABILITY = 0; - /** - * Check the affordability notes of a single app. - */ - private static final int MSG_CHECK_INDIVIDUAL_AFFORDABILITY = 1; - - Agent(@NonNull InternalResourceService irs, @NonNull Scribe scribe, @NonNull Analyst analyst) { - mLock = irs.getLock(); - mIrs = irs; - mScribe = scribe; - mAnalyst = analyst; - mHandler = new AgentHandler(TareHandlerThread.get().getLooper()); - mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class); - mBalanceThresholdAlarmQueue = new BalanceThresholdAlarmQueue( - mIrs.getContext(), TareHandlerThread.get().getLooper()); - } - - private class TotalDeltaCalculator implements Consumer<OngoingEvent> { - private Ledger mLedger; - private long mNowElapsed; - private long mNow; - private long mTotal; - - void reset(@NonNull Ledger ledger, long nowElapsed, long now) { - mLedger = ledger; - mNowElapsed = nowElapsed; - mNow = now; - mTotal = 0; - } - - @Override - public void accept(OngoingEvent ongoingEvent) { - mTotal += getActualDeltaLocked(ongoingEvent, mLedger, mNowElapsed, mNow).price; - } - } - - @GuardedBy("mLock") - private final TotalDeltaCalculator mTotalDeltaCalculator = new TotalDeltaCalculator(); - - /** Get an app's current balance, factoring in any currently ongoing events. */ - @GuardedBy("mLock") - long getBalanceLocked(final int userId, @NonNull final String pkgName) { - final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); - long balance = ledger.getCurrentBalance(); - SparseArrayMap<String, OngoingEvent> ongoingEvents = - mCurrentOngoingEvents.get(userId, pkgName); - if (ongoingEvents != null) { - final long nowElapsed = SystemClock.elapsedRealtime(); - final long now = getCurrentTimeMillis(); - mTotalDeltaCalculator.reset(ledger, nowElapsed, now); - ongoingEvents.forEach(mTotalDeltaCalculator); - balance += mTotalDeltaCalculator.mTotal; - } - return balance; - } - - @GuardedBy("mLock") - private boolean isAffordableLocked(long balance, long price, long stockLimitHonoringCtp) { - return balance >= price - && mScribe.getRemainingConsumableCakesLocked() >= stockLimitHonoringCtp; - } - - @GuardedBy("mLock") - void noteInstantaneousEventLocked(final int userId, @NonNull final String pkgName, - final int eventId, @Nullable String tag) { - if (mIrs.isSystem(userId, pkgName)) { - // Events are free for the system. Don't bother recording them. - return; - } - - final long now = getCurrentTimeMillis(); - final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); - final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); - - final int eventType = getEventType(eventId); - switch (eventType) { - case TYPE_ACTION: - final EconomicPolicy.Cost actionCost = - economicPolicy.getCostOfAction(eventId, userId, pkgName); - - recordTransactionLocked(userId, pkgName, ledger, - new Ledger.Transaction(now, now, eventId, tag, - -actionCost.price, actionCost.costToProduce), - true); - break; - - case TYPE_REWARD: - final EconomicPolicy.Reward reward = economicPolicy.getReward(eventId); - if (reward != null) { - final long rewardSum = ledger.get24HourSum(eventId, now); - final long rewardVal = Math.max(0, - Math.min(reward.maxDailyReward - rewardSum, reward.instantReward)); - recordTransactionLocked(userId, pkgName, ledger, - new Ledger.Transaction(now, now, eventId, tag, rewardVal, 0), true); - } - break; - - default: - Slog.w(TAG, "Unsupported event type: " + eventType); - } - scheduleBalanceCheckLocked(userId, pkgName); - } - - @GuardedBy("mLock") - void noteOngoingEventLocked(final int userId, @NonNull final String pkgName, final int eventId, - @Nullable String tag, final long startElapsed) { - noteOngoingEventLocked(userId, pkgName, eventId, tag, startElapsed, true); - } - - @GuardedBy("mLock") - private void noteOngoingEventLocked(final int userId, @NonNull final String pkgName, - final int eventId, @Nullable String tag, final long startElapsed, - final boolean updateBalanceCheck) { - if (mIrs.isSystem(userId, pkgName)) { - // Events are free for the system. Don't bother recording them. - return; - } - - SparseArrayMap<String, OngoingEvent> ongoingEvents = - mCurrentOngoingEvents.get(userId, pkgName); - if (ongoingEvents == null) { - ongoingEvents = new SparseArrayMap<>(); - mCurrentOngoingEvents.add(userId, pkgName, ongoingEvents); - } - OngoingEvent ongoingEvent = ongoingEvents.get(eventId, tag); - - final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); - final int eventType = getEventType(eventId); - switch (eventType) { - case TYPE_ACTION: - final EconomicPolicy.Cost actionCost = - economicPolicy.getCostOfAction(eventId, userId, pkgName); - - if (ongoingEvent == null) { - ongoingEvents.add(eventId, tag, - new OngoingEvent(eventId, tag, startElapsed, actionCost)); - } else { - ongoingEvent.refCount++; - } - break; - - case TYPE_REWARD: - final EconomicPolicy.Reward reward = economicPolicy.getReward(eventId); - if (reward != null) { - if (ongoingEvent == null) { - ongoingEvents.add(eventId, tag, new OngoingEvent( - eventId, tag, startElapsed, reward)); - } else { - ongoingEvent.refCount++; - } - } - break; - - default: - Slog.w(TAG, "Unsupported event type: " + eventType); - } - - if (updateBalanceCheck) { - scheduleBalanceCheckLocked(userId, pkgName); - } - } - - @GuardedBy("mLock") - void onDeviceStateChangedLocked() { - onPricingChangedLocked(); - } - - @GuardedBy("mLock") - void onPricingChangedLocked() { - onAnythingChangedLocked(true); - } - - @GuardedBy("mLock") - void onAppStatesChangedLocked(final int userId, @NonNull ArraySet<String> pkgNames) { - final long now = getCurrentTimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); - final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); - - for (int i = 0; i < pkgNames.size(); ++i) { - final String pkgName = pkgNames.valueAt(i); - final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed); - SparseArrayMap<String, OngoingEvent> ongoingEvents = - mCurrentOngoingEvents.get(userId, pkgName); - if (ongoingEvents != null) { - mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed); - ongoingEvents.forEach(mOngoingEventUpdater); - final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes = - mActionAffordabilityNotes.get(userId, pkgName); - if (actionAffordabilityNotes != null) { - final int size = actionAffordabilityNotes.size(); - final long newBalance = - mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance(); - for (int n = 0; n < size; ++n) { - final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n); - note.recalculateCosts(economicPolicy, userId, pkgName); - final boolean isAffordable = isVip - || isAffordableLocked(newBalance, - note.getCachedModifiedPrice(), - note.getStockLimitHonoringCtp()); - if (note.isCurrentlyAffordable() != isAffordable) { - note.setNewAffordability(isAffordable); - mIrs.postAffordabilityChanged(userId, pkgName, note); - } - } - } - scheduleBalanceCheckLocked(userId, pkgName); - } - } - } - - @GuardedBy("mLock") - void onVipStatusChangedLocked(final int userId, @NonNull String pkgName) { - final long now = getCurrentTimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); - final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); - - final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed); - SparseArrayMap<String, OngoingEvent> ongoingEvents = - mCurrentOngoingEvents.get(userId, pkgName); - if (ongoingEvents != null) { - mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed); - ongoingEvents.forEach(mOngoingEventUpdater); - } - final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes = - mActionAffordabilityNotes.get(userId, pkgName); - if (actionAffordabilityNotes != null) { - final int size = actionAffordabilityNotes.size(); - final long newBalance = - mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance(); - for (int n = 0; n < size; ++n) { - final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n); - note.recalculateCosts(economicPolicy, userId, pkgName); - final boolean isAffordable = isVip - || isAffordableLocked(newBalance, - note.getCachedModifiedPrice(), note.getStockLimitHonoringCtp()); - if (note.isCurrentlyAffordable() != isAffordable) { - note.setNewAffordability(isAffordable); - mIrs.postAffordabilityChanged(userId, pkgName, note); - } - } - } - scheduleBalanceCheckLocked(userId, pkgName); - } - - @GuardedBy("mLock") - void onVipStatusChangedLocked(@NonNull SparseSetArray<String> pkgs) { - for (int u = pkgs.size() - 1; u >= 0; --u) { - final int userId = pkgs.keyAt(u); - - for (int p = pkgs.sizeAt(u) - 1; p >= 0; --p) { - onVipStatusChangedLocked(userId, pkgs.valueAt(u, p)); - } - } - } - - @GuardedBy("mLock") - private void onAnythingChangedLocked(final boolean updateOngoingEvents) { - final long now = getCurrentTimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); - final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); - - for (int uIdx = mCurrentOngoingEvents.numMaps() - 1; uIdx >= 0; --uIdx) { - final int userId = mCurrentOngoingEvents.keyAt(uIdx); - - for (int pIdx = mCurrentOngoingEvents.numElementsForKey(userId) - 1; pIdx >= 0; - --pIdx) { - final String pkgName = mCurrentOngoingEvents.keyAt(uIdx, pIdx); - - SparseArrayMap<String, OngoingEvent> ongoingEvents = - mCurrentOngoingEvents.valueAt(uIdx, pIdx); - if (ongoingEvents != null) { - if (updateOngoingEvents) { - mOngoingEventUpdater.reset(userId, pkgName, now, nowElapsed); - ongoingEvents.forEach(mOngoingEventUpdater); - } - scheduleBalanceCheckLocked(userId, pkgName); - } - } - } - for (int uIdx = mActionAffordabilityNotes.numMaps() - 1; uIdx >= 0; --uIdx) { - final int userId = mActionAffordabilityNotes.keyAt(uIdx); - - for (int pIdx = mActionAffordabilityNotes.numElementsForKey(userId) - 1; pIdx >= 0; - --pIdx) { - final String pkgName = mActionAffordabilityNotes.keyAt(uIdx, pIdx); - - final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes = - mActionAffordabilityNotes.valueAt(uIdx, pIdx); - - if (actionAffordabilityNotes != null) { - final int size = actionAffordabilityNotes.size(); - final long newBalance = getBalanceLocked(userId, pkgName); - final boolean isVip = mIrs.isVip(userId, pkgName, nowElapsed); - for (int n = 0; n < size; ++n) { - final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n); - note.recalculateCosts(economicPolicy, userId, pkgName); - final boolean isAffordable = isVip - || isAffordableLocked(newBalance, - note.getCachedModifiedPrice(), - note.getStockLimitHonoringCtp()); - if (note.isCurrentlyAffordable() != isAffordable) { - note.setNewAffordability(isAffordable); - mIrs.postAffordabilityChanged(userId, pkgName, note); - } - } - } - } - } - } - - @GuardedBy("mLock") - void stopOngoingActionLocked(final int userId, @NonNull final String pkgName, final int eventId, - @Nullable String tag, final long nowElapsed, final long now) { - stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now, true, true); - } - - /** - * @param updateBalanceCheck Whether to reschedule the affordability/balance - * check alarm. - * @param notifyOnAffordabilityChange Whether to evaluate the app's ability to afford - * registered bills and notify listeners about any changes. - */ - @GuardedBy("mLock") - private void stopOngoingActionLocked(final int userId, @NonNull final String pkgName, - final int eventId, @Nullable String tag, final long nowElapsed, final long now, - final boolean updateBalanceCheck, final boolean notifyOnAffordabilityChange) { - if (mIrs.isSystem(userId, pkgName)) { - // Events are free for the system. Don't bother recording them. - return; - } - - final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); - - SparseArrayMap<String, OngoingEvent> ongoingEvents = - mCurrentOngoingEvents.get(userId, pkgName); - if (ongoingEvents == null) { - // This may occur if TARE goes from disabled to enabled while an event is already - // occurring. - Slog.w(TAG, "No ongoing transactions for " + appToString(userId, pkgName)); - return; - } - final OngoingEvent ongoingEvent = ongoingEvents.get(eventId, tag); - if (ongoingEvent == null) { - // This may occur if TARE goes from disabled to enabled while an event is already - // occurring. - Slog.w(TAG, "Nonexistent ongoing transaction " - + eventToString(eventId) + (tag == null ? "" : ":" + tag) - + " for " + appToString(userId, pkgName) + " ended"); - return; - } - ongoingEvent.refCount--; - if (ongoingEvent.refCount <= 0) { - final long startElapsed = ongoingEvent.startTimeElapsed; - final long startTime = now - (nowElapsed - startElapsed); - final EconomicPolicy.Cost actualDelta = - getActualDeltaLocked(ongoingEvent, ledger, nowElapsed, now); - recordTransactionLocked(userId, pkgName, ledger, - new Ledger.Transaction(startTime, now, eventId, tag, actualDelta.price, - actualDelta.costToProduce), - notifyOnAffordabilityChange); - - ongoingEvents.delete(eventId, tag); - } - if (updateBalanceCheck) { - scheduleBalanceCheckLocked(userId, pkgName); - } - } - - @GuardedBy("mLock") - @NonNull - private EconomicPolicy.Cost getActualDeltaLocked(@NonNull OngoingEvent ongoingEvent, - @NonNull Ledger ledger, long nowElapsed, long now) { - final long startElapsed = ongoingEvent.startTimeElapsed; - final long durationSecs = (nowElapsed - startElapsed) / 1000; - final long computedDelta = durationSecs * ongoingEvent.getDeltaPerSec(); - if (ongoingEvent.reward == null) { - return new EconomicPolicy.Cost( - durationSecs * ongoingEvent.getCtpPerSec(), computedDelta); - } - final long rewardSum = ledger.get24HourSum(ongoingEvent.eventId, now); - return new EconomicPolicy.Cost(0, - Math.max(0, - Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta))); - } - - @VisibleForTesting - @GuardedBy("mLock") - void recordTransactionLocked(final int userId, @NonNull final String pkgName, - @NonNull Ledger ledger, @NonNull Ledger.Transaction transaction, - final boolean notifyOnAffordabilityChange) { - if (!DEBUG && transaction.delta == 0) { - // Skip recording transactions with a delta of 0 to save on space. - return; - } - if (mIrs.isSystem(userId, pkgName)) { - Slog.wtfStack(TAG, - "Tried to adjust system balance for " + appToString(userId, pkgName)); - return; - } - final boolean isVip = mIrs.isVip(userId, pkgName); - if (isVip) { - // This could happen if the app was made a VIP after it started performing actions. - // Continue recording the transaction for debugging purposes, but don't let it change - // any numbers. - transaction = new Ledger.Transaction( - transaction.startTimeMs, transaction.endTimeMs, - transaction.eventId, transaction.tag, 0 /* delta */, transaction.ctp); - } - final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); - final long originalBalance = ledger.getCurrentBalance(); - final long maxBalance = economicPolicy.getMaxSatiatedBalance(userId, pkgName); - if (transaction.delta > 0 - && originalBalance + transaction.delta > maxBalance) { - // Set lower bound at 0 so we don't accidentally take away credits when we were trying - // to _give_ the app credits. - final long newDelta = Math.max(0, maxBalance - originalBalance); - Slog.i(TAG, "Would result in becoming too rich. Decreasing transaction " - + eventToString(transaction.eventId) - + (transaction.tag == null ? "" : ":" + transaction.tag) - + " for " + appToString(userId, pkgName) - + " by " + cakeToString(transaction.delta - newDelta)); - transaction = new Ledger.Transaction( - transaction.startTimeMs, transaction.endTimeMs, - transaction.eventId, transaction.tag, newDelta, transaction.ctp); - } - ledger.recordTransaction(transaction); - mScribe.adjustRemainingConsumableCakesLocked(-transaction.ctp); - mAnalyst.noteTransaction(transaction); - if (transaction.delta != 0 && notifyOnAffordabilityChange) { - final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes = - mActionAffordabilityNotes.get(userId, pkgName); - if (actionAffordabilityNotes != null) { - final long newBalance = ledger.getCurrentBalance(); - for (int i = 0; i < actionAffordabilityNotes.size(); ++i) { - final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i); - final boolean isAffordable = isVip - || isAffordableLocked(newBalance, - note.getCachedModifiedPrice(), note.getStockLimitHonoringCtp()); - if (note.isCurrentlyAffordable() != isAffordable) { - note.setNewAffordability(isAffordable); - mIrs.postAffordabilityChanged(userId, pkgName, note); - } - } - } - } - if (transaction.ctp != 0) { - mHandler.sendEmptyMessage(MSG_CHECK_ALL_AFFORDABILITY); - mIrs.maybePerformQuantitativeEasingLocked(); - } - } - - @GuardedBy("mLock") - void reclaimAllAssetsLocked(final int userId, @NonNull final String pkgName, int regulationId) { - final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); - final long curBalance = ledger.getCurrentBalance(); - if (curBalance <= 0) { - return; - } - if (DEBUG) { - Slog.i(TAG, "Reclaiming " + cakeToString(curBalance) - + " from " + appToString(userId, pkgName) - + " because of " + eventToString(regulationId)); - } - - final long now = getCurrentTimeMillis(); - recordTransactionLocked(userId, pkgName, ledger, - new Ledger.Transaction(now, now, regulationId, null, -curBalance, 0), - true); - } - - /** - * Reclaim a percentage of unused ARCs from every app that hasn't been used recently. The - * reclamation will not reduce an app's balance below its minimum balance as dictated by - * {@code scaleMinBalance}. - * - * @param percentage A value between 0 and 1 to indicate how much of the unused balance - * should be reclaimed. - * @param minUnusedTimeMs The minimum amount of time (in milliseconds) that must have - * transpired since the last user usage event before we will consider - * reclaiming ARCs from the app. - * @param scaleMinBalance Whether or not to use the scaled minimum app balance. If false, - * this will use the constant min balance floor given by - * {@link EconomicPolicy#getMinSatiatedBalance(int, String)}. If true, - * this will use the scaled balance given by - * {@link InternalResourceService#getMinBalanceLocked(int, String)}. - */ - @GuardedBy("mLock") - void reclaimUnusedAssetsLocked(double percentage, long minUnusedTimeMs, - boolean scaleMinBalance) { - final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); - final SparseArrayMap<String, Ledger> ledgers = mScribe.getLedgersLocked(); - final long now = getCurrentTimeMillis(); - for (int u = 0; u < ledgers.numMaps(); ++u) { - final int userId = ledgers.keyAt(u); - for (int p = 0; p < ledgers.numElementsForKey(userId); ++p) { - final Ledger ledger = ledgers.valueAt(u, p); - final long curBalance = ledger.getCurrentBalance(); - if (curBalance <= 0) { - continue; - } - final String pkgName = ledgers.keyAt(u, p); - // AppStandby only counts elapsed time for things like this - // TODO: should we use clock time instead? - final long timeSinceLastUsedMs = - mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId); - if (timeSinceLastUsedMs >= minUnusedTimeMs) { - final long minBalance; - if (!scaleMinBalance) { - // Use a constant floor instead of the scaled floor from the IRS. - minBalance = economicPolicy.getMinSatiatedBalance(userId, pkgName); - } else { - minBalance = mIrs.getMinBalanceLocked(userId, pkgName); - } - long toReclaim = (long) (curBalance * percentage); - if (curBalance - toReclaim < minBalance) { - toReclaim = curBalance - minBalance; - } - if (toReclaim > 0) { - if (DEBUG) { - Slog.i(TAG, "Reclaiming unused wealth! Taking " - + cakeToString(toReclaim) - + " from " + appToString(userId, pkgName)); - } - - recordTransactionLocked(userId, pkgName, ledger, - new Ledger.Transaction(now, now, REGULATION_WEALTH_RECLAMATION, - null, -toReclaim, 0), - true); - } - } - } - } - } - - /** - * Reclaim a percentage of unused ARCs from an app that was just removed from an exemption list. - * The amount reclaimed will depend on how recently the app was used. The reclamation will not - * reduce an app's balance below its current minimum balance. - */ - @GuardedBy("mLock") - void onAppUnexemptedLocked(final int userId, @NonNull final String pkgName) { - final long curBalance = getBalanceLocked(userId, pkgName); - final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName); - if (curBalance <= minBalance) { - return; - } - // AppStandby only counts elapsed time for things like this - // TODO: should we use clock time instead? - final long timeSinceLastUsedMs = - mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId); - // The app is no longer exempted. We should take away some of credits so it's more in line - // with other non-exempt apps. However, don't take away as many credits if the app was used - // recently. - final double percentageToReclaim; - if (timeSinceLastUsedMs < DAY_IN_MILLIS) { - percentageToReclaim = .25; - } else if (timeSinceLastUsedMs < 2 * DAY_IN_MILLIS) { - percentageToReclaim = .5; - } else if (timeSinceLastUsedMs < 3 * DAY_IN_MILLIS) { - percentageToReclaim = .75; - } else { - percentageToReclaim = 1; - } - final long overage = curBalance - minBalance; - final long toReclaim = (long) (overage * percentageToReclaim); - if (toReclaim > 0) { - if (DEBUG) { - Slog.i(TAG, "Reclaiming bonus wealth! Taking " + toReclaim - + " from " + appToString(userId, pkgName)); - } - - final long now = getCurrentTimeMillis(); - final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); - recordTransactionLocked(userId, pkgName, ledger, - new Ledger.Transaction(now, now, REGULATION_DEMOTION, null, -toReclaim, 0), - true); - } - } - - /** - * Reclaim all ARCs from an app that was just restricted. - */ - @GuardedBy("mLock") - void onAppRestrictedLocked(final int userId, @NonNull final String pkgName) { - reclaimAllAssetsLocked(userId, pkgName, REGULATION_BG_RESTRICTED); - } - - /** - * Give an app that was just unrestricted some ARCs. - */ - @GuardedBy("mLock") - void onAppUnrestrictedLocked(final int userId, @NonNull final String pkgName) { - final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); - if (ledger.getCurrentBalance() > 0) { - Slog.wtf(TAG, "App " + pkgName + " had credits while it was restricted"); - // App already got credits somehow. Move along. - return; - } - - final long now = getCurrentTimeMillis(); - - recordTransactionLocked(userId, pkgName, ledger, - new Ledger.Transaction(now, now, REGULATION_BG_UNRESTRICTED, null, - mIrs.getMinBalanceLocked(userId, pkgName), 0), true); - } - - /** Returns true if an app should be given credits in the general distributions. */ - private boolean shouldGiveCredits(@NonNull InstalledPackageInfo packageInfo) { - // Skip apps that wouldn't be doing any work. Giving them ARCs would be wasteful. - if (!packageInfo.hasCode) { - return false; - } - final int userId = UserHandle.getUserId(packageInfo.uid); - // No point allocating ARCs to the system. It can do whatever it wants. - return !mIrs.isSystem(userId, packageInfo.packageName) - && !mIrs.isPackageRestricted(userId, packageInfo.packageName); - } - - void onCreditSupplyChanged() { - mHandler.sendEmptyMessage(MSG_CHECK_ALL_AFFORDABILITY); - } - - @GuardedBy("mLock") - void distributeBasicIncomeLocked(int batteryLevel) { - final SparseArrayMap<String, InstalledPackageInfo> pkgs = mIrs.getInstalledPackages(); - - final long now = getCurrentTimeMillis(); - for (int uIdx = pkgs.numMaps() - 1; uIdx >= 0; --uIdx) { - final int userId = pkgs.keyAt(uIdx); - - for (int pIdx = pkgs.numElementsForKeyAt(uIdx) - 1; pIdx >= 0; --pIdx) { - final InstalledPackageInfo pkgInfo = pkgs.valueAt(uIdx, pIdx); - if (!shouldGiveCredits(pkgInfo)) { - continue; - } - final String pkgName = pkgInfo.packageName; - final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); - final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName); - final double perc = batteryLevel / 100d; - // TODO: maybe don't give credits to bankrupt apps until battery level >= 50% - final long shortfall = minBalance - ledger.getCurrentBalance(); - if (shortfall > 0) { - recordTransactionLocked(userId, pkgName, ledger, - new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME, - null, (long) (perc * shortfall), 0), true); - } - } - } - } - - /** Give each app an initial balance. */ - @GuardedBy("mLock") - void grantBirthrightsLocked() { - UserManagerInternal userManagerInternal = - LocalServices.getService(UserManagerInternal.class); - final int[] userIds = userManagerInternal.getUserIds(); - for (int userId : userIds) { - grantBirthrightsLocked(userId); - } - } - - @GuardedBy("mLock") - void grantBirthrightsLocked(final int userId) { - final List<InstalledPackageInfo> pkgs = mIrs.getInstalledPackages(userId); - final long now = getCurrentTimeMillis(); - - for (int i = 0; i < pkgs.size(); ++i) { - final InstalledPackageInfo packageInfo = pkgs.get(i); - if (!shouldGiveCredits(packageInfo)) { - continue; - } - final String pkgName = packageInfo.packageName; - final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); - if (ledger.getCurrentBalance() > 0) { - // App already got credits somehow. Move along. - Slog.wtf(TAG, "App " + pkgName + " had credits before economy was set up"); - continue; - } - - recordTransactionLocked(userId, pkgName, ledger, - new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null, - mIrs.getMinBalanceLocked(userId, pkgName), 0), - true); - } - } - - @GuardedBy("mLock") - void grantBirthrightLocked(final int userId, @NonNull final String pkgName) { - final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); - if (ledger.getCurrentBalance() > 0) { - Slog.wtf(TAG, "App " + pkgName + " had credits as soon as it was installed"); - // App already got credits somehow. Move along. - return; - } - - final long now = getCurrentTimeMillis(); - - recordTransactionLocked(userId, pkgName, ledger, - new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null, - mIrs.getMinBalanceLocked(userId, pkgName), 0), true); - } - - @GuardedBy("mLock") - void onAppExemptedLocked(final int userId, @NonNull final String pkgName) { - final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName); - final long missing = minBalance - getBalanceLocked(userId, pkgName); - if (missing <= 0) { - return; - } - - final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); - final long now = getCurrentTimeMillis(); - - recordTransactionLocked(userId, pkgName, ledger, - new Ledger.Transaction(now, now, REGULATION_PROMOTION, null, missing, 0), true); - } - - @GuardedBy("mLock") - void onPackageRemovedLocked(final int userId, @NonNull final String pkgName) { - mScribe.discardLedgerLocked(userId, pkgName); - mCurrentOngoingEvents.delete(userId, pkgName); - mBalanceThresholdAlarmQueue.removeAlarmForKey(UserPackage.of(userId, pkgName)); - } - - @GuardedBy("mLock") - void onUserRemovedLocked(final int userId) { - mCurrentOngoingEvents.delete(userId); - mBalanceThresholdAlarmQueue.removeAlarmsForUserId(userId); - } - - @VisibleForTesting - static class TrendCalculator implements Consumer<OngoingEvent> { - static final long WILL_NOT_CROSS_THRESHOLD = -1; - - private long mCurBalance; - private long mRemainingConsumableCredits; - /** - * The maximum change in credits per second towards the upper threshold - * {@link #mUpperThreshold}. A value of 0 means the current ongoing events will never - * result in the app crossing the upper threshold. - */ - private long mMaxDeltaPerSecToUpperThreshold; - /** - * The maximum change in credits per second towards the lower threshold - * {@link #mLowerThreshold}. A value of 0 means the current ongoing events will never - * result in the app crossing the lower threshold. - */ - private long mMaxDeltaPerSecToLowerThreshold; - /** - * The maximum change in credits per second towards the highest CTP threshold below the - * remaining consumable credits (cached in {@link #mCtpThreshold}). A value of 0 means - * the current ongoing events will never result in the app crossing the lower threshold. - */ - private long mMaxDeltaPerSecToCtpThreshold; - private long mUpperThreshold; - private long mLowerThreshold; - private long mCtpThreshold; - - void reset(long curBalance, long remainingConsumableCredits, - @Nullable ArraySet<ActionAffordabilityNote> actionAffordabilityNotes) { - mCurBalance = curBalance; - mRemainingConsumableCredits = remainingConsumableCredits; - mMaxDeltaPerSecToUpperThreshold = mMaxDeltaPerSecToLowerThreshold = 0; - mMaxDeltaPerSecToCtpThreshold = 0; - mUpperThreshold = Long.MIN_VALUE; - mLowerThreshold = Long.MAX_VALUE; - mCtpThreshold = 0; - if (actionAffordabilityNotes != null) { - for (int i = 0; i < actionAffordabilityNotes.size(); ++i) { - final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i); - final long price = note.getCachedModifiedPrice(); - if (price <= mCurBalance) { - mLowerThreshold = (mLowerThreshold == Long.MAX_VALUE) - ? price : Math.max(mLowerThreshold, price); - } else { - mUpperThreshold = (mUpperThreshold == Long.MIN_VALUE) - ? price : Math.min(mUpperThreshold, price); - } - final long ctp = note.getStockLimitHonoringCtp(); - if (ctp <= mRemainingConsumableCredits) { - mCtpThreshold = Math.max(mCtpThreshold, ctp); - } - } - } - } - - /** - * Returns the amount of time (in millisecond) it will take for the app to cross the next - * lowest action affordability note (compared to its current balance) based on current - * ongoing events. - * Returns {@link #WILL_NOT_CROSS_THRESHOLD} if the app will never cross the lowest - * threshold. - */ - long getTimeToCrossLowerThresholdMs() { - if (mMaxDeltaPerSecToLowerThreshold == 0 && mMaxDeltaPerSecToCtpThreshold == 0) { - // Will never cross lower threshold based on current events. - return WILL_NOT_CROSS_THRESHOLD; - } - long minSeconds = Long.MAX_VALUE; - if (mMaxDeltaPerSecToLowerThreshold != 0) { - // deltaPerSec is a negative value, so do threshold-balance to cancel out the - // negative. - minSeconds = (mLowerThreshold - mCurBalance) / mMaxDeltaPerSecToLowerThreshold; - } - if (mMaxDeltaPerSecToCtpThreshold != 0) { - minSeconds = Math.min(minSeconds, - // deltaPerSec is a negative value, so do threshold-balance to cancel - // out the negative. - (mCtpThreshold - mRemainingConsumableCredits) - / mMaxDeltaPerSecToCtpThreshold); - } - return minSeconds * 1000; - } - - /** - * Returns the amount of time (in millisecond) it will take for the app to cross the next - * highest action affordability note (compared to its current balance) based on current - * ongoing events. - * Returns {@link #WILL_NOT_CROSS_THRESHOLD} if the app will never cross the upper - * threshold. - */ - long getTimeToCrossUpperThresholdMs() { - if (mMaxDeltaPerSecToUpperThreshold == 0) { - // Will never cross upper threshold based on current events. - return WILL_NOT_CROSS_THRESHOLD; - } - final long minSeconds = - (mUpperThreshold - mCurBalance) / mMaxDeltaPerSecToUpperThreshold; - return minSeconds * 1000; - } - - @Override - public void accept(OngoingEvent ongoingEvent) { - final long deltaPerSec = ongoingEvent.getDeltaPerSec(); - if (mCurBalance >= mLowerThreshold && deltaPerSec < 0) { - mMaxDeltaPerSecToLowerThreshold += deltaPerSec; - } else if (mCurBalance < mUpperThreshold && deltaPerSec > 0) { - mMaxDeltaPerSecToUpperThreshold += deltaPerSec; - } - final long ctpPerSec = ongoingEvent.getCtpPerSec(); - if (mRemainingConsumableCredits >= mCtpThreshold && deltaPerSec < 0) { - mMaxDeltaPerSecToCtpThreshold -= ctpPerSec; - } - } - } - - @GuardedBy("mLock") - private final TrendCalculator mTrendCalculator = new TrendCalculator(); - - @GuardedBy("mLock") - private void scheduleBalanceCheckLocked(final int userId, @NonNull final String pkgName) { - SparseArrayMap<String, OngoingEvent> ongoingEvents = - mCurrentOngoingEvents.get(userId, pkgName); - if (ongoingEvents == null || mIrs.isVip(userId, pkgName)) { - // No ongoing transactions. No reason to schedule - mBalanceThresholdAlarmQueue.removeAlarmForKey(UserPackage.of(userId, pkgName)); - return; - } - mTrendCalculator.reset(getBalanceLocked(userId, pkgName), - mScribe.getRemainingConsumableCakesLocked(), - mActionAffordabilityNotes.get(userId, pkgName)); - ongoingEvents.forEach(mTrendCalculator); - final long lowerTimeMs = mTrendCalculator.getTimeToCrossLowerThresholdMs(); - final long upperTimeMs = mTrendCalculator.getTimeToCrossUpperThresholdMs(); - final long timeToThresholdMs; - if (lowerTimeMs == TrendCalculator.WILL_NOT_CROSS_THRESHOLD) { - if (upperTimeMs == TrendCalculator.WILL_NOT_CROSS_THRESHOLD) { - // Will never cross a threshold based on current events. - mBalanceThresholdAlarmQueue.removeAlarmForKey(UserPackage.of(userId, pkgName)); - return; - } - timeToThresholdMs = upperTimeMs; - } else { - timeToThresholdMs = (upperTimeMs == TrendCalculator.WILL_NOT_CROSS_THRESHOLD) - ? lowerTimeMs : Math.min(lowerTimeMs, upperTimeMs); - } - mBalanceThresholdAlarmQueue.addAlarm(UserPackage.of(userId, pkgName), - SystemClock.elapsedRealtime() + timeToThresholdMs); - } - - @GuardedBy("mLock") - void tearDownLocked() { - mCurrentOngoingEvents.clear(); - mBalanceThresholdAlarmQueue.removeAllAlarms(); - mHandler.removeAllMessages(); - } - - @VisibleForTesting - static class OngoingEvent { - public final long startTimeElapsed; - public final int eventId; - @Nullable - public final String tag; - @Nullable - public final EconomicPolicy.Reward reward; - @Nullable - public final EconomicPolicy.Cost actionCost; - public int refCount; - - OngoingEvent(int eventId, @Nullable String tag, long startTimeElapsed, - @NonNull EconomicPolicy.Reward reward) { - this.startTimeElapsed = startTimeElapsed; - this.eventId = eventId; - this.tag = tag; - this.reward = reward; - this.actionCost = null; - refCount = 1; - } - - OngoingEvent(int eventId, @Nullable String tag, long startTimeElapsed, - @NonNull EconomicPolicy.Cost actionCost) { - this.startTimeElapsed = startTimeElapsed; - this.eventId = eventId; - this.tag = tag; - this.reward = null; - this.actionCost = actionCost; - refCount = 1; - } - - long getDeltaPerSec() { - if (actionCost != null) { - return -actionCost.price; - } - if (reward != null) { - return reward.ongoingRewardPerSecond; - } - Slog.wtfStack(TAG, "No action or reward in ongoing event?!??!"); - return 0; - } - - long getCtpPerSec() { - if (actionCost != null) { - return actionCost.costToProduce; - } - return 0; - } - } - - private class OngoingEventUpdater implements Consumer<OngoingEvent> { - private int mUserId; - private String mPkgName; - private long mNow; - private long mNowElapsed; - - private void reset(int userId, String pkgName, long now, long nowElapsed) { - mUserId = userId; - mPkgName = pkgName; - mNow = now; - mNowElapsed = nowElapsed; - } - - @Override - public void accept(OngoingEvent ongoingEvent) { - // Disable balance check & affordability notifications here because - // we're in the middle of updating ongoing action costs/prices and - // sending out notifications or rescheduling the balance check alarm - // would be a waste since we'll have to redo them again after all of - // our internal state is updated. - final boolean updateBalanceCheck = false; - stopOngoingActionLocked(mUserId, mPkgName, ongoingEvent.eventId, ongoingEvent.tag, - mNowElapsed, mNow, updateBalanceCheck, /* notifyOnAffordabilityChange */ false); - noteOngoingEventLocked(mUserId, mPkgName, ongoingEvent.eventId, ongoingEvent.tag, - mNowElapsed, updateBalanceCheck); - } - } - - private final OngoingEventUpdater mOngoingEventUpdater = new OngoingEventUpdater(); - - /** Track when apps will cross the closest affordability threshold (in both directions). */ - private class BalanceThresholdAlarmQueue extends AlarmQueue<UserPackage> { - private BalanceThresholdAlarmQueue(Context context, Looper looper) { - super(context, looper, ALARM_TAG_AFFORDABILITY_CHECK, "Affordability check", true, - 15_000L); - } - - @Override - protected boolean isForUser(@NonNull UserPackage key, int userId) { - return key.userId == userId; - } - - @Override - protected void processExpiredAlarms(@NonNull ArraySet<UserPackage> expired) { - for (int i = 0; i < expired.size(); ++i) { - UserPackage p = expired.valueAt(i); - mHandler.obtainMessage( - MSG_CHECK_INDIVIDUAL_AFFORDABILITY, p.userId, 0, p.packageName) - .sendToTarget(); - } - } - } - - @GuardedBy("mLock") - public void registerAffordabilityChangeListenerLocked(int userId, @NonNull String pkgName, - @NonNull EconomyManagerInternal.AffordabilityChangeListener listener, - @NonNull EconomyManagerInternal.ActionBill bill) { - ArraySet<ActionAffordabilityNote> actionAffordabilityNotes = - mActionAffordabilityNotes.get(userId, pkgName); - if (actionAffordabilityNotes == null) { - actionAffordabilityNotes = new ArraySet<>(); - mActionAffordabilityNotes.add(userId, pkgName, actionAffordabilityNotes); - } - final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); - final ActionAffordabilityNote note = - new ActionAffordabilityNote(bill, listener, economicPolicy); - if (actionAffordabilityNotes.add(note)) { - if (mIrs.getEnabledMode() == ENABLED_MODE_OFF) { - // When TARE isn't enabled, we always say something is affordable. We also don't - // want to silently drop affordability change listeners in case TARE becomes enabled - // because then clients will be in an ambiguous state. - note.setNewAffordability(true); - return; - } - final boolean isVip = mIrs.isVip(userId, pkgName); - note.recalculateCosts(economicPolicy, userId, pkgName); - note.setNewAffordability(isVip - || isAffordableLocked(getBalanceLocked(userId, pkgName), - note.getCachedModifiedPrice(), note.getStockLimitHonoringCtp())); - mIrs.postAffordabilityChanged(userId, pkgName, note); - // Update ongoing alarm - scheduleBalanceCheckLocked(userId, pkgName); - } - } - - @GuardedBy("mLock") - public void unregisterAffordabilityChangeListenerLocked(int userId, @NonNull String pkgName, - @NonNull EconomyManagerInternal.AffordabilityChangeListener listener, - @NonNull EconomyManagerInternal.ActionBill bill) { - final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes = - mActionAffordabilityNotes.get(userId, pkgName); - if (actionAffordabilityNotes != null) { - final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked(); - final ActionAffordabilityNote note = - new ActionAffordabilityNote(bill, listener, economicPolicy); - if (actionAffordabilityNotes.remove(note)) { - // Update ongoing alarm - scheduleBalanceCheckLocked(userId, pkgName); - } - } - } - - static final class ActionAffordabilityNote { - private final EconomyManagerInternal.ActionBill mActionBill; - private final EconomyManagerInternal.AffordabilityChangeListener mListener; - private long mStockLimitHonoringCtp; - private long mModifiedPrice; - private boolean mIsAffordable; - - @VisibleForTesting - ActionAffordabilityNote(@NonNull EconomyManagerInternal.ActionBill bill, - @NonNull EconomyManagerInternal.AffordabilityChangeListener listener, - @NonNull EconomicPolicy economicPolicy) { - mActionBill = bill; - final List<EconomyManagerInternal.AnticipatedAction> anticipatedActions = - bill.getAnticipatedActions(); - for (int i = 0; i < anticipatedActions.size(); ++i) { - final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i); - final EconomicPolicy.Action action = economicPolicy.getAction(aa.actionId); - if (action == null) { - if ((aa.actionId & EconomicPolicy.ALL_POLICIES) == 0) { - throw new IllegalArgumentException("Invalid action id: " + aa.actionId); - } else { - Slog.w(TAG, "Tracking disabled policy's action? " + aa.actionId); - } - } - } - mListener = listener; - } - - @NonNull - EconomyManagerInternal.ActionBill getActionBill() { - return mActionBill; - } - - @NonNull - EconomyManagerInternal.AffordabilityChangeListener getListener() { - return mListener; - } - - private long getCachedModifiedPrice() { - return mModifiedPrice; - } - - /** Returns the cumulative CTP of actions in this note that respect the stock limit. */ - private long getStockLimitHonoringCtp() { - return mStockLimitHonoringCtp; - } - - @VisibleForTesting - void recalculateCosts(@NonNull EconomicPolicy economicPolicy, - int userId, @NonNull String pkgName) { - long modifiedPrice = 0; - long stockLimitHonoringCtp = 0; - final List<EconomyManagerInternal.AnticipatedAction> anticipatedActions = - mActionBill.getAnticipatedActions(); - for (int i = 0; i < anticipatedActions.size(); ++i) { - final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i); - final EconomicPolicy.Action action = economicPolicy.getAction(aa.actionId); - - final EconomicPolicy.Cost actionCost = - economicPolicy.getCostOfAction(aa.actionId, userId, pkgName); - modifiedPrice += actionCost.price * aa.numInstantaneousCalls - + actionCost.price * (aa.ongoingDurationMs / 1000); - if (action.respectsStockLimit) { - stockLimitHonoringCtp += - actionCost.costToProduce * aa.numInstantaneousCalls - + actionCost.costToProduce * (aa.ongoingDurationMs / 1000); - } - } - mModifiedPrice = modifiedPrice; - mStockLimitHonoringCtp = stockLimitHonoringCtp; - } - - boolean isCurrentlyAffordable() { - return mIsAffordable; - } - - private void setNewAffordability(boolean isAffordable) { - mIsAffordable = isAffordable; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ActionAffordabilityNote)) return false; - ActionAffordabilityNote other = (ActionAffordabilityNote) o; - return mActionBill.equals(other.mActionBill) - && mListener.equals(other.mListener); - } - - @Override - public int hashCode() { - int hash = 0; - hash = 31 * hash + Objects.hash(mListener); - hash = 31 * hash + mActionBill.hashCode(); - return hash; - } - } - - private final class AgentHandler extends Handler { - AgentHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_CHECK_ALL_AFFORDABILITY: { - synchronized (mLock) { - removeMessages(MSG_CHECK_ALL_AFFORDABILITY); - onAnythingChangedLocked(false); - } - } - break; - - case MSG_CHECK_INDIVIDUAL_AFFORDABILITY: { - final int userId = msg.arg1; - final String pkgName = (String) msg.obj; - synchronized (mLock) { - final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes = - mActionAffordabilityNotes.get(userId, pkgName); - if (actionAffordabilityNotes != null - && actionAffordabilityNotes.size() > 0) { - final long newBalance = getBalanceLocked(userId, pkgName); - final boolean isVip = mIrs.isVip(userId, pkgName); - - for (int i = 0; i < actionAffordabilityNotes.size(); ++i) { - final ActionAffordabilityNote note = - actionAffordabilityNotes.valueAt(i); - final boolean isAffordable = isVip || isAffordableLocked( - newBalance, note.getCachedModifiedPrice(), - note.getStockLimitHonoringCtp()); - if (note.isCurrentlyAffordable() != isAffordable) { - note.setNewAffordability(isAffordable); - mIrs.postAffordabilityChanged(userId, pkgName, note); - } - } - } - scheduleBalanceCheckLocked(userId, pkgName); - } - } - break; - } - } - - void removeAllMessages() { - removeMessages(MSG_CHECK_ALL_AFFORDABILITY); - removeMessages(MSG_CHECK_INDIVIDUAL_AFFORDABILITY); - } - } - - @GuardedBy("mLock") - void dumpLocked(IndentingPrintWriter pw) { - mBalanceThresholdAlarmQueue.dump(pw); - - pw.println(); - pw.println("Ongoing events:"); - pw.increaseIndent(); - boolean printedEvents = false; - final long nowElapsed = SystemClock.elapsedRealtime(); - for (int u = mCurrentOngoingEvents.numMaps() - 1; u >= 0; --u) { - final int userId = mCurrentOngoingEvents.keyAt(u); - for (int p = mCurrentOngoingEvents.numElementsForKey(userId) - 1; p >= 0; --p) { - final String pkgName = mCurrentOngoingEvents.keyAt(u, p); - final SparseArrayMap<String, OngoingEvent> ongoingEvents = - mCurrentOngoingEvents.get(userId, pkgName); - - boolean printedApp = false; - - for (int e = ongoingEvents.numMaps() - 1; e >= 0; --e) { - final int eventId = ongoingEvents.keyAt(e); - for (int t = ongoingEvents.numElementsForKey(eventId) - 1; t >= 0; --t) { - if (!printedApp) { - printedApp = true; - pw.println(appToString(userId, pkgName)); - pw.increaseIndent(); - } - printedEvents = true; - - OngoingEvent ongoingEvent = ongoingEvents.valueAt(e, t); - - pw.print(EconomicPolicy.eventToString(ongoingEvent.eventId)); - if (ongoingEvent.tag != null) { - pw.print("("); - pw.print(ongoingEvent.tag); - pw.print(")"); - } - pw.print(" runtime="); - TimeUtils.formatDuration(nowElapsed - ongoingEvent.startTimeElapsed, pw); - pw.print(" delta/sec="); - pw.print(cakeToString(ongoingEvent.getDeltaPerSec())); - final long ctp = ongoingEvent.getCtpPerSec(); - if (ctp != 0) { - pw.print(" ctp/sec="); - pw.print(cakeToString(ongoingEvent.getCtpPerSec())); - } - pw.print(" refCount="); - pw.print(ongoingEvent.refCount); - pw.println(); - } - } - - if (printedApp) { - pw.decreaseIndent(); - } - } - } - if (!printedEvents) { - pw.print("N/A"); - } - pw.decreaseIndent(); - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java deleted file mode 100644 index 8381d1a322e0..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java +++ /dev/null @@ -1,454 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -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_INITIAL_CONSUMPTION_LIMIT_CAKES; -import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES; -import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES; -import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_CONSUMPTION_LIMIT_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_HEADLESS_SYSTEM_APP_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; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP; -import static android.app.tare.EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT; -import static android.app.tare.EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT; -import static android.app.tare.EconomyManager.KEY_AM_MAX_SATIATED_BALANCE; -import static android.app.tare.EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT; -import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED; -import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP; -import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_MAX; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_INSTANT; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_MAX; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_ONGOING; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_MAX; -import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING; -import static android.app.tare.EconomyManager.arcToCake; -import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS; - -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.cakeToString; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ContentResolver; -import android.provider.DeviceConfig; -import android.util.IndentingPrintWriter; -import android.util.Slog; -import android.util.SparseArray; - -/** - * Policy defining pricing information and daily ARC requirements and suggestions for - * AlarmManager. - */ -public class AlarmManagerEconomicPolicy extends EconomicPolicy { - private static final String TAG = "TARE- " + AlarmManagerEconomicPolicy.class.getSimpleName(); - - public static final int ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE = - TYPE_ACTION | POLICY_ALARM | 0; - public static final int ACTION_ALARM_WAKEUP_EXACT = - TYPE_ACTION | POLICY_ALARM | 1; - public static final int ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE = - TYPE_ACTION | POLICY_ALARM | 2; - public static final int ACTION_ALARM_WAKEUP_INEXACT = - TYPE_ACTION | POLICY_ALARM | 3; - public static final int ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE = - TYPE_ACTION | POLICY_ALARM | 4; - public static final int ACTION_ALARM_NONWAKEUP_EXACT = - TYPE_ACTION | POLICY_ALARM | 5; - public static final int ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE = - TYPE_ACTION | POLICY_ALARM | 6; - public static final int ACTION_ALARM_NONWAKEUP_INEXACT = - TYPE_ACTION | POLICY_ALARM | 7; - public static final int ACTION_ALARM_CLOCK = - TYPE_ACTION | POLICY_ALARM | 8; - - private static final int[] COST_MODIFIERS = new int[]{ - COST_MODIFIER_CHARGING, - COST_MODIFIER_DEVICE_IDLE, - COST_MODIFIER_POWER_SAVE_MODE, - COST_MODIFIER_PROCESS_STATE - }; - - private long mMinSatiatedBalanceExempted; - private long mMinSatiatedBalanceHeadlessSystemApp; - private long mMinSatiatedBalanceOther; - private long mMaxSatiatedBalance; - private long mInitialSatiatedConsumptionLimit; - private long mMinSatiatedConsumptionLimit; - private long mMaxSatiatedConsumptionLimit; - - private final Injector mInjector; - - private final SparseArray<Action> mActions = new SparseArray<>(); - private final SparseArray<Reward> mRewards = new SparseArray<>(); - - AlarmManagerEconomicPolicy(InternalResourceService irs, Injector injector) { - super(irs); - mInjector = injector; - loadConstants("", null); - } - - @Override - void setup(@NonNull DeviceConfig.Properties properties) { - super.setup(properties); - ContentResolver resolver = mIrs.getContext().getContentResolver(); - loadConstants(mInjector.getSettingsGlobalString(resolver, TARE_ALARM_MANAGER_CONSTANTS), - properties); - } - - @Override - long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) { - if (mIrs.isPackageRestricted(userId, pkgName)) { - return 0; - } - if (mIrs.isPackageExempted(userId, pkgName)) { - return mMinSatiatedBalanceExempted; - } - if (mIrs.isHeadlessSystemApp(userId, pkgName)) { - return mMinSatiatedBalanceHeadlessSystemApp; - } - // TODO: take other exemptions into account - return mMinSatiatedBalanceOther; - } - - @Override - long getMaxSatiatedBalance(int userId, @NonNull String pkgName) { - if (mIrs.isPackageRestricted(userId, pkgName)) { - return 0; - } - // 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; - } - - @Override - long getInitialSatiatedConsumptionLimit() { - return mInitialSatiatedConsumptionLimit; - } - - @Override - long getMinSatiatedConsumptionLimit() { - return mMinSatiatedConsumptionLimit; - } - - @Override - long getMaxSatiatedConsumptionLimit() { - return mMaxSatiatedConsumptionLimit; - } - - @NonNull - @Override - int[] getCostModifiers() { - return COST_MODIFIERS; - } - - @Nullable - @Override - Action getAction(@AppAction int actionId) { - return mActions.get(actionId); - } - - @Nullable - @Override - Reward getReward(@UtilityReward int rewardId) { - return mRewards.get(rewardId); - } - - private void loadConstants(String policyValuesString, - @Nullable DeviceConfig.Properties properties) { - mActions.clear(); - mRewards.clear(); - - try { - mUserSettingDeviceConfigMediator.setSettingsString(policyValuesString); - mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties); - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Global setting key incorrect: ", e); - } - - mMinSatiatedBalanceOther = getConstantAsCake( - KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES); - mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake( - KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, - DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES, - mMinSatiatedBalanceOther); - mMinSatiatedBalanceExempted = getConstantAsCake( - KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, - DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES, - mMinSatiatedBalanceHeadlessSystemApp); - mMaxSatiatedBalance = getConstantAsCake( - KEY_AM_MAX_SATIATED_BALANCE, DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES, - Math.max(arcToCake(1), mMinSatiatedBalanceExempted)); - mMinSatiatedConsumptionLimit = getConstantAsCake( - KEY_AM_MIN_CONSUMPTION_LIMIT, DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES, - arcToCake(1)); - mInitialSatiatedConsumptionLimit = getConstantAsCake( - KEY_AM_INITIAL_CONSUMPTION_LIMIT, DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES, - mMinSatiatedConsumptionLimit); - mMaxSatiatedConsumptionLimit = getConstantAsCake( - KEY_AM_MAX_CONSUMPTION_LIMIT, DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES, - mInitialSatiatedConsumptionLimit); - - final long exactAllowWhileIdleWakeupBasePrice = getConstantAsCake( - KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES); - - // Apps must hold the SCHEDULE_EXACT_ALARM or USE_EXACT_ALARMS permission in order to use - // exact alarms. Since the user has the option of granting/revoking the permission, we can - // be a little lenient on the action cost checks and only stop the action if the app has - // run out of credits, and not when the system has run out of stock. - mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, - new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, - getConstantAsCake( - KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES), - exactAllowWhileIdleWakeupBasePrice, - /* respectsStockLimit */ false)); - mActions.put(ACTION_ALARM_WAKEUP_EXACT, - new Action(ACTION_ALARM_WAKEUP_EXACT, - getConstantAsCake( - KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES), - getConstantAsCake( - KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES), - /* respectsStockLimit */ false)); - - final long inexactAllowWhileIdleWakeupBasePrice = - getConstantAsCake( - KEY_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, - getConstantAsCake( - KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES), - inexactAllowWhileIdleWakeupBasePrice, - /* respectsStockLimit */ false)); - mActions.put(ACTION_ALARM_WAKEUP_INEXACT, - new Action(ACTION_ALARM_WAKEUP_INEXACT, - getConstantAsCake( - KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES), - getConstantAsCake( - KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE_CAKES), - /* respectsStockLimit */ false)); - - final long exactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake( - 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, - getConstantAsCake( - KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES), - exactAllowWhileIdleNonWakeupBasePrice, - /* respectsStockLimit */ false)); - - mActions.put(ACTION_ALARM_NONWAKEUP_EXACT, - new Action(ACTION_ALARM_NONWAKEUP_EXACT, - getConstantAsCake( - KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES), - getConstantAsCake( - KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE_CAKES), - /* respectsStockLimit */ false)); - - final long inexactAllowWhileIdleNonWakeupBasePrice = getConstantAsCake( - 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( - 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, - inexactAllowWhileIdleNonWakeupCtp, - inexactAllowWhileIdleNonWakeupBasePrice)); - - mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT, - new Action(ACTION_ALARM_NONWAKEUP_INEXACT, - getConstantAsCake( - KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP, - DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP_CAKES), - getConstantAsCake( - KEY_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, - getConstantAsCake( - KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP, - DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES), - getConstantAsCake( - KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE, - DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES), - /* respectsStockLimit */ false)); - - mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY, - getConstantAsCake( - KEY_AM_REWARD_TOP_ACTIVITY_INSTANT, - DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT_CAKES), - getConstantAsCake( - KEY_AM_REWARD_TOP_ACTIVITY_ONGOING, - DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING_CAKES), - getConstantAsCake( - KEY_AM_REWARD_TOP_ACTIVITY_MAX, - DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX_CAKES))); - mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN, - getConstantAsCake( - KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT, - DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES), - getConstantAsCake( - KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING, - DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES), - getConstantAsCake( - KEY_AM_REWARD_NOTIFICATION_SEEN_MAX, - DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX_CAKES))); - mRewards.put(REWARD_NOTIFICATION_INTERACTION, - new Reward(REWARD_NOTIFICATION_INTERACTION, - getConstantAsCake( - KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT, - DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES), - getConstantAsCake( - KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING, - DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES), - getConstantAsCake( - KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX, - DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES))); - mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION, - getConstantAsCake( - KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT, - DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT_CAKES), - getConstantAsCake( - KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING, - DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT, - DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES), - getConstantAsCake( - KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING, - DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES), - getConstantAsCake( - KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX, - DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX_CAKES))); - } - - @Override - void dump(IndentingPrintWriter pw) { - pw.println("Min satiated balances:"); - pw.increaseIndent(); - pw.print("Exempted", cakeToString(mMinSatiatedBalanceExempted)).println(); - pw.print("Other", cakeToString(mMinSatiatedBalanceOther)).println(); - pw.decreaseIndent(); - pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println(); - pw.print("Consumption limits: ["); - pw.print(cakeToString(mMinSatiatedConsumptionLimit)); - pw.print(", "); - pw.print(cakeToString(mInitialSatiatedConsumptionLimit)); - pw.print(", "); - pw.print(cakeToString(mMaxSatiatedConsumptionLimit)); - pw.println("]"); - - pw.println(); - pw.println("Actions:"); - pw.increaseIndent(); - for (int i = 0; i < mActions.size(); ++i) { - dumpAction(pw, mActions.valueAt(i)); - } - pw.decreaseIndent(); - - pw.println(); - pw.println("Rewards:"); - pw.increaseIndent(); - for (int i = 0; i < mRewards.size(); ++i) { - dumpReward(pw, mRewards.valueAt(i)); - } - pw.decreaseIndent(); - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Analyst.java b/apex/jobscheduler/service/java/com/android/server/tare/Analyst.java deleted file mode 100644 index 06333f16dbf2..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/Analyst.java +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static android.text.format.DateUtils.HOUR_IN_MILLIS; - -import static com.android.server.tare.EconomicPolicy.TYPE_ACTION; -import static com.android.server.tare.EconomicPolicy.TYPE_REGULATION; -import static com.android.server.tare.EconomicPolicy.TYPE_REWARD; -import static com.android.server.tare.EconomicPolicy.getEventType; -import static com.android.server.tare.TareUtils.cakeToString; - -import android.annotation.NonNull; -import android.os.BatteryManagerInternal; -import android.os.RemoteException; -import android.util.IndentingPrintWriter; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.app.IBatteryStats; -import com.android.server.LocalServices; -import com.android.server.am.BatteryStatsService; - -import java.util.ArrayList; -import java.util.List; - -/** - * Responsible for maintaining statistics and analysis of TARE's performance. - */ -public class Analyst { - private static final String TAG = "TARE-" + Analyst.class.getSimpleName(); - private static final boolean DEBUG = InternalResourceService.DEBUG - || Log.isLoggable(TAG, Log.DEBUG); - - private static final int NUM_PERIODS_TO_RETAIN = 8; - @VisibleForTesting - static final long MIN_REPORT_DURATION_FOR_RESET = 24 * HOUR_IN_MILLIS; - - static final class Report { - /** How much the battery was discharged over the tracked period. */ - public int cumulativeBatteryDischarge = 0; - public int currentBatteryLevel = 0; - /** - * Profit from performing actions. This excludes special circumstances where we charge the - * app - * less than the action's CTP. - */ - public long cumulativeProfit = 0; - public int numProfitableActions = 0; - /** - * Losses from performing actions for special circumstances (eg. for a TOP app) where we - * charge - * the app less than the action's CTP. - */ - public long cumulativeLoss = 0; - public int numUnprofitableActions = 0; - /** - * The total number of rewards given to apps over this period. - */ - public long cumulativeRewards = 0; - public int numRewards = 0; - /** - * Regulations that increased an app's balance. - */ - public long cumulativePositiveRegulations = 0; - public int numPositiveRegulations = 0; - /** - * Regulations that decreased an app's balance. - */ - public long cumulativeNegativeRegulations = 0; - public int numNegativeRegulations = 0; - - /** - * The approximate amount of time the screen has been off while on battery while this - * report has been active. - */ - public long screenOffDurationMs = 0; - /** - * The approximate amount of battery discharge while this report has been active. - */ - public long screenOffDischargeMah = 0; - /** The offset used to get the delta when polling the screen off time from BatteryStats. */ - private long bsScreenOffRealtimeBase = 0; - /** - * The offset used to get the delta when polling the screen off discharge from BatteryStats. - */ - private long bsScreenOffDischargeMahBase = 0; - - private void clear() { - cumulativeBatteryDischarge = 0; - currentBatteryLevel = 0; - cumulativeProfit = 0; - numProfitableActions = 0; - cumulativeLoss = 0; - numUnprofitableActions = 0; - cumulativeRewards = 0; - numRewards = 0; - cumulativePositiveRegulations = 0; - numPositiveRegulations = 0; - cumulativeNegativeRegulations = 0; - numNegativeRegulations = 0; - screenOffDurationMs = 0; - screenOffDischargeMah = 0; - bsScreenOffRealtimeBase = 0; - bsScreenOffDischargeMahBase = 0; - } - } - - private final IBatteryStats mIBatteryStats; - - private int mPeriodIndex = 0; - /** How much the battery was discharged over the tracked period. */ - private final Report[] mReports = new Report[NUM_PERIODS_TO_RETAIN]; - - Analyst() { - this(BatteryStatsService.getService()); - } - - @VisibleForTesting Analyst(IBatteryStats iBatteryStats) { - mIBatteryStats = iBatteryStats; - } - - /** Returns the list of most recent reports, with the oldest report first. */ - @NonNull - List<Report> getReports() { - final List<Report> list = new ArrayList<>(NUM_PERIODS_TO_RETAIN); - for (int i = 1; i <= NUM_PERIODS_TO_RETAIN; ++i) { - final int idx = (mPeriodIndex + i) % NUM_PERIODS_TO_RETAIN; - final Report report = mReports[idx]; - if (report != null) { - list.add(report); - } - } - return list; - } - - long getBatteryScreenOffDischargeMah() { - long discharge = 0; - for (Report report : mReports) { - if (report == null) { - continue; - } - discharge += report.screenOffDischargeMah; - } - return discharge; - } - - long getBatteryScreenOffDurationMs() { - long duration = 0; - for (Report report : mReports) { - if (report == null) { - continue; - } - duration += report.screenOffDurationMs; - } - return duration; - } - - /** - * Tracks the given reports instead of whatever is currently saved. Reports should be ordered - * oldest to most recent. - */ - void loadReports(@NonNull List<Report> reports) { - final int numReports = reports.size(); - mPeriodIndex = Math.max(0, Math.min(NUM_PERIODS_TO_RETAIN, numReports) - 1); - for (int i = 0; i < NUM_PERIODS_TO_RETAIN; ++i) { - if (i < numReports) { - mReports[i] = reports.get(i); - } else { - mReports[i] = null; - } - } - final Report latest = mReports[mPeriodIndex]; - if (latest != null) { - latest.bsScreenOffRealtimeBase = getLatestBatteryScreenOffRealtimeMs(); - latest.bsScreenOffDischargeMahBase = getLatestScreenOffDischargeMah(); - } - } - - void noteBatteryLevelChange(int newBatteryLevel) { - final boolean deviceDischargedEnough = mReports[mPeriodIndex] != null - && newBatteryLevel >= 90 - // Battery level is increasing, so device is charging. - && mReports[mPeriodIndex].currentBatteryLevel < newBatteryLevel - && mReports[mPeriodIndex].cumulativeBatteryDischarge >= 25; - final boolean reportLongEnough = mReports[mPeriodIndex] != null - // Battery level is increasing, so device is charging. - && mReports[mPeriodIndex].currentBatteryLevel < newBatteryLevel - && mReports[mPeriodIndex].screenOffDurationMs >= MIN_REPORT_DURATION_FOR_RESET; - final boolean shouldStartNewReport = deviceDischargedEnough || reportLongEnough; - if (shouldStartNewReport) { - mPeriodIndex = (mPeriodIndex + 1) % NUM_PERIODS_TO_RETAIN; - if (mReports[mPeriodIndex] != null) { - final Report report = mReports[mPeriodIndex]; - report.clear(); - report.currentBatteryLevel = newBatteryLevel; - report.bsScreenOffRealtimeBase = getLatestBatteryScreenOffRealtimeMs(); - report.bsScreenOffDischargeMahBase = getLatestScreenOffDischargeMah(); - return; - } - } - - if (mReports[mPeriodIndex] == null) { - Report report = initializeReport(); - mReports[mPeriodIndex] = report; - report.currentBatteryLevel = newBatteryLevel; - return; - } - - final Report report = mReports[mPeriodIndex]; - if (newBatteryLevel < report.currentBatteryLevel) { - report.cumulativeBatteryDischarge += (report.currentBatteryLevel - newBatteryLevel); - - final long latestScreenOffRealtime = getLatestBatteryScreenOffRealtimeMs(); - final long latestScreenOffDischargeMah = getLatestScreenOffDischargeMah(); - if (report.bsScreenOffRealtimeBase > latestScreenOffRealtime) { - // BatteryStats reset - report.bsScreenOffRealtimeBase = 0; - report.bsScreenOffDischargeMahBase = 0; - } - report.screenOffDurationMs += - (latestScreenOffRealtime - report.bsScreenOffRealtimeBase); - report.screenOffDischargeMah += - (latestScreenOffDischargeMah - report.bsScreenOffDischargeMahBase); - report.bsScreenOffRealtimeBase = latestScreenOffRealtime; - report.bsScreenOffDischargeMahBase = latestScreenOffDischargeMah; - } - report.currentBatteryLevel = newBatteryLevel; - } - - void noteTransaction(@NonNull Ledger.Transaction transaction) { - if (mReports[mPeriodIndex] == null) { - mReports[mPeriodIndex] = initializeReport(); - } - final Report report = mReports[mPeriodIndex]; - switch (getEventType(transaction.eventId)) { - case TYPE_ACTION: - // For now, assume all instances where price < CTP is a special instance. - // TODO: add an explicit signal for special circumstances - if (-transaction.delta > transaction.ctp) { - report.cumulativeProfit += (-transaction.delta - transaction.ctp); - report.numProfitableActions++; - } else if (-transaction.delta < transaction.ctp) { - report.cumulativeLoss += (transaction.ctp + transaction.delta); - report.numUnprofitableActions++; - } - break; - case TYPE_REGULATION: - if (transaction.delta > 0) { - report.cumulativePositiveRegulations += transaction.delta; - report.numPositiveRegulations++; - } else if (transaction.delta < 0) { - report.cumulativeNegativeRegulations -= transaction.delta; - report.numNegativeRegulations++; - } - break; - case TYPE_REWARD: - if (transaction.delta != 0) { - report.cumulativeRewards += transaction.delta; - report.numRewards++; - } - break; - } - } - - void tearDown() { - for (int i = 0; i < mReports.length; ++i) { - mReports[i] = null; - } - mPeriodIndex = 0; - } - - private long getLatestBatteryScreenOffRealtimeMs() { - try { - return mIBatteryStats.computeBatteryScreenOffRealtimeMs(); - } catch (RemoteException e) { - // Shouldn't happen - return 0; - } - } - - private long getLatestScreenOffDischargeMah() { - try { - return mIBatteryStats.getScreenOffDischargeMah(); - } catch (RemoteException e) { - // Shouldn't happen - return 0; - } - } - - @NonNull - private Report initializeReport() { - final Report report = new Report(); - report.bsScreenOffRealtimeBase = getLatestBatteryScreenOffRealtimeMs(); - report.bsScreenOffDischargeMahBase = getLatestScreenOffDischargeMah(); - return report; - } - - @NonNull - private String padStringWithSpaces(@NonNull String text, int targetLength) { - // Make sure to have at least one space on either side. - final int padding = Math.max(2, targetLength - text.length()) >>> 1; - return " ".repeat(padding) + text + " ".repeat(padding); - } - - void dump(IndentingPrintWriter pw) { - final BatteryManagerInternal bmi = LocalServices.getService(BatteryManagerInternal.class); - final long batteryCapacityMah = bmi.getBatteryFullCharge() / 1000; - pw.println("Reports:"); - pw.increaseIndent(); - pw.print(" Total Discharge"); - final int statColsLength = 47; - pw.print(padStringWithSpaces("Profit (avg/action : avg/discharge)", statColsLength)); - pw.print(padStringWithSpaces("Loss (avg/action : avg/discharge)", statColsLength)); - pw.print(padStringWithSpaces("Rewards (avg/reward : avg/discharge)", statColsLength)); - pw.print(padStringWithSpaces("+Regs (avg/reg : avg/discharge)", statColsLength)); - pw.print(padStringWithSpaces("-Regs (avg/reg : avg/discharge)", statColsLength)); - pw.print(padStringWithSpaces("Bg drain estimate", statColsLength)); - pw.println(); - for (int r = 0; r < NUM_PERIODS_TO_RETAIN; ++r) { - final int idx = (mPeriodIndex - r + NUM_PERIODS_TO_RETAIN) % NUM_PERIODS_TO_RETAIN; - final Report report = mReports[idx]; - if (report == null) { - continue; - } - pw.print("t-"); - pw.print(r); - pw.print(": "); - pw.print(padStringWithSpaces(Integer.toString(report.cumulativeBatteryDischarge), 15)); - if (report.numProfitableActions > 0) { - final String perDischarge = report.cumulativeBatteryDischarge > 0 - ? cakeToString(report.cumulativeProfit / report.cumulativeBatteryDischarge) - : "N/A"; - pw.print(padStringWithSpaces(String.format("%s (%s : %s)", - cakeToString(report.cumulativeProfit), - cakeToString(report.cumulativeProfit / report.numProfitableActions), - perDischarge), - statColsLength)); - } else { - pw.print(padStringWithSpaces("N/A", statColsLength)); - } - if (report.numUnprofitableActions > 0) { - final String perDischarge = report.cumulativeBatteryDischarge > 0 - ? cakeToString(report.cumulativeLoss / report.cumulativeBatteryDischarge) - : "N/A"; - pw.print(padStringWithSpaces(String.format("%s (%s : %s)", - cakeToString(report.cumulativeLoss), - cakeToString(report.cumulativeLoss / report.numUnprofitableActions), - perDischarge), - statColsLength)); - } else { - pw.print(padStringWithSpaces("N/A", statColsLength)); - } - if (report.numRewards > 0) { - final String perDischarge = report.cumulativeBatteryDischarge > 0 - ? cakeToString(report.cumulativeRewards / report.cumulativeBatteryDischarge) - : "N/A"; - pw.print(padStringWithSpaces(String.format("%s (%s : %s)", - cakeToString(report.cumulativeRewards), - cakeToString(report.cumulativeRewards / report.numRewards), - perDischarge), - statColsLength)); - } else { - pw.print(padStringWithSpaces("N/A", statColsLength)); - } - if (report.numPositiveRegulations > 0) { - final String perDischarge = report.cumulativeBatteryDischarge > 0 - ? cakeToString( - report.cumulativePositiveRegulations / report.cumulativeBatteryDischarge) - : "N/A"; - pw.print(padStringWithSpaces(String.format("%s (%s : %s)", - cakeToString(report.cumulativePositiveRegulations), - cakeToString(report.cumulativePositiveRegulations - / report.numPositiveRegulations), - perDischarge), - statColsLength)); - } else { - pw.print(padStringWithSpaces("N/A", statColsLength)); - } - if (report.numNegativeRegulations > 0) { - final String perDischarge = report.cumulativeBatteryDischarge > 0 - ? cakeToString( - report.cumulativeNegativeRegulations / report.cumulativeBatteryDischarge) - : "N/A"; - pw.print(padStringWithSpaces(String.format("%s (%s : %s)", - cakeToString(report.cumulativeNegativeRegulations), - cakeToString(report.cumulativeNegativeRegulations - / report.numNegativeRegulations), - perDischarge), - statColsLength)); - } else { - pw.print(padStringWithSpaces("N/A", statColsLength)); - } - if (report.screenOffDurationMs > 0) { - pw.print(padStringWithSpaces(String.format("%d mAh (%.2f%%/hr)", - report.screenOffDischargeMah, - 100.0 * report.screenOffDischargeMah * HOUR_IN_MILLIS - / (batteryCapacityMah * report.screenOffDurationMs)), - statColsLength)); - } else { - pw.print(padStringWithSpaces("N/A", statColsLength)); - } - pw.println(); - } - pw.decreaseIndent(); - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java deleted file mode 100644 index 2b48d49e3052..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import android.annotation.NonNull; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.BatteryManager; -import android.os.SystemClock; -import android.util.IndentingPrintWriter; -import android.util.Log; -import android.util.Slog; - -/** Modifier that makes things free when the device is charging. */ -class ChargingModifier extends Modifier { - private static final String TAG = "TARE-" + ChargingModifier.class.getSimpleName(); - private static final boolean DEBUG = InternalResourceService.DEBUG - || Log.isLoggable(TAG, Log.DEBUG); - - private final InternalResourceService mIrs; - private final ChargingTracker mChargingTracker; - - ChargingModifier(@NonNull InternalResourceService irs) { - super(); - mIrs = irs; - mChargingTracker = new ChargingTracker(); - } - - @Override - public void setup() { - mChargingTracker.startTracking(mIrs.getContext()); - } - - @Override - public void tearDown() { - mChargingTracker.stopTracking(mIrs.getContext()); - } - - @Override - long getModifiedCostToProduce(long ctp) { - return modifyValue(ctp); - } - - @Override - long getModifiedPrice(long price) { - return modifyValue(price); - } - - private long modifyValue(long val) { - if (mChargingTracker.mCharging) { - return 0; - } - return val; - } - - @Override - void dump(IndentingPrintWriter pw) { - pw.print("charging="); - pw.println(mChargingTracker.mCharging); - } - - private final class ChargingTracker extends BroadcastReceiver { - private boolean mIsSetup = false; - - /** - * Track whether we're "charging", where charging means that we're ready to commit to - * doing work. - */ - private volatile boolean mCharging; - - public void startTracking(@NonNull Context context) { - if (mIsSetup) { - return; - } - - final IntentFilter filter = new IntentFilter(); - filter.addAction(BatteryManager.ACTION_CHARGING); - filter.addAction(BatteryManager.ACTION_DISCHARGING); - context.registerReceiver(this, filter); - - // Initialise tracker state. - final BatteryManager batteryManager = context.getSystemService(BatteryManager.class); - mCharging = batteryManager.isCharging(); - - mIsSetup = true; - } - - public void stopTracking(@NonNull Context context) { - if (!mIsSetup) { - return; - } - - context.unregisterReceiver(this); - mIsSetup = false; - } - - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (BatteryManager.ACTION_CHARGING.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Received charging intent, fired @ " - + SystemClock.elapsedRealtime()); - } - if (!mCharging) { - mCharging = true; - mIrs.onDeviceStateChanged(); - } - } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Disconnected from power."); - } - if (mCharging) { - mCharging = false; - mIrs.onDeviceStateChanged(); - } - } - } - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java deleted file mode 100644 index 7a9607657972..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.tare.EconomyManager; -import android.provider.DeviceConfig; -import android.util.ArraySet; -import android.util.IndentingPrintWriter; -import android.util.Slog; -import android.util.SparseArray; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; - -import libcore.util.EmptyArray; - -/** Combines all enabled policies into one. */ -public class CompleteEconomicPolicy extends EconomicPolicy { - private static final String TAG = "TARE-" + CompleteEconomicPolicy.class.getSimpleName(); - - private final CompleteInjector mInjector; - private final ArraySet<EconomicPolicy> mEnabledEconomicPolicies = new ArraySet<>(); - /** Lazily populated set of actions covered by this policy. */ - private final SparseArray<Action> mActions = new SparseArray<>(); - /** Lazily populated set of rewards covered by this policy. */ - private final SparseArray<Reward> mRewards = new SparseArray<>(); - private int mEnabledEconomicPolicyIds = 0; - private int[] mCostModifiers = EmptyArray.INT; - private long mInitialConsumptionLimit; - private long mMinConsumptionLimit; - private long mMaxConsumptionLimit; - - CompleteEconomicPolicy(@NonNull InternalResourceService irs) { - this(irs, new CompleteInjector()); - } - - @VisibleForTesting - CompleteEconomicPolicy(@NonNull InternalResourceService irs, - @NonNull CompleteInjector injector) { - super(irs); - mInjector = injector; - - if (mInjector.isPolicyEnabled(POLICY_ALARM, null)) { - mEnabledEconomicPolicyIds |= POLICY_ALARM; - mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(mIrs, mInjector)); - } - if (mInjector.isPolicyEnabled(POLICY_JOB, null)) { - mEnabledEconomicPolicyIds |= POLICY_JOB; - mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(mIrs, mInjector)); - } - } - - @Override - void setup(@NonNull DeviceConfig.Properties properties) { - super.setup(properties); - - mActions.clear(); - mRewards.clear(); - - mEnabledEconomicPolicies.clear(); - mEnabledEconomicPolicyIds = 0; - if (mInjector.isPolicyEnabled(POLICY_ALARM, properties)) { - mEnabledEconomicPolicyIds |= POLICY_ALARM; - mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(mIrs, mInjector)); - } - if (mInjector.isPolicyEnabled(POLICY_JOB, properties)) { - mEnabledEconomicPolicyIds |= POLICY_JOB; - mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(mIrs, mInjector)); - } - - ArraySet<Integer> costModifiers = new ArraySet<>(); - for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) { - final int[] sm = mEnabledEconomicPolicies.valueAt(i).getCostModifiers(); - for (int s : sm) { - costModifiers.add(s); - } - } - mCostModifiers = ArrayUtils.convertToIntArray(costModifiers); - - for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) { - mEnabledEconomicPolicies.valueAt(i).setup(properties); - } - updateLimits(); - } - - private void updateLimits() { - long initialConsumptionLimit = 0; - long minConsumptionLimit = 0; - long maxConsumptionLimit = 0; - for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) { - final EconomicPolicy economicPolicy = mEnabledEconomicPolicies.valueAt(i); - initialConsumptionLimit += economicPolicy.getInitialSatiatedConsumptionLimit(); - minConsumptionLimit += economicPolicy.getMinSatiatedConsumptionLimit(); - maxConsumptionLimit += economicPolicy.getMaxSatiatedConsumptionLimit(); - } - mInitialConsumptionLimit = initialConsumptionLimit; - mMinConsumptionLimit = minConsumptionLimit; - mMaxConsumptionLimit = maxConsumptionLimit; - } - - @Override - long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) { - long min = 0; - for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) { - min += mEnabledEconomicPolicies.valueAt(i).getMinSatiatedBalance(userId, pkgName); - } - return min; - } - - @Override - long getMaxSatiatedBalance(int userId, @NonNull String pkgName) { - long max = 0; - for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) { - max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedBalance(userId, pkgName); - } - return max; - } - - @Override - long getInitialSatiatedConsumptionLimit() { - return mInitialConsumptionLimit; - } - - @Override - long getMinSatiatedConsumptionLimit() { - return mMinConsumptionLimit; - } - - @Override - long getMaxSatiatedConsumptionLimit() { - return mMaxConsumptionLimit; - } - - @NonNull - @Override - int[] getCostModifiers() { - return mCostModifiers == null ? EmptyArray.INT : mCostModifiers; - } - - @Nullable - @Override - Action getAction(@AppAction int actionId) { - if (mActions.contains(actionId)) { - return mActions.get(actionId); - } - - long ctp = 0, price = 0; - boolean exists = false; - for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) { - Action a = mEnabledEconomicPolicies.valueAt(i).getAction(actionId); - if (a != null) { - exists = true; - ctp += a.costToProduce; - price += a.basePrice; - } - } - final Action action = exists ? new Action(actionId, ctp, price) : null; - mActions.put(actionId, action); - return action; - } - - @Nullable - @Override - Reward getReward(@UtilityReward int rewardId) { - if (mRewards.contains(rewardId)) { - return mRewards.get(rewardId); - } - - long instantReward = 0, ongoingReward = 0, maxReward = 0; - boolean exists = false; - for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) { - Reward r = mEnabledEconomicPolicies.valueAt(i).getReward(rewardId); - if (r != null) { - exists = true; - instantReward += r.instantReward; - ongoingReward += r.ongoingRewardPerSecond; - maxReward += r.maxDailyReward; - } - } - final Reward reward = exists - ? new Reward(rewardId, instantReward, ongoingReward, maxReward) : null; - mRewards.put(rewardId, reward); - return reward; - } - - boolean isPolicyEnabled(@Policy int policyId) { - return (mEnabledEconomicPolicyIds & policyId) == policyId; - } - - int getEnabledPolicyIds() { - return mEnabledEconomicPolicyIds; - } - - @VisibleForTesting - static class CompleteInjector extends Injector { - - boolean isPolicyEnabled(int policy, @Nullable DeviceConfig.Properties properties) { - final String key; - final boolean defaultEnable; - switch (policy) { - case POLICY_ALARM: - key = EconomyManager.KEY_ENABLE_POLICY_ALARM; - defaultEnable = EconomyManager.DEFAULT_ENABLE_POLICY_ALARM; - break; - case POLICY_JOB: - key = EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER; - defaultEnable = EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER; - break; - default: - Slog.wtf(TAG, "Unknown policy: " + policy); - return false; - } - if (properties == null) { - return defaultEnable; - } - return properties.getBoolean(key, defaultEnable); - } - } - - @Override - void dump(IndentingPrintWriter pw) { - dumpActiveModifiers(pw); - - pw.println(); - pw.println(getClass().getSimpleName() + ":"); - pw.increaseIndent(); - - pw.println("Cached actions:"); - pw.increaseIndent(); - for (int i = 0; i < mActions.size(); ++i) { - final Action action = mActions.valueAt(i); - if (action != null) { - dumpAction(pw, action); - } - } - pw.decreaseIndent(); - - pw.println(); - pw.println("Cached rewards:"); - pw.increaseIndent(); - for (int i = 0; i < mRewards.size(); ++i) { - final Reward reward = mRewards.valueAt(i); - if (reward != null) { - dumpReward(pw, reward); - } - } - pw.decreaseIndent(); - - for (int i = 0; i < mEnabledEconomicPolicies.size(); i++) { - final EconomicPolicy economicPolicy = mEnabledEconomicPolicies.valueAt(i); - pw.println(); - pw.print("(Includes) "); - pw.println(economicPolicy.getClass().getSimpleName() + ":"); - pw.increaseIndent(); - economicPolicy.dump(pw); - pw.decreaseIndent(); - } - pw.decreaseIndent(); - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java deleted file mode 100644 index 47ff307deda6..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import android.annotation.NonNull; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.PowerManager; -import android.util.IndentingPrintWriter; -import android.util.Log; - -/** Modifier that makes things more expensive in light and deep doze. */ -class DeviceIdleModifier extends Modifier { - private static final String TAG = "TARE-" + DeviceIdleModifier.class.getSimpleName(); - private static final boolean DEBUG = InternalResourceService.DEBUG - || Log.isLoggable(TAG, Log.DEBUG); - - private final InternalResourceService mIrs; - private final PowerManager mPowerManager; - private final DeviceIdleTracker mDeviceIdleTracker; - - DeviceIdleModifier(@NonNull InternalResourceService irs) { - super(); - mIrs = irs; - mPowerManager = irs.getContext().getSystemService(PowerManager.class); - mDeviceIdleTracker = new DeviceIdleTracker(); - } - - @Override - public void setup() { - mDeviceIdleTracker.startTracking(mIrs.getContext()); - } - - @Override - public void tearDown() { - mDeviceIdleTracker.stopTracking(mIrs.getContext()); - } - - @Override - long getModifiedCostToProduce(long ctp) { - if (mDeviceIdleTracker.mDeviceIdle) { - return (long) (1.2 * ctp); - } - if (mDeviceIdleTracker.mDeviceLightIdle) { - return (long) (1.1 * ctp); - } - return ctp; - } - - @Override - void dump(IndentingPrintWriter pw) { - pw.print("idle="); - pw.println(mDeviceIdleTracker.mDeviceIdle); - pw.print("lightIdle="); - pw.println(mDeviceIdleTracker.mDeviceLightIdle); - } - - private final class DeviceIdleTracker extends BroadcastReceiver { - private boolean mIsSetup = false; - - private volatile boolean mDeviceIdle; - private volatile boolean mDeviceLightIdle; - - DeviceIdleTracker() { - } - - void startTracking(@NonNull Context context) { - if (mIsSetup) { - return; - } - - IntentFilter filter = new IntentFilter(); - filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); - filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED); - context.registerReceiver(this, filter); - - // Initialise tracker state. - mDeviceIdle = mPowerManager.isDeviceIdleMode(); - mDeviceLightIdle = mPowerManager.isLightDeviceIdleMode(); - - mIsSetup = true; - } - - void stopTracking(@NonNull Context context) { - if (!mIsSetup) { - return; - } - - context.unregisterReceiver(this); - mIsSetup = false; - } - - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) { - if (mDeviceIdle != mPowerManager.isDeviceIdleMode()) { - mDeviceIdle = mPowerManager.isDeviceIdleMode(); - mIrs.onDeviceStateChanged(); - } - } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action)) { - if (mDeviceLightIdle != mPowerManager.isLightDeviceIdleMode()) { - mDeviceLightIdle = mPowerManager.isLightDeviceIdleMode(); - mIrs.onDeviceStateChanged(); - } - } - } - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java deleted file mode 100644 index 61096b9fa179..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package 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; -import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE; -import static com.android.server.tare.Modifier.NUM_COST_MODIFIERS; -import static com.android.server.tare.TareUtils.cakeToString; - -import android.annotation.CallSuper; -import android.annotation.IntDef; -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 com.android.internal.annotations.VisibleForTesting; -import com.android.server.utils.UserSettingDeviceConfigMediator; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * An EconomicPolicy includes pricing information and daily ARC requirements and suggestions. - * Policies are defined per participating system service. This allows each service’s EconomicPolicy - * to be isolated while allowing the core economic system to scale across policies to achieve a - * logical system-wide value system. - */ -public abstract class EconomicPolicy { - private static final String TAG = "TARE-" + EconomicPolicy.class.getSimpleName(); - - private static final int SHIFT_TYPE = 30; - static final int MASK_TYPE = 0b11 << SHIFT_TYPE; - static final int TYPE_REGULATION = 0 << SHIFT_TYPE; - static final int TYPE_ACTION = 1 << SHIFT_TYPE; - static final int TYPE_REWARD = 2 << SHIFT_TYPE; - - private static final int SHIFT_POLICY = 28; - static final int MASK_POLICY = 0b11 << SHIFT_POLICY; - static final int ALL_POLICIES = MASK_POLICY; - // Reserve 0 for the base/common policy. - public static final int POLICY_ALARM = 1 << SHIFT_POLICY; - public static final int POLICY_JOB = 2 << SHIFT_POLICY; - - static final int MASK_EVENT = -1 ^ (MASK_TYPE | MASK_POLICY); - - static final int REGULATION_BASIC_INCOME = TYPE_REGULATION | 0; - static final int REGULATION_BIRTHRIGHT = TYPE_REGULATION | 1; - static final int REGULATION_WEALTH_RECLAMATION = TYPE_REGULATION | 2; - static final int REGULATION_PROMOTION = TYPE_REGULATION | 3; - static final int REGULATION_DEMOTION = TYPE_REGULATION | 4; - /** App is fully restricted from running in the background. */ - static final int REGULATION_BG_RESTRICTED = TYPE_REGULATION | 5; - static final int REGULATION_BG_UNRESTRICTED = TYPE_REGULATION | 6; - static final int REGULATION_FORCE_STOP = TYPE_REGULATION | 8; - - static final int REWARD_NOTIFICATION_SEEN = TYPE_REWARD | 0; - static final int REWARD_NOTIFICATION_INTERACTION = TYPE_REWARD | 1; - static final int REWARD_TOP_ACTIVITY = TYPE_REWARD | 2; - static final int REWARD_WIDGET_INTERACTION = TYPE_REWARD | 3; - static final int REWARD_OTHER_USER_INTERACTION = TYPE_REWARD | 4; - - @IntDef({ - AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, - AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT, - AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE, - AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT, - AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE, - AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT, - AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE, - AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT, - AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, - JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, - JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, - JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, - JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, - JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START, - JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, - JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, - JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING, - JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, - JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING, - JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface AppAction { - } - - @IntDef({ - TYPE_ACTION, - TYPE_REGULATION, - TYPE_REWARD, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface EventType { - } - - @IntDef({ - ALL_POLICIES, - POLICY_ALARM, - POLICY_JOB, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Policy { - } - - @IntDef({ - REWARD_TOP_ACTIVITY, - REWARD_NOTIFICATION_SEEN, - REWARD_NOTIFICATION_INTERACTION, - REWARD_WIDGET_INTERACTION, - REWARD_OTHER_USER_INTERACTION, - JobSchedulerEconomicPolicy.REWARD_APP_INSTALL, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface UtilityReward { - } - - static class Action { - /** Unique id (including across policies) for this action. */ - public final int id; - /** - * How many ARCs the system says it takes to perform this action. - */ - public final long costToProduce; - /** - * The base price to perform this action. If this is - * less than the {@link #costToProduce}, then the system should not perform - * the action unless a modifier lowers the cost to produce. - */ - public final long basePrice; - /** - * Whether the remaining stock limit affects an app's ability to perform this action. - * If {@code false}, then the action can be performed, even if the cost is higher - * than the remaining stock. This does not affect checking against an app's balance. - */ - public final boolean respectsStockLimit; - - Action(int id, long costToProduce, long basePrice) { - this(id, costToProduce, basePrice, true); - } - - Action(int id, long costToProduce, long basePrice, boolean respectsStockLimit) { - this.id = id; - this.costToProduce = costToProduce; - this.basePrice = basePrice; - this.respectsStockLimit = respectsStockLimit; - } - } - - static class Reward { - /** Unique id (including across policies) for this reward. */ - @UtilityReward - public final int id; - public final long instantReward; - /** Reward credited per second of ongoing activity. */ - public final long ongoingRewardPerSecond; - /** The maximum amount an app can earn from this reward within a 24 hour period. */ - public final long maxDailyReward; - - Reward(int id, long instantReward, long ongoingReward, long maxDailyReward) { - this.id = id; - this.instantReward = instantReward; - this.ongoingRewardPerSecond = ongoingReward; - this.maxDailyReward = maxDailyReward; - } - } - - static class Cost { - public final long costToProduce; - public final long price; - - Cost(long costToProduce, long price) { - this.costToProduce = costToProduce; - this.price = price; - } - } - - protected final InternalResourceService mIrs; - protected final UserSettingDeviceConfigMediator mUserSettingDeviceConfigMediator; - private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS]; - - EconomicPolicy(@NonNull InternalResourceService irs) { - mIrs = irs; - // 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 user settings exist, then just stick with the Settings constants, even if there - // are invalid values. - mUserSettingDeviceConfigMediator = - new UserSettingDeviceConfigMediator.SettingsOverridesAllMediator(','); - for (int mId : getCostModifiers()) { - initModifier(mId, irs); - } - } - - @CallSuper - 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) { - modifier.setup(); - } - } - } - - @CallSuper - void tearDown() { - for (int i = 0; i < NUM_COST_MODIFIERS; ++i) { - final Modifier modifier = COST_MODIFIER_BY_INDEX[i]; - if (modifier != null) { - modifier.tearDown(); - } - } - } - - /** - * Returns the minimum suggested balance an app should have when the device is at 100% battery. - * This takes into account any exemptions the app may have. - */ - abstract long getMinSatiatedBalance(int userId, @NonNull String pkgName); - - /** - * Returns the maximum balance an app should have when the device is at 100% battery. This - * exists to ensure that no single app accumulate all available resources and increases fairness - * for all apps. - */ - abstract long getMaxSatiatedBalance(int userId, @NonNull String pkgName); - - /** - * Returns the maximum number of cakes that should be consumed during a full 100% discharge - * cycle. This is the initial limit. The system may choose to increase the limit over time, - * but the increased limit should never exceed the value returned from - * {@link #getMaxSatiatedConsumptionLimit()}. - */ - abstract long getInitialSatiatedConsumptionLimit(); - - /** - * Returns the minimum number of cakes that should be available for consumption during a full - * 100% discharge cycle. - */ - abstract long getMinSatiatedConsumptionLimit(); - - /** - * Returns the maximum number of cakes that should be available for consumption during a full - * 100% discharge cycle. - */ - abstract long getMaxSatiatedConsumptionLimit(); - - /** Return the set of modifiers that should apply to this policy's costs. */ - @NonNull - abstract int[] getCostModifiers(); - - @Nullable - abstract Action getAction(@AppAction int actionId); - - @Nullable - abstract Reward getReward(@UtilityReward int rewardId); - - void dump(IndentingPrintWriter pw) { - } - - @NonNull - final Cost getCostOfAction(int actionId, int userId, @NonNull String pkgName) { - final Action action = getAction(actionId); - if (action == null || mIrs.isVip(userId, pkgName)) { - return new Cost(0, 0); - } - long ctp = action.costToProduce; - long price = action.basePrice; - final int[] costModifiers = getCostModifiers(); - boolean useProcessStatePriceDeterminant = false; - for (int costModifier : costModifiers) { - if (costModifier == COST_MODIFIER_PROCESS_STATE) { - useProcessStatePriceDeterminant = true; - } else { - final Modifier modifier = getModifier(costModifier); - ctp = modifier.getModifiedCostToProduce(ctp); - price = modifier.getModifiedPrice(price); - } - } - // ProcessStateModifier needs to be done last. - if (useProcessStatePriceDeterminant) { - ProcessStateModifier processStateModifier = - (ProcessStateModifier) getModifier(COST_MODIFIER_PROCESS_STATE); - price = processStateModifier.getModifiedPrice(userId, pkgName, ctp, price); - } - return new Cost(ctp, price); - } - - private static void initModifier(@Modifier.CostModifier final int modifierId, - @NonNull InternalResourceService irs) { - if (modifierId < 0 || modifierId >= COST_MODIFIER_BY_INDEX.length) { - throw new IllegalArgumentException("Invalid modifier id " + modifierId); - } - Modifier modifier = COST_MODIFIER_BY_INDEX[modifierId]; - if (modifier == null) { - switch (modifierId) { - case COST_MODIFIER_CHARGING: - modifier = new ChargingModifier(irs); - break; - case COST_MODIFIER_DEVICE_IDLE: - modifier = new DeviceIdleModifier(irs); - break; - case COST_MODIFIER_POWER_SAVE_MODE: - modifier = new PowerSaveModeModifier(irs); - break; - case COST_MODIFIER_PROCESS_STATE: - modifier = new ProcessStateModifier(irs); - break; - default: - throw new IllegalArgumentException("Invalid modifier id " + modifierId); - } - COST_MODIFIER_BY_INDEX[modifierId] = modifier; - } - } - - @NonNull - private static Modifier getModifier(@Modifier.CostModifier final int modifierId) { - if (modifierId < 0 || modifierId >= COST_MODIFIER_BY_INDEX.length) { - throw new IllegalArgumentException("Invalid modifier id " + modifierId); - } - final Modifier modifier = COST_MODIFIER_BY_INDEX[modifierId]; - if (modifier == null) { - throw new IllegalStateException( - "Modifier #" + modifierId + " was never initialized"); - } - return modifier; - } - - @EventType - static int getEventType(int eventId) { - return eventId & MASK_TYPE; - } - - static boolean isReward(int eventId) { - return getEventType(eventId) == TYPE_REWARD; - } - - @NonNull - static String eventToString(int eventId) { - switch (eventId & MASK_TYPE) { - case TYPE_ACTION: - return actionToString(eventId); - - case TYPE_REGULATION: - return regulationToString(eventId); - - case TYPE_REWARD: - return rewardToString(eventId); - - default: - return "UNKNOWN_EVENT:" + Integer.toHexString(eventId); - } - } - - @NonNull - static String actionToString(int eventId) { - switch (eventId & MASK_POLICY) { - case POLICY_ALARM: - switch (eventId) { - case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE: - return "ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE"; - case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT: - return "ALARM_WAKEUP_EXACT"; - case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE: - return "ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE"; - case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT: - return "ALARM_WAKEUP_INEXACT"; - case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE: - return "ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE"; - case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT: - return "ALARM_NONWAKEUP_EXACT"; - case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE: - return "ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE"; - case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT: - return "ALARM_NONWAKEUP_INEXACT"; - case AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK: - return "ALARM_CLOCK"; - } - break; - - case POLICY_JOB: - switch (eventId) { - case JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START: - return "JOB_MAX_START"; - case JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING: - return "JOB_MAX_RUNNING"; - case JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START: - return "JOB_HIGH_START"; - case JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING: - return "JOB_HIGH_RUNNING"; - case JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START: - return "JOB_DEFAULT_START"; - case JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING: - return "JOB_DEFAULT_RUNNING"; - case JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START: - return "JOB_LOW_START"; - case JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING: - return "JOB_LOW_RUNNING"; - case JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START: - return "JOB_MIN_START"; - case JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING: - return "JOB_MIN_RUNNING"; - case JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT: - return "JOB_TIMEOUT"; - } - break; - } - return "UNKNOWN_ACTION:" + Integer.toHexString(eventId); - } - - @NonNull - static String regulationToString(int eventId) { - switch (eventId) { - case REGULATION_BASIC_INCOME: - return "BASIC_INCOME"; - case REGULATION_BIRTHRIGHT: - return "BIRTHRIGHT"; - case REGULATION_WEALTH_RECLAMATION: - return "WEALTH_RECLAMATION"; - case REGULATION_PROMOTION: - return "PROMOTION"; - case REGULATION_DEMOTION: - return "DEMOTION"; - case REGULATION_BG_RESTRICTED: - return "BG_RESTRICTED"; - case REGULATION_BG_UNRESTRICTED: - return "BG_UNRESTRICTED"; - case REGULATION_FORCE_STOP: - return "FORCE_STOP"; - } - return "UNKNOWN_REGULATION:" + Integer.toHexString(eventId); - } - - @NonNull - static String rewardToString(int eventId) { - switch (eventId) { - case REWARD_TOP_ACTIVITY: - return "REWARD_TOP_ACTIVITY"; - case REWARD_NOTIFICATION_SEEN: - return "REWARD_NOTIFICATION_SEEN"; - case REWARD_NOTIFICATION_INTERACTION: - return "REWARD_NOTIFICATION_INTERACTION"; - case REWARD_WIDGET_INTERACTION: - return "REWARD_WIDGET_INTERACTION"; - case REWARD_OTHER_USER_INTERACTION: - return "REWARD_OTHER_USER_INTERACTION"; - case JobSchedulerEconomicPolicy.REWARD_APP_INSTALL: - return "REWARD_JOB_APP_INSTALL"; - } - return "UNKNOWN_REWARD:" + Integer.toHexString(eventId); - } - - protected long getConstantAsCake(String key, long defaultValCake) { - return getConstantAsCake(key, defaultValCake, 0); - } - - protected long getConstantAsCake(String key, long defaultValCake, long minValCake) { - return Math.max(minValCake, - parseCreditValue( - mUserSettingDeviceConfigMediator.getString(key, null), defaultValCake)); - } - - @VisibleForTesting - static class Injector { - @Nullable - String getSettingsGlobalString(@NonNull ContentResolver resolver, @NonNull String name) { - return Settings.Global.getString(resolver, name); - } - } - - protected static void dumpActiveModifiers(IndentingPrintWriter pw) { - for (int i = 0; i < NUM_COST_MODIFIERS; ++i) { - pw.print("Modifier "); - pw.println(i); - pw.increaseIndent(); - - Modifier modifier = COST_MODIFIER_BY_INDEX[i]; - if (modifier != null) { - modifier.dump(pw); - } else { - pw.println("NOT ACTIVE"); - } - - pw.decreaseIndent(); - } - } - - protected static void dumpAction(IndentingPrintWriter pw, @NonNull Action action) { - pw.print(actionToString(action.id)); - pw.print(": "); - pw.print("ctp="); - pw.print(cakeToString(action.costToProduce)); - pw.print(", basePrice="); - pw.print(cakeToString(action.basePrice)); - pw.println(); - } - - protected static void dumpReward(IndentingPrintWriter pw, @NonNull Reward reward) { - pw.print(rewardToString(reward.id)); - pw.print(": "); - pw.print("instant="); - pw.print(cakeToString(reward.instantReward)); - pw.print(", ongoing/sec="); - pw.print(cakeToString(reward.ongoingRewardPerSecond)); - pw.print(", maxDaily="); - pw.print(cakeToString(reward.maxDailyReward)); - pw.println(); - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java deleted file mode 100644 index 5b305ad91118..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.tare.EconomyManager; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * Interface for the system server to deal with the resource economy subsystem. - * - * @hide - */ -public interface EconomyManagerInternal { - /** - * Used to indicate a future action an app is expected to take. - */ - final class AnticipatedAction { - public final int actionId; - public final int numInstantaneousCalls; - public final long ongoingDurationMs; - private final int mHashCode; - - /** - * @param actionId The expected action - * @param numInstantaneousCalls How many instantaneous times the action will be performed - * @param ongoingDurationMs An estimate of how long the ongoing event will go on for - */ - public AnticipatedAction(@EconomicPolicy.AppAction int actionId, - int numInstantaneousCalls, long ongoingDurationMs) { - this.actionId = actionId; - this.numInstantaneousCalls = numInstantaneousCalls; - this.ongoingDurationMs = ongoingDurationMs; - - int hash = 0; - hash = 31 * hash + actionId; - hash = 31 * hash + numInstantaneousCalls; - hash = 31 * hash + (int) (ongoingDurationMs ^ (ongoingDurationMs >>> 32)); - mHashCode = hash; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AnticipatedAction that = (AnticipatedAction) o; - return actionId == that.actionId - && numInstantaneousCalls == that.numInstantaneousCalls - && ongoingDurationMs == that.ongoingDurationMs; - } - - @Override - public int hashCode() { - return mHashCode; - } - } - - /** - * A collection of {@link AnticipatedAction AnticipatedActions} that will be performed together. - */ - final class ActionBill { - private static final Comparator<AnticipatedAction> - sAnticipatedActionComparator = Comparator.comparingInt(aa -> aa.actionId); - - private final List<AnticipatedAction> mAnticipatedActions; - private final int mHashCode; - - public ActionBill(@NonNull List<AnticipatedAction> anticipatedActions) { - List<AnticipatedAction> actions = new ArrayList<>(anticipatedActions); - actions.sort(sAnticipatedActionComparator); - mAnticipatedActions = Collections.unmodifiableList(actions); - - int hash = 0; - for (int i = 0; i < mAnticipatedActions.size(); ++i) { - hash = 31 * hash + mAnticipatedActions.get(i).hashCode(); - } - mHashCode = hash; - } - - List<AnticipatedAction> getAnticipatedActions() { - return mAnticipatedActions; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ActionBill that = (ActionBill) o; - return mAnticipatedActions.equals(that.mAnticipatedActions); - } - - @Override - public int hashCode() { - return mHashCode; - } - } - - /** Listener for when an app's ability to afford a bill changes. */ - interface AffordabilityChangeListener { - void onAffordabilityChanged(int userId, @NonNull String pkgName, @NonNull ActionBill bill, - boolean canAfford); - } - - /** Listener for various TARE state changes. */ - interface TareStateChangeListener { - void onTareEnabledModeChanged(@EconomyManager.EnabledMode int tareEnabledMode); - } - - /** - * Return {@code true} if the app is able to pay for the anticipated actions. - */ - boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill); - - /** - * Returns the maximum duration (in milliseconds) that the specified app can afford the bill, - * based on current prices. - */ - long getMaxDurationMs(int userId, @NonNull String pkgName, @NonNull ActionBill bill); - - /** Returns the current TARE enabled mode. */ - @EconomyManager.EnabledMode - int getEnabledMode(); - - /** Returns the current TARE enabled mode for the specified policy. */ - @EconomyManager.EnabledMode - int getEnabledMode(@EconomicPolicy.Policy int policyId); - - /** - * Register an {@link AffordabilityChangeListener} to track when an app's ability to afford the - * indicated bill changes. - */ - void registerAffordabilityChangeListener(int userId, @NonNull String pkgName, - @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill); - - /** - * Unregister a {@link AffordabilityChangeListener} from being notified of any changes to an - * app's ability to afford the specified bill. - */ - void unregisterAffordabilityChangeListener(int userId, @NonNull String pkgName, - @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill); - - /** - * Register a {@link TareStateChangeListener} to track when TARE's state changes. - */ - void registerTareStateChangeListener(@NonNull TareStateChangeListener listener, - @EconomicPolicy.Policy int policyId); - - /** - * Unregister a {@link TareStateChangeListener} from being notified when TARE's state changes. - */ - void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener); - - /** - * Note that an instantaneous event has occurred. The event must be specified in one of the - * EconomicPolicies. - * - * @param tag An optional tag that can be used to differentiate the same event for the same app. - */ - void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId, - @Nullable String tag); - - /** - * Note that a long-running event is starting. The event must be specified in one of the - * EconomicPolicies. You must always call - * {@link #noteOngoingEventStopped(int, String, int, String)} to end the event. Ongoing - * events will be separated and grouped by event-tag combinations. There must be an equal - * number of start() and stop() calls for the same event-tag combination in order for the - * tracking to finally stop (ie. ongoing events are ref-counted). - * - * @param tag An optional tag that can be used to differentiate the same event for the same app. - */ - void noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId, - @Nullable String tag); - - /** - * Note that a long-running event has stopped. The event must be specified in one of the - * EconomicPolicies. Ongoing events are separated and grouped by event-tag combinations. - * There must be an equal number of start() and stop() calls for the same event-tag combination - * in order for the tracking to finally stop (ie. ongoing events are ref-counted). - * - * @param tag An optional tag that can be used to differentiate the same event for the same app. - */ - void noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId, - @Nullable String tag); -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java deleted file mode 100644 index 49c6d1b928d7..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.app.AppGlobals; -import android.content.Context; -import android.content.Intent; -import android.content.PermissionChecker; -import android.content.pm.ApplicationInfo; -import android.content.pm.InstallSourceInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.os.RemoteException; - -import com.android.internal.util.ArrayUtils; - -/** POJO to cache only the information about installed packages that TARE cares about. */ -class InstalledPackageInfo { - static final int NO_UID = -1; - - /** - * Flags to use when querying for front door activities. Disabled components are included - * are included for completeness since the app can enable them at any time. - */ - private static final int HEADLESS_APP_QUERY_FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_DISABLED_COMPONENTS; - - public final int uid; - public final String packageName; - public final boolean hasCode; - /** - * Whether the app is a system app that is "headless." Headless in this context means that - * the app doesn't have any "front door" activities --- activities that would show in the - * launcher. - */ - public final boolean isHeadlessSystemApp; - public final boolean isSystemInstaller; - @Nullable - public final String installerPackageName; - - InstalledPackageInfo(@NonNull Context context, @UserIdInt int userId, - @NonNull PackageInfo packageInfo) { - final ApplicationInfo applicationInfo = packageInfo.applicationInfo; - uid = applicationInfo == null ? NO_UID : applicationInfo.uid; - packageName = packageInfo.packageName; - hasCode = applicationInfo != null && applicationInfo.hasCode(); - - final PackageManager packageManager = context.getPackageManager(); - final Intent frontDoorActivityIntent = new Intent(Intent.ACTION_MAIN) - .addCategory(Intent.CATEGORY_LAUNCHER) - .setPackage(packageName); - isHeadlessSystemApp = applicationInfo != null - && (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) - && ArrayUtils.isEmpty( - packageManager.queryIntentActivitiesAsUser( - frontDoorActivityIntent, HEADLESS_APP_QUERY_FLAGS, userId)); - - isSystemInstaller = applicationInfo != null - && ArrayUtils.indexOf( - packageInfo.requestedPermissions, Manifest.permission.INSTALL_PACKAGES) >= 0 - && PackageManager.PERMISSION_GRANTED - == PermissionChecker.checkPermissionForPreflight(context, - Manifest.permission.INSTALL_PACKAGES, PermissionChecker.PID_UNKNOWN, - applicationInfo.uid, packageName); - InstallSourceInfo installSourceInfo = null; - try { - installSourceInfo = AppGlobals.getPackageManager().getInstallSourceInfo(packageName, - userId); - } catch (RemoteException e) { - // Shouldn't happen. - } - installerPackageName = - installSourceInfo == null ? null : installSourceInfo.getInstallingPackageName(); - } - - @Override - public String toString() { - return "IPO{" - + "uid=" + uid - + ", pkgName=" + packageName - + (hasCode ? " HAS_CODE" : "") - + (isHeadlessSystemApp ? " HEADLESS_SYSTEM" : "") - + (isSystemInstaller ? " SYSTEM_INSTALLER" : "") - + ", installer=" + installerPackageName - + '}'; - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java deleted file mode 100644 index 6635484b20b9..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +++ /dev/null @@ -1,1900 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static android.app.tare.EconomyManager.ENABLED_MODE_OFF; -import static android.app.tare.EconomyManager.ENABLED_MODE_ON; -import static android.app.tare.EconomyManager.ENABLED_MODE_SHADOW; -import static android.app.tare.EconomyManager.enabledModeToString; -import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS; -import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS; -import static android.text.format.DateUtils.DAY_IN_MILLIS; -import static android.text.format.DateUtils.HOUR_IN_MILLIS; -import static android.text.format.DateUtils.MINUTE_IN_MILLIS; - -import static com.android.server.tare.TareUtils.appToString; -import static com.android.server.tare.TareUtils.cakeToString; -import static com.android.server.tare.TareUtils.getCurrentTimeMillis; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.AlarmManager; -import android.app.AppOpsManager; -import android.app.tare.EconomyManager; -import android.app.tare.IEconomyManager; -import android.app.usage.UsageEvents; -import android.app.usage.UsageStatsManagerInternal; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.BatteryManager; -import android.os.BatteryManagerInternal; -import android.os.Binder; -import android.os.Handler; -import android.os.IDeviceIdleController; -import android.os.Looper; -import android.os.Message; -import android.os.ParcelFileDescriptor; -import android.os.PowerManager; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.os.UserHandle; -import android.provider.DeviceConfig; -import android.provider.Settings; -import android.util.ArraySet; -import android.util.IndentingPrintWriter; -import android.util.Log; -import android.util.Slog; -import android.util.SparseArrayMap; -import android.util.SparseLongArray; -import android.util.SparseSetArray; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.app.IAppOpsCallback; -import com.android.internal.app.IAppOpsService; -import com.android.internal.os.SomeArgs; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.DumpUtils; -import com.android.server.LocalServices; -import com.android.server.SystemService; -import com.android.server.pm.UserManagerInternal; -import com.android.server.tare.EconomicPolicy.Cost; -import com.android.server.tare.EconomyManagerInternal.TareStateChangeListener; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Responsible for handling app's ARC count based on events, ensuring ARCs are credited when - * appropriate, and reclaiming ARCs at the right times. The IRS deals with the high level details - * while the {@link Agent} deals with the nitty-gritty details. - * - * Note on locking: Any function with the suffix 'Locked' needs to lock on {@link #mLock}. - * - * @hide - */ -public class InternalResourceService extends SystemService { - public static final String TAG = "TARE-IRS"; - public static final boolean DEBUG = Log.isLoggable("TARE", Log.DEBUG); - - static final long UNUSED_RECLAMATION_PERIOD_MS = 24 * HOUR_IN_MILLIS; - /** How much of an app's unused wealth should be reclaimed periodically. */ - private static final float DEFAULT_UNUSED_RECLAMATION_PERCENTAGE = .1f; - /** - * The minimum amount of time an app must not have been used by the user before we start - * periodically reclaiming ARCs from it. - */ - private static final long MIN_UNUSED_TIME_MS = 3 * DAY_IN_MILLIS; - /** The amount of time to delay reclamation by after boot. */ - private static final long RECLAMATION_STARTUP_DELAY_MS = 30_000L; - /** - * The amount of time after TARE has first been set up that a system installer will be allowed - * expanded credit privileges. - */ - static final long INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS = 7 * DAY_IN_MILLIS; - /** - * The amount of time to wait after TARE has first been set up before considering adjusting the - * stock/consumption limit. - */ - private static final long STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS = 5 * DAY_IN_MILLIS; - /** - * The battery level above which we may consider quantitative easing (increasing the consumption - * limit). - */ - private static final int QUANTITATIVE_EASING_BATTERY_THRESHOLD = 50; - /** - * The battery level above which we may consider adjusting the desired stock level. - */ - private static final int STOCK_RECALCULATION_BATTERY_THRESHOLD = 80; - /** - * The amount of time to wait before considering recalculating the desired stock level. - */ - private static final long STOCK_RECALCULATION_DELAY_MS = 16 * HOUR_IN_MILLIS; - /** - * The minimum amount of time we must have background drain for before considering - * recalculating the desired stock level. - */ - private static final long STOCK_RECALCULATION_MIN_DATA_DURATION_MS = 8 * HOUR_IN_MILLIS; - private static final int PACKAGE_QUERY_FLAGS = - PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_APEX | PackageManager.GET_PERMISSIONS; - - /** Global lock for all resource economy state. */ - private final Object mLock = new Object(); - - private final Handler mHandler; - private final BatteryManagerInternal mBatteryManagerInternal; - private final PackageManager mPackageManager; - private final PackageManagerInternal mPackageManagerInternal; - private final UserManagerInternal mUserManagerInternal; - - private IAppOpsService mAppOpsService; - private IDeviceIdleController mDeviceIdleController; - - private final Agent mAgent; - private final Analyst mAnalyst; - private final ConfigObserver mConfigObserver; - private final EconomyManagerStub mEconomyManagerStub; - private final Scribe mScribe; - - @GuardedBy("mLock") - private CompleteEconomicPolicy mCompleteEconomicPolicy; - - @NonNull - @GuardedBy("mLock") - private final SparseArrayMap<String, InstalledPackageInfo> mPkgCache = new SparseArrayMap<>(); - - /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */ - @GuardedBy("mLock") - private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>(); - - /** Cached mapping of userId+package to their UIDs (for all users) */ - @GuardedBy("mPackageToUidCache") - private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>(); - - @GuardedBy("mStateChangeListeners") - private final SparseSetArray<TareStateChangeListener> mStateChangeListeners = - new SparseSetArray<>(); - - /** - * List of packages that are fully restricted and shouldn't be allowed to run in the background. - */ - @GuardedBy("mLock") - private final SparseSetArray<String> mRestrictedApps = new SparseSetArray<>(); - - /** List of packages that are "exempted" from battery restrictions. */ - // TODO(144864180): include userID - @GuardedBy("mLock") - private ArraySet<String> mExemptedApps = new ArraySet<>(); - - @GuardedBy("mLock") - private final SparseArrayMap<String, Boolean> mVipOverrides = new SparseArrayMap<>(); - - /** - * Set of temporary Very Important Packages and when their VIP status ends, in the elapsed - * realtime ({@link android.annotation.ElapsedRealtimeLong}) timebase. - */ - @GuardedBy("mLock") - private final SparseArrayMap<String, Long> mTemporaryVips = new SparseArrayMap<>(); - - /** Set of apps each installer is responsible for installing. */ - @GuardedBy("mLock") - private final SparseArrayMap<String, ArraySet<String>> mInstallers = new SparseArrayMap<>(); - - /** The package name of the wellbeing app. */ - @GuardedBy("mLock") - @Nullable - private String mWellbeingPackage; - - private volatile boolean mHasBattery = true; - @EconomyManager.EnabledMode - private volatile int mEnabledMode; - private volatile int mBootPhase; - private volatile boolean mExemptListLoaded; - // In the range [0,100] to represent 0% to 100% battery. - @GuardedBy("mLock") - private int mCurrentBatteryLevel; - - // TODO(250007395): make configurable per device (via config.xml) - private final int mDefaultTargetBackgroundBatteryLifeHours; - @GuardedBy("mLock") - private int mTargetBackgroundBatteryLifeHours; - - private final IAppOpsCallback mApbListener = new IAppOpsCallback.Stub() { - @Override - public void opChanged(int op, int uid, String packageName, String persistentDeviceId) { - boolean restricted = false; - try { - restricted = mAppOpsService.checkOperation( - AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName) - != AppOpsManager.MODE_ALLOWED; - } catch (RemoteException e) { - // Shouldn't happen - } - final int userId = UserHandle.getUserId(uid); - synchronized (mLock) { - if (restricted) { - if (mRestrictedApps.add(userId, packageName)) { - mAgent.onAppRestrictedLocked(userId, packageName); - } - } else if (mRestrictedApps.remove(UserHandle.getUserId(uid), packageName)) { - mAgent.onAppUnrestrictedLocked(userId, packageName); - } - } - } - }; - - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Nullable - private String getPackageName(Intent intent) { - Uri uri = intent.getData(); - return uri != null ? uri.getSchemeSpecificPart() : null; - } - - @Override - public void onReceive(Context context, Intent intent) { - switch (intent.getAction()) { - case Intent.ACTION_BATTERY_CHANGED: { - final boolean hasBattery = - intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, mHasBattery); - if (mHasBattery != hasBattery) { - mHasBattery = hasBattery; - mConfigObserver.updateEnabledStatus(); - } - } - break; - case Intent.ACTION_BATTERY_LEVEL_CHANGED: - onBatteryLevelChanged(); - break; - case Intent.ACTION_PACKAGE_FULLY_REMOVED: { - final String pkgName = getPackageName(intent); - final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1); - onPackageRemoved(pkgUid, pkgName); - } - break; - case Intent.ACTION_PACKAGE_ADDED: { - if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { - final String pkgName = getPackageName(intent); - final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1); - onPackageAdded(pkgUid, pkgName); - } - } - break; - case Intent.ACTION_PACKAGE_RESTARTED: { - final String pkgName = getPackageName(intent); - final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1); - final int userId = UserHandle.getUserId(pkgUid); - onPackageForceStopped(userId, pkgName); - } - break; - case Intent.ACTION_USER_ADDED: { - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - onUserAdded(userId); - } - break; - case Intent.ACTION_USER_REMOVED: { - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - onUserRemoved(userId); - } - break; - case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED: - onExemptionListChanged(); - break; - } - } - }; - - private final UsageStatsManagerInternal.UsageEventListener mSurveillanceAgent = - new UsageStatsManagerInternal.UsageEventListener() { - /** - * Callback to inform listeners of a new event. - */ - @Override - public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) { - // Skip posting a message to the handler for events we don't care about. - switch (event.getEventType()) { - case UsageEvents.Event.ACTIVITY_RESUMED: - case UsageEvents.Event.ACTIVITY_PAUSED: - case UsageEvents.Event.ACTIVITY_STOPPED: - case UsageEvents.Event.ACTIVITY_DESTROYED: - case UsageEvents.Event.USER_INTERACTION: - case UsageEvents.Event.CHOOSER_ACTION: - case UsageEvents.Event.NOTIFICATION_INTERRUPTION: - case UsageEvents.Event.NOTIFICATION_SEEN: - mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event) - .sendToTarget(); - break; - default: - if (DEBUG) { - Slog.d(TAG, "Dropping event " + event.getEventType()); - } - break; - } - } - }; - - private final AlarmManager.OnAlarmListener mUnusedWealthReclamationListener = - new AlarmManager.OnAlarmListener() { - @Override - public void onAlarm() { - synchronized (mLock) { - mAgent.reclaimUnusedAssetsLocked( - DEFAULT_UNUSED_RECLAMATION_PERCENTAGE, MIN_UNUSED_TIME_MS, false); - mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis()); - scheduleUnusedWealthReclamationLocked(); - } - } - }; - - private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0; - private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1; - private static final int MSG_PROCESS_USAGE_EVENT = 2; - private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 3; - private static final int MSG_NOTIFY_STATE_CHANGE_LISTENER = 4; - private static final int MSG_CLEAN_UP_TEMP_VIP_LIST = 5; - private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*"; - - /** - * Initializes the system service. - * <p> - * Subclasses must define a single argument constructor that accepts the context - * and passes it to super. - * </p> - * - * @param context The system server context. - */ - public InternalResourceService(Context context) { - super(context); - - mHandler = new IrsHandler(TareHandlerThread.get().getLooper()); - mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); - mPackageManager = context.getPackageManager(); - mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); - mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); - mEconomyManagerStub = new EconomyManagerStub(); - mAnalyst = new Analyst(); - mScribe = new Scribe(this, mAnalyst); - mCompleteEconomicPolicy = new CompleteEconomicPolicy(this); - mAgent = new Agent(this, mScribe, mAnalyst); - - mConfigObserver = new ConfigObserver(mHandler, context); - - mDefaultTargetBackgroundBatteryLifeHours = - mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH) - ? 100 // ~ 1.0%/hr - : 40; // ~ 2.5%/hr - mTargetBackgroundBatteryLifeHours = mDefaultTargetBackgroundBatteryLifeHours; - - publishLocalService(EconomyManagerInternal.class, new LocalService()); - } - - @Override - public void onStart() { - publishBinderService(Context.RESOURCE_ECONOMY_SERVICE, mEconomyManagerStub); - } - - @Override - public void onBootPhase(int phase) { - mBootPhase = phase; - - switch (phase) { - case PHASE_SYSTEM_SERVICES_READY: - mAppOpsService = IAppOpsService.Stub.asInterface( - ServiceManager.getService(Context.APP_OPS_SERVICE)); - mDeviceIdleController = IDeviceIdleController.Stub.asInterface( - ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); - mConfigObserver.start(); - onBootPhaseSystemServicesReady(); - break; - case PHASE_THIRD_PARTY_APPS_CAN_START: - onBootPhaseThirdPartyAppsCanStart(); - break; - case PHASE_BOOT_COMPLETED: - onBootPhaseBootCompleted(); - break; - } - } - - @NonNull - Object getLock() { - return mLock; - } - - /** Returns the installed packages for all users. */ - @NonNull - @GuardedBy("mLock") - CompleteEconomicPolicy getCompleteEconomicPolicyLocked() { - return mCompleteEconomicPolicy; - } - - /** Returns the number of apps that this app is expected to update at some point. */ - int getAppUpdateResponsibilityCount(final int userId, @NonNull final String pkgName) { - synchronized (mLock) { - // TODO(248274798): return 0 if the app has lost the install permission - return ArrayUtils.size(mInstallers.get(userId, pkgName)); - } - } - - @NonNull - SparseArrayMap<String, InstalledPackageInfo> getInstalledPackages() { - synchronized (mLock) { - return mPkgCache; - } - } - - /** Returns the installed packages for the specified user. */ - @NonNull - List<InstalledPackageInfo> getInstalledPackages(final int userId) { - final List<InstalledPackageInfo> userPkgs = new ArrayList<>(); - synchronized (mLock) { - final int uIdx = mPkgCache.indexOfKey(userId); - if (uIdx < 0) { - return userPkgs; - } - for (int p = mPkgCache.numElementsForKeyAt(uIdx) - 1; p >= 0; --p) { - final InstalledPackageInfo packageInfo = mPkgCache.valueAt(uIdx, p); - userPkgs.add(packageInfo); - } - } - return userPkgs; - } - - @Nullable - InstalledPackageInfo getInstalledPackageInfo(final int userId, @NonNull final String pkgName) { - synchronized (mLock) { - return mPkgCache.get(userId, pkgName); - } - } - - @GuardedBy("mLock") - long getConsumptionLimitLocked() { - return mCurrentBatteryLevel * mScribe.getSatiatedConsumptionLimitLocked() / 100; - } - - @GuardedBy("mLock") - long getMinBalanceLocked(final int userId, @NonNull final String pkgName) { - return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMinSatiatedBalance(userId, pkgName) - / 100; - } - - @GuardedBy("mLock") - long getInitialSatiatedConsumptionLimitLocked() { - return mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit(); - } - - - long getRealtimeSinceFirstSetupMs() { - return mScribe.getRealtimeSinceFirstSetupMs(SystemClock.elapsedRealtime()); - } - - int getUid(final int userId, @NonNull final String pkgName) { - synchronized (mPackageToUidCache) { - Integer uid = mPackageToUidCache.get(userId, pkgName); - if (uid == null) { - uid = mPackageManagerInternal.getPackageUid(pkgName, 0, userId); - mPackageToUidCache.add(userId, pkgName, uid); - } - return uid; - } - } - - @EconomyManager.EnabledMode - int getEnabledMode() { - return mEnabledMode; - } - - @EconomyManager.EnabledMode - int getEnabledMode(int policyId) { - synchronized (mLock) { - // For now, treat enabled policies as using the same enabled mode as full TARE. - // TODO: have enabled mode by policy - if (mCompleteEconomicPolicy.isPolicyEnabled(policyId)) { - return mEnabledMode; - } - return ENABLED_MODE_OFF; - } - } - - boolean isHeadlessSystemApp(final int userId, @NonNull String pkgName) { - if (pkgName == null) { - Slog.wtfStack(TAG, "isHeadlessSystemApp called with null package"); - return false; - } - synchronized (mLock) { - final InstalledPackageInfo ipo = getInstalledPackageInfo(userId, pkgName); - if (ipo != null && ipo.isHeadlessSystemApp) { - return true; - } - // The wellbeing app is pre-set on the device, not expected to be interacted with - // much by the user, but can be expected to do work in the background on behalf of - // the user. As such, it's a pseudo-headless system app, so treat it as a headless - // system app. - return pkgName.equals(mWellbeingPackage); - } - } - - boolean isPackageExempted(final int userId, @NonNull String pkgName) { - synchronized (mLock) { - return mExemptedApps.contains(pkgName); - } - } - - boolean isPackageRestricted(final int userId, @NonNull String pkgName) { - synchronized (mLock) { - return mRestrictedApps.contains(userId, pkgName); - } - } - - boolean isSystem(final int userId, @NonNull String pkgName) { - if ("android".equals(pkgName)) { - return true; - } - return UserHandle.isCore(getUid(userId, pkgName)); - } - - boolean isVip(final int userId, @NonNull String pkgName) { - return isVip(userId, pkgName, SystemClock.elapsedRealtime()); - } - - boolean isVip(final int userId, @NonNull String pkgName, final long nowElapsed) { - synchronized (mLock) { - final Boolean override = mVipOverrides.get(userId, pkgName); - if (override != null) { - return override; - } - } - if (isSystem(userId, pkgName)) { - // The government, I mean the system, can create ARCs as it needs to in order to - // operate. - return true; - } - synchronized (mLock) { - final Long expirationTimeElapsed = mTemporaryVips.get(userId, pkgName); - if (expirationTimeElapsed != null) { - return nowElapsed <= expirationTimeElapsed; - } - } - return false; - } - - void onBatteryLevelChanged() { - synchronized (mLock) { - final int newBatteryLevel = getCurrentBatteryLevel(); - mAnalyst.noteBatteryLevelChange(newBatteryLevel); - final boolean increased = newBatteryLevel > mCurrentBatteryLevel; - if (increased) { - if (newBatteryLevel >= STOCK_RECALCULATION_BATTERY_THRESHOLD) { - maybeAdjustDesiredStockLevelLocked(); - } - mAgent.distributeBasicIncomeLocked(newBatteryLevel); - } else if (newBatteryLevel == mCurrentBatteryLevel) { - // The broadcast is also sent when the plug type changes... - return; - } - mCurrentBatteryLevel = newBatteryLevel; - adjustCreditSupplyLocked(increased); - } - } - - void onDeviceStateChanged() { - synchronized (mLock) { - mAgent.onDeviceStateChangedLocked(); - } - } - - void onExemptionListChanged() { - final int[] userIds = mUserManagerInternal.getUserIds(); - synchronized (mLock) { - final ArraySet<String> removed = mExemptedApps; - final ArraySet<String> added = new ArraySet<>(); - try { - mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist()); - mExemptListLoaded = true; - } catch (RemoteException e) { - // Shouldn't happen. - } - - for (int i = mExemptedApps.size() - 1; i >= 0; --i) { - final String pkg = mExemptedApps.valueAt(i); - if (!removed.contains(pkg)) { - added.add(pkg); - } - removed.remove(pkg); - } - for (int a = added.size() - 1; a >= 0; --a) { - final String pkgName = added.valueAt(a); - for (int userId : userIds) { - // Since the exemption list doesn't specify user ID and we track by user ID, - // we need to see if the app exists on the user before talking to the agent. - // Otherwise, we may end up with invalid ledgers. - final boolean appExists = getUid(userId, pkgName) >= 0; - if (appExists) { - mAgent.onAppExemptedLocked(userId, pkgName); - } - } - } - for (int r = removed.size() - 1; r >= 0; --r) { - final String pkgName = removed.valueAt(r); - for (int userId : userIds) { - // Since the exemption list doesn't specify user ID and we track by user ID, - // we need to see if the app exists on the user before talking to the agent. - // Otherwise, we may end up with invalid ledgers. - final boolean appExists = getUid(userId, pkgName) >= 0; - if (appExists) { - mAgent.onAppUnexemptedLocked(userId, pkgName); - } - } - } - } - } - - void onPackageAdded(final int uid, @NonNull final String pkgName) { - final int userId = UserHandle.getUserId(uid); - final PackageInfo packageInfo; - try { - packageInfo = - mPackageManager.getPackageInfoAsUser(pkgName, PACKAGE_QUERY_FLAGS, userId); - } catch (PackageManager.NameNotFoundException e) { - Slog.wtf(TAG, "PM couldn't find newly added package: " + pkgName, e); - return; - } - synchronized (mPackageToUidCache) { - mPackageToUidCache.add(userId, pkgName, uid); - } - synchronized (mLock) { - final InstalledPackageInfo ipo = new InstalledPackageInfo(getContext(), userId, - packageInfo); - final InstalledPackageInfo oldIpo = mPkgCache.add(userId, pkgName, ipo); - maybeUpdateInstallerStatusLocked(oldIpo, ipo); - mUidToPackageCache.add(uid, pkgName); - // TODO: only do this when the user first launches the app (app leaves stopped state) - mAgent.grantBirthrightLocked(userId, pkgName); - if (ipo.installerPackageName != null) { - mAgent.noteInstantaneousEventLocked(userId, ipo.installerPackageName, - JobSchedulerEconomicPolicy.REWARD_APP_INSTALL, null); - } - } - } - - void onPackageForceStopped(final int userId, @NonNull final String pkgName) { - synchronized (mLock) { - // Remove all credits if the user force stops the app. It will slowly regain them - // in response to different events. - mAgent.reclaimAllAssetsLocked(userId, pkgName, EconomicPolicy.REGULATION_FORCE_STOP); - } - } - - void onPackageRemoved(final int uid, @NonNull final String pkgName) { - final int userId = UserHandle.getUserId(uid); - synchronized (mPackageToUidCache) { - mPackageToUidCache.delete(userId, pkgName); - } - synchronized (mLock) { - mUidToPackageCache.remove(uid, pkgName); - mVipOverrides.delete(userId, pkgName); - final InstalledPackageInfo ipo = mPkgCache.delete(userId, pkgName); - mInstallers.delete(userId, pkgName); - if (ipo != null && ipo.installerPackageName != null) { - final ArraySet<String> list = mInstallers.get(userId, ipo.installerPackageName); - if (list != null) { - list.remove(pkgName); - } - } - mAgent.onPackageRemovedLocked(userId, pkgName); - } - } - - void onUidStateChanged(final int uid) { - synchronized (mLock) { - final ArraySet<String> pkgNames = getPackagesForUidLocked(uid); - if (pkgNames == null) { - Slog.e(TAG, "Don't have packages for uid " + uid); - } else { - mAgent.onAppStatesChangedLocked(UserHandle.getUserId(uid), pkgNames); - } - } - } - - void onUserAdded(final int userId) { - synchronized (mLock) { - final List<PackageInfo> pkgs = - mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId); - for (int i = pkgs.size() - 1; i >= 0; --i) { - final InstalledPackageInfo ipo = - new InstalledPackageInfo(getContext(), userId, pkgs.get(i)); - final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo); - maybeUpdateInstallerStatusLocked(oldIpo, ipo); - } - mAgent.grantBirthrightsLocked(userId); - final long nowElapsed = SystemClock.elapsedRealtime(); - mScribe.setUserAddedTimeLocked(userId, nowElapsed); - grantInstallersTemporaryVipStatusLocked(userId, - nowElapsed, INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS); - } - } - - void onUserRemoved(final int userId) { - synchronized (mLock) { - mVipOverrides.delete(userId); - final int uIdx = mPkgCache.indexOfKey(userId); - if (uIdx >= 0) { - for (int p = mPkgCache.numElementsForKeyAt(uIdx) - 1; p >= 0; --p) { - final InstalledPackageInfo pkgInfo = mPkgCache.valueAt(uIdx, p); - mUidToPackageCache.remove(pkgInfo.uid); - } - } - mInstallers.delete(userId); - mPkgCache.delete(userId); - mAgent.onUserRemovedLocked(userId); - mScribe.onUserRemovedLocked(userId); - } - } - - /** - * Try to increase the consumption limit if apps are reaching the current limit too quickly. - */ - @GuardedBy("mLock") - void maybePerformQuantitativeEasingLocked() { - if (mConfigObserver.ENABLE_TIP3) { - maybeAdjustDesiredStockLevelLocked(); - return; - } - if (getRealtimeSinceFirstSetupMs() < STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS) { - // Things can be very tumultuous soon after first setup. - return; - } - // We don't need to increase the limit if the device runs out of consumable credits - // when the battery is low. - final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked(); - if (mCurrentBatteryLevel <= QUANTITATIVE_EASING_BATTERY_THRESHOLD - || remainingConsumableCakes > 0) { - return; - } - final long currentConsumptionLimit = mScribe.getSatiatedConsumptionLimitLocked(); - final long shortfall = (mCurrentBatteryLevel - QUANTITATIVE_EASING_BATTERY_THRESHOLD) - * currentConsumptionLimit / 100; - final long newConsumptionLimit = Math.min(currentConsumptionLimit + shortfall, - mCompleteEconomicPolicy.getMaxSatiatedConsumptionLimit()); - if (newConsumptionLimit != currentConsumptionLimit) { - Slog.i(TAG, "Increasing consumption limit from " + cakeToString(currentConsumptionLimit) - + " to " + cakeToString(newConsumptionLimit)); - mScribe.setConsumptionLimitLocked(newConsumptionLimit); - adjustCreditSupplyLocked(/* allowIncrease */ true); - } - } - - /** - * Adjust the consumption limit based on historical data and the target battery drain. - */ - @GuardedBy("mLock") - void maybeAdjustDesiredStockLevelLocked() { - if (!mConfigObserver.ENABLE_TIP3) { - return; - } - if (getRealtimeSinceFirstSetupMs() < STOCK_ADJUSTMENT_FIRST_SETUP_GRACE_PERIOD_MS) { - // Things can be very tumultuous soon after first setup. - return; - } - // Don't adjust the limit too often or while the battery is low. - final long now = getCurrentTimeMillis(); - if ((now - mScribe.getLastStockRecalculationTimeLocked()) < STOCK_RECALCULATION_DELAY_MS - || mCurrentBatteryLevel <= STOCK_RECALCULATION_BATTERY_THRESHOLD) { - return; - } - - // For now, use screen off battery drain as a proxy for background battery drain. - // TODO: get more accurate background battery drain numbers - final long totalScreenOffDurationMs = mAnalyst.getBatteryScreenOffDurationMs(); - if (totalScreenOffDurationMs < STOCK_RECALCULATION_MIN_DATA_DURATION_MS) { - return; - } - final long totalDischargeMah = mAnalyst.getBatteryScreenOffDischargeMah(); - if (totalDischargeMah == 0) { - Slog.i(TAG, "Total discharge was 0"); - return; - } - final long batteryCapacityMah = mBatteryManagerInternal.getBatteryFullCharge() / 1000; - final long estimatedLifeHours = batteryCapacityMah * totalScreenOffDurationMs - / totalDischargeMah / HOUR_IN_MILLIS; - final long percentageOfTarget = - 100 * estimatedLifeHours / mTargetBackgroundBatteryLifeHours; - if (DEBUG) { - Slog.d(TAG, "maybeAdjustDesiredStockLevelLocked:" - + " screenOffMs=" + totalScreenOffDurationMs - + " dischargeMah=" + totalDischargeMah - + " capacityMah=" + batteryCapacityMah - + " estimatedLifeHours=" + estimatedLifeHours - + " %ofTarget=" + percentageOfTarget); - } - final long currentConsumptionLimit = mScribe.getSatiatedConsumptionLimitLocked(); - final long newConsumptionLimit; - if (percentageOfTarget > 105) { - // The stock is too low. We're doing pretty well. We can increase the stock slightly - // to let apps do more work in the background. - newConsumptionLimit = Math.min((long) (currentConsumptionLimit * 1.01), - mCompleteEconomicPolicy.getMaxSatiatedConsumptionLimit()); - } else if (percentageOfTarget < 100) { - // The stock is too high IMO. We're below the target. Decrease the stock to reduce - // background work. - newConsumptionLimit = Math.max((long) (currentConsumptionLimit * .98), - mCompleteEconomicPolicy.getMinSatiatedConsumptionLimit()); - } else { - // The stock is just right. - return; - } - // TODO(250007191): calculate and log implied service level - if (newConsumptionLimit != currentConsumptionLimit) { - Slog.i(TAG, "Adjusting consumption limit from " + cakeToString(currentConsumptionLimit) - + " to " + cakeToString(newConsumptionLimit) - + " because drain was " + percentageOfTarget + "% of target"); - mScribe.setConsumptionLimitLocked(newConsumptionLimit); - adjustCreditSupplyLocked(/* allowIncrease */ true); - mScribe.setLastStockRecalculationTimeLocked(now); - } - } - - void postAffordabilityChanged(final int userId, @NonNull final String pkgName, - @NonNull Agent.ActionAffordabilityNote affordabilityNote) { - if (DEBUG) { - Slog.d(TAG, userId + ":" + pkgName + " affordability changed to " - + affordabilityNote.isCurrentlyAffordable()); - } - final SomeArgs args = SomeArgs.obtain(); - args.argi1 = userId; - args.arg1 = pkgName; - args.arg2 = affordabilityNote; - mHandler.obtainMessage(MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER, args).sendToTarget(); - } - - @GuardedBy("mLock") - private void adjustCreditSupplyLocked(boolean allowIncrease) { - final long newLimit = getConsumptionLimitLocked(); - final long remainingConsumableCakes = mScribe.getRemainingConsumableCakesLocked(); - if (remainingConsumableCakes == newLimit) { - return; - } - if (remainingConsumableCakes > newLimit) { - mScribe.adjustRemainingConsumableCakesLocked(newLimit - remainingConsumableCakes); - } else if (allowIncrease) { - final double perc = mCurrentBatteryLevel / 100d; - final long shortfall = newLimit - remainingConsumableCakes; - mScribe.adjustRemainingConsumableCakesLocked((long) (perc * shortfall)); - } - mAgent.onCreditSupplyChanged(); - } - - @GuardedBy("mLock") - private void grantInstallersTemporaryVipStatusLocked(int userId, long nowElapsed, - long grantDurationMs) { - final long grantEndTimeElapsed = nowElapsed + grantDurationMs; - final int uIdx = mPkgCache.indexOfKey(userId); - if (uIdx < 0) { - return; - } - for (int pIdx = mPkgCache.numElementsForKey(uIdx) - 1; pIdx >= 0; --pIdx) { - final InstalledPackageInfo ipo = mPkgCache.valueAt(uIdx, pIdx); - - if (ipo.isSystemInstaller) { - final Long currentGrantEndTimeElapsed = mTemporaryVips.get(userId, ipo.packageName); - if (currentGrantEndTimeElapsed == null - || currentGrantEndTimeElapsed < grantEndTimeElapsed) { - mTemporaryVips.add(userId, ipo.packageName, grantEndTimeElapsed); - } - } - } - mHandler.sendEmptyMessageDelayed(MSG_CLEAN_UP_TEMP_VIP_LIST, grantDurationMs); - } - - @GuardedBy("mLock") - private void processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event) { - if (mEnabledMode == ENABLED_MODE_OFF) { - return; - } - final String pkgName = event.getPackageName(); - if (DEBUG) { - Slog.d(TAG, "Processing event " + event.getEventType() - + " (" + event.mInstanceId + ")" - + " for " + appToString(userId, pkgName)); - } - final long nowElapsed = SystemClock.elapsedRealtime(); - switch (event.getEventType()) { - case UsageEvents.Event.ACTIVITY_RESUMED: - mAgent.noteOngoingEventLocked(userId, pkgName, - EconomicPolicy.REWARD_TOP_ACTIVITY, String.valueOf(event.mInstanceId), - nowElapsed); - break; - case UsageEvents.Event.ACTIVITY_PAUSED: - case UsageEvents.Event.ACTIVITY_STOPPED: - case UsageEvents.Event.ACTIVITY_DESTROYED: - final long now = getCurrentTimeMillis(); - mAgent.stopOngoingActionLocked(userId, pkgName, - EconomicPolicy.REWARD_TOP_ACTIVITY, String.valueOf(event.mInstanceId), - nowElapsed, now); - break; - case UsageEvents.Event.USER_INTERACTION: - case UsageEvents.Event.CHOOSER_ACTION: - mAgent.noteInstantaneousEventLocked(userId, pkgName, - EconomicPolicy.REWARD_OTHER_USER_INTERACTION, null); - break; - case UsageEvents.Event.NOTIFICATION_INTERRUPTION: - case UsageEvents.Event.NOTIFICATION_SEEN: - mAgent.noteInstantaneousEventLocked(userId, pkgName, - EconomicPolicy.REWARD_NOTIFICATION_SEEN, null); - break; - } - } - - @GuardedBy("mLock") - private void scheduleUnusedWealthReclamationLocked() { - final long now = getCurrentTimeMillis(); - final long nextReclamationTime = Math.max(now + RECLAMATION_STARTUP_DELAY_MS, - mScribe.getLastReclamationTimeLocked() + UNUSED_RECLAMATION_PERIOD_MS); - mHandler.post(() -> { - // Never call out to AlarmManager with the lock held. This sits below AM. - AlarmManager alarmManager = getContext().getSystemService(AlarmManager.class); - if (alarmManager != null) { - alarmManager.setWindow(AlarmManager.ELAPSED_REALTIME, - SystemClock.elapsedRealtime() + (nextReclamationTime - now), - 30 * MINUTE_IN_MILLIS, - ALARM_TAG_WEALTH_RECLAMATION, mUnusedWealthReclamationListener, mHandler); - } else { - mHandler.sendEmptyMessageDelayed( - MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT, RECLAMATION_STARTUP_DELAY_MS); - } - }); - } - - private int getCurrentBatteryLevel() { - return mBatteryManagerInternal.getBatteryLevel(); - } - - @Nullable - @GuardedBy("mLock") - private ArraySet<String> getPackagesForUidLocked(final int uid) { - ArraySet<String> packages = mUidToPackageCache.get(uid); - if (packages == null) { - final String[] pkgs = mPackageManager.getPackagesForUid(uid); - if (pkgs != null) { - for (String pkg : pkgs) { - mUidToPackageCache.add(uid, pkg); - } - packages = mUidToPackageCache.get(uid); - } - } - return packages; - } - - private boolean isTareSupported() { - // TARE is presently designed for devices with batteries. Don't enable it on - // battery-less devices for now. - return mHasBattery; - } - - @GuardedBy("mLock") - private void loadInstalledPackageListLocked() { - mPkgCache.clear(); - final int[] userIds = mUserManagerInternal.getUserIds(); - for (int userId : userIds) { - final List<PackageInfo> pkgs = - mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId); - for (int i = pkgs.size() - 1; i >= 0; --i) { - final InstalledPackageInfo ipo = - new InstalledPackageInfo(getContext(), userId, pkgs.get(i)); - final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo); - maybeUpdateInstallerStatusLocked(oldIpo, ipo); - } - } - } - - /** - * Used to update the set of installed apps for each installer. This only has an effect if the - * installer package name is different between {@code oldIpo} and {@code newIpo}. - */ - @GuardedBy("mLock") - private void maybeUpdateInstallerStatusLocked(@Nullable InstalledPackageInfo oldIpo, - @NonNull InstalledPackageInfo newIpo) { - final boolean changed; - if (oldIpo == null) { - changed = newIpo.installerPackageName != null; - } else { - changed = !Objects.equals(oldIpo.installerPackageName, newIpo.installerPackageName); - } - if (!changed) { - return; - } - // InstallSourceInfo doesn't track userId, so for now, assume the installer on the package's - // user profile did the installation. - // TODO(246640162): use the actual installer's user ID - final int userId = UserHandle.getUserId(newIpo.uid); - final String pkgName = newIpo.packageName; - if (oldIpo != null) { - final ArraySet<String> oldList = mInstallers.get(userId, oldIpo.installerPackageName); - if (oldList != null) { - oldList.remove(pkgName); - } - } - if (newIpo.installerPackageName != null) { - ArraySet<String> newList = mInstallers.get(userId, newIpo.installerPackageName); - if (newList == null) { - newList = new ArraySet<>(); - mInstallers.add(userId, newIpo.installerPackageName, newList); - } - newList.add(pkgName); - } - } - - private void registerListeners() { - final IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_BATTERY_CHANGED); - filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED); - filter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); - getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); - - final IntentFilter pkgFilter = new IntentFilter(); - pkgFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); - pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); - pkgFilter.addDataScheme("package"); - getContext() - .registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, pkgFilter, null, null); - - final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); - userFilter.addAction(Intent.ACTION_USER_ADDED); - getContext() - .registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); - - UsageStatsManagerInternal usmi = LocalServices.getService(UsageStatsManagerInternal.class); - usmi.registerListener(mSurveillanceAgent); - - try { - mAppOpsService - .startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null, mApbListener); - } catch (RemoteException e) { - // shouldn't happen. - } - } - - /** Perform long-running and/or heavy setup work. This should be called off the main thread. */ - private void setupHeavyWork() { - if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || mEnabledMode == ENABLED_MODE_OFF) { - return; - } - synchronized (mLock) { - mCompleteEconomicPolicy.setup(mConfigObserver.getAllDeviceConfigProperties()); - loadInstalledPackageListLocked(); - final SparseLongArray timeSinceUsersAdded; - final boolean isFirstSetup = !mScribe.recordExists(); - final long nowElapsed = SystemClock.elapsedRealtime(); - if (isFirstSetup) { - mAgent.grantBirthrightsLocked(); - mScribe.setConsumptionLimitLocked( - mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()); - // Set the last reclamation time to now so we don't start reclaiming assets - // too early. - mScribe.setLastReclamationTimeLocked(getCurrentTimeMillis()); - timeSinceUsersAdded = new SparseLongArray(); - } else { - mScribe.loadFromDiskLocked(); - if (mScribe.getSatiatedConsumptionLimitLocked() - < mCompleteEconomicPolicy.getMinSatiatedConsumptionLimit() - || mScribe.getSatiatedConsumptionLimitLocked() - > mCompleteEconomicPolicy.getMaxSatiatedConsumptionLimit()) { - // Reset the consumption limit since several factors may have changed. - mScribe.setConsumptionLimitLocked( - mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()); - } else { - // Adjust the supply in case battery level changed while the device was off. - adjustCreditSupplyLocked(true); - } - timeSinceUsersAdded = mScribe.getRealtimeSinceUsersAddedLocked(nowElapsed); - } - - final int[] userIds = mUserManagerInternal.getUserIds(); - for (int userId : userIds) { - final long timeSinceUserAddedMs = timeSinceUsersAdded.get(userId, 0); - // Temporarily mark installers as VIPs so they aren't subject to credit - // limits and policies on first boot. - if (timeSinceUserAddedMs < INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS) { - final long remainingGraceDurationMs = - INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS - timeSinceUserAddedMs; - - grantInstallersTemporaryVipStatusLocked(userId, nowElapsed, - remainingGraceDurationMs); - } - } - scheduleUnusedWealthReclamationLocked(); - } - } - - private void onBootPhaseSystemServicesReady() { - if (mBootPhase < PHASE_SYSTEM_SERVICES_READY || mEnabledMode == ENABLED_MODE_OFF) { - return; - } - synchronized (mLock) { - registerListeners(); - // As of Android UDC, users can't change the wellbeing package, so load it once - // as soon as possible and don't bother trying to update it afterwards. - mWellbeingPackage = mPackageManager.getWellbeingPackageName(); - mCurrentBatteryLevel = getCurrentBatteryLevel(); - // Get the current battery presence, if available. This would succeed if TARE is - // toggled long after boot. - final Intent batteryStatus = getContext().registerReceiver(null, - new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - if (batteryStatus != null) { - final boolean hasBattery = - batteryStatus.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true); - if (mHasBattery != hasBattery) { - mHasBattery = hasBattery; - mConfigObserver.updateEnabledStatus(); - } - } - } - } - - private void onBootPhaseThirdPartyAppsCanStart() { - if (mBootPhase < PHASE_THIRD_PARTY_APPS_CAN_START || mEnabledMode == ENABLED_MODE_OFF) { - return; - } - mHandler.post(this::setupHeavyWork); - } - - private void onBootPhaseBootCompleted() { - if (mBootPhase < PHASE_BOOT_COMPLETED || mEnabledMode == ENABLED_MODE_OFF) { - return; - } - synchronized (mLock) { - if (!mExemptListLoaded) { - try { - mExemptedApps = new ArraySet<>(mDeviceIdleController.getFullPowerWhitelist()); - mExemptListLoaded = true; - } catch (RemoteException e) { - // Shouldn't happen. - } - } - } - } - - private void setupEverything() { - if (mEnabledMode == ENABLED_MODE_OFF) { - return; - } - if (mBootPhase >= PHASE_SYSTEM_SERVICES_READY) { - onBootPhaseSystemServicesReady(); - } - if (mBootPhase >= PHASE_THIRD_PARTY_APPS_CAN_START) { - onBootPhaseThirdPartyAppsCanStart(); - } - if (mBootPhase >= PHASE_BOOT_COMPLETED) { - onBootPhaseBootCompleted(); - } - } - - private void tearDownEverything() { - if (mEnabledMode != ENABLED_MODE_OFF) { - return; - } - synchronized (mLock) { - mAgent.tearDownLocked(); - mAnalyst.tearDown(); - mCompleteEconomicPolicy.tearDown(); - mExemptedApps.clear(); - mExemptListLoaded = false; - mHandler.post(() -> { - // Never call out to AlarmManager with the lock held. This sits below AM. - AlarmManager alarmManager = getContext().getSystemService(AlarmManager.class); - if (alarmManager != null) { - alarmManager.cancel(mUnusedWealthReclamationListener); - } - }); - mPkgCache.clear(); - mScribe.tearDownLocked(); - mUidToPackageCache.clear(); - getContext().unregisterReceiver(mBroadcastReceiver); - UsageStatsManagerInternal usmi = - LocalServices.getService(UsageStatsManagerInternal.class); - usmi.unregisterListener(mSurveillanceAgent); - try { - mAppOpsService.stopWatchingMode(mApbListener); - } catch (RemoteException e) { - // shouldn't happen. - } - } - synchronized (mPackageToUidCache) { - mPackageToUidCache.clear(); - } - } - - private final class IrsHandler extends Handler { - IrsHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_CLEAN_UP_TEMP_VIP_LIST: { - removeMessages(MSG_CLEAN_UP_TEMP_VIP_LIST); - - synchronized (mLock) { - final long nowElapsed = SystemClock.elapsedRealtime(); - - long earliestExpiration = Long.MAX_VALUE; - for (int u = 0; u < mTemporaryVips.numMaps(); ++u) { - final int userId = mTemporaryVips.keyAt(u); - - for (int p = mTemporaryVips.numElementsForKeyAt(u) - 1; p >= 0; --p) { - final String pkgName = mTemporaryVips.keyAt(u, p); - final Long expiration = mTemporaryVips.valueAt(u, p); - - if (expiration == null || expiration < nowElapsed) { - mTemporaryVips.delete(userId, pkgName); - } else { - earliestExpiration = Math.min(earliestExpiration, expiration); - } - } - } - - if (earliestExpiration < Long.MAX_VALUE) { - sendEmptyMessageDelayed(MSG_CLEAN_UP_TEMP_VIP_LIST, - earliestExpiration - nowElapsed); - } - } - } - break; - - case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: { - final SomeArgs args = (SomeArgs) msg.obj; - final int userId = args.argi1; - final String pkgName = (String) args.arg1; - final Agent.ActionAffordabilityNote affordabilityNote = - (Agent.ActionAffordabilityNote) args.arg2; - - final EconomyManagerInternal.AffordabilityChangeListener listener = - affordabilityNote.getListener(); - listener.onAffordabilityChanged(userId, pkgName, - affordabilityNote.getActionBill(), - affordabilityNote.isCurrentlyAffordable()); - - args.recycle(); - } - break; - - case MSG_NOTIFY_STATE_CHANGE_LISTENER: { - final int policy = msg.arg1; - final TareStateChangeListener listener = (TareStateChangeListener) msg.obj; - listener.onTareEnabledModeChanged(getEnabledMode(policy)); - } - break; - - case MSG_NOTIFY_STATE_CHANGE_LISTENERS: { - final int changedPolicies = msg.arg1; - synchronized (mStateChangeListeners) { - final int size = mStateChangeListeners.size(); - for (int l = 0; l < size; ++l) { - final int policy = mStateChangeListeners.keyAt(l); - if ((policy & changedPolicies) == 0) { - continue; - } - final ArraySet<TareStateChangeListener> listeners = - mStateChangeListeners.get(policy); - final int enabledMode = getEnabledMode(policy); - for (int p = listeners.size() - 1; p >= 0; --p) { - final TareStateChangeListener listener = listeners.valueAt(p); - listener.onTareEnabledModeChanged(enabledMode); - } - } - } - } - break; - - case MSG_PROCESS_USAGE_EVENT: { - final int userId = msg.arg1; - final UsageEvents.Event event = (UsageEvents.Event) msg.obj; - synchronized (mLock) { - processUsageEventLocked(userId, event); - } - } - break; - - case MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT: { - removeMessages(MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT); - synchronized (mLock) { - scheduleUnusedWealthReclamationLocked(); - } - } - break; - } - } - } - - /** - * Binder stub trampoline implementation - */ - final class EconomyManagerStub extends IEconomyManager.Stub { - /** - * "dumpsys" infrastructure - */ - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return; - - boolean dumpAll = true; - if (!ArrayUtils.isEmpty(args)) { - String arg = args[0]; - if ("-h".equals(arg) || "--help".equals(arg)) { - dumpHelp(pw); - return; - } else if ("-a".equals(arg)) { - // -a is passed when dumping a bug report. Bug reports have a time limit for - // each service dump, so we can't dump everything. - dumpAll = false; - } else if (arg.length() > 0 && arg.charAt(0) == '-') { - pw.println("Unknown option: " + arg); - return; - } - } - - final long identityToken = Binder.clearCallingIdentity(); - try { - dumpInternal(new IndentingPrintWriter(pw, " "), dumpAll); - } finally { - Binder.restoreCallingIdentity(identityToken); - } - } - - @Override - @EconomyManager.EnabledMode - public int getEnabledMode() { - return InternalResourceService.this.getEnabledMode(); - } - - @Override - public int handleShellCommand(@NonNull ParcelFileDescriptor in, - @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, - @NonNull String[] args) { - return (new TareShellCommand(InternalResourceService.this)).exec( - this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), - args); - } - } - - private final class LocalService implements EconomyManagerInternal { - /** - * Use an extremely large value to indicate that an app can pay for a bill indefinitely. - * The value set here should be large/long enough that there's no reasonable expectation - * of a device operating uninterrupted (or in the exact same state) for that period of time. - * We intentionally don't use Long.MAX_VALUE to avoid potential overflow if a client - * doesn't check the value and just immediately adds it to the current time. - */ - private static final long FOREVER_MS = 27 * 365 * 24 * HOUR_IN_MILLIS; - - @Override - public void registerAffordabilityChangeListener(int userId, @NonNull String pkgName, - @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill) { - if (!isTareSupported() || isSystem(userId, pkgName)) { - // The system's affordability never changes. - return; - } - synchronized (mLock) { - mAgent.registerAffordabilityChangeListenerLocked(userId, pkgName, listener, bill); - } - } - - @Override - public void unregisterAffordabilityChangeListener(int userId, @NonNull String pkgName, - @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill) { - if (isSystem(userId, pkgName)) { - // The system's affordability never changes. - return; - } - synchronized (mLock) { - mAgent.unregisterAffordabilityChangeListenerLocked(userId, pkgName, listener, bill); - } - } - - @Override - public void registerTareStateChangeListener(@NonNull TareStateChangeListener listener, - int policyId) { - if (!isTareSupported()) { - return; - } - synchronized (mStateChangeListeners) { - if (mStateChangeListeners.add(policyId, listener)) { - mHandler.obtainMessage(MSG_NOTIFY_STATE_CHANGE_LISTENER, policyId, 0, listener) - .sendToTarget(); - } - } - } - - @Override - public void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener) { - synchronized (mStateChangeListeners) { - for (int i = mStateChangeListeners.size() - 1; i >= 0; --i) { - final ArraySet<TareStateChangeListener> listeners = - mStateChangeListeners.get(mStateChangeListeners.keyAt(i)); - listeners.remove(listener); - } - } - } - - @Override - public boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill) { - if (mEnabledMode == ENABLED_MODE_OFF) { - return true; - } - if (isVip(userId, pkgName)) { - // The government, I mean the system, can create ARCs as it needs to in order to - // allow VIPs to operate. - return true; - } - // TODO: take temp-allowlist into consideration - long requiredBalance = 0; - final List<EconomyManagerInternal.AnticipatedAction> projectedActions = - bill.getAnticipatedActions(); - synchronized (mLock) { - for (int i = 0; i < projectedActions.size(); ++i) { - AnticipatedAction action = projectedActions.get(i); - final Cost cost = mCompleteEconomicPolicy.getCostOfAction( - action.actionId, userId, pkgName); - requiredBalance += cost.price * action.numInstantaneousCalls - + cost.price * (action.ongoingDurationMs / 1000); - } - return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance - && mScribe.getRemainingConsumableCakesLocked() >= requiredBalance; - } - } - - @Override - public long getMaxDurationMs(int userId, @NonNull String pkgName, - @NonNull ActionBill bill) { - if (mEnabledMode == ENABLED_MODE_OFF) { - return FOREVER_MS; - } - if (isVip(userId, pkgName)) { - return FOREVER_MS; - } - long totalCostPerSecond = 0; - final List<EconomyManagerInternal.AnticipatedAction> projectedActions = - bill.getAnticipatedActions(); - synchronized (mLock) { - for (int i = 0; i < projectedActions.size(); ++i) { - AnticipatedAction action = projectedActions.get(i); - final Cost cost = mCompleteEconomicPolicy.getCostOfAction( - action.actionId, userId, pkgName); - totalCostPerSecond += cost.price; - } - if (totalCostPerSecond == 0) { - return FOREVER_MS; - } - final long minBalance = Math.min( - mAgent.getBalanceLocked(userId, pkgName), - mScribe.getRemainingConsumableCakesLocked()); - return minBalance * 1000 / totalCostPerSecond; - } - } - - @Override - public int getEnabledMode() { - return mEnabledMode; - } - - @Override - public int getEnabledMode(int policyId) { - return InternalResourceService.this.getEnabledMode(policyId); - } - - @Override - public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId, - @Nullable String tag) { - if (mEnabledMode == ENABLED_MODE_OFF) { - return; - } - synchronized (mLock) { - mAgent.noteInstantaneousEventLocked(userId, pkgName, eventId, tag); - } - } - - @Override - public void noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId, - @Nullable String tag) { - if (mEnabledMode == ENABLED_MODE_OFF) { - return; - } - synchronized (mLock) { - final long nowElapsed = SystemClock.elapsedRealtime(); - mAgent.noteOngoingEventLocked(userId, pkgName, eventId, tag, nowElapsed); - } - } - - @Override - public void noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId, - @Nullable String tag) { - if (mEnabledMode == ENABLED_MODE_OFF) { - return; - } - final long nowElapsed = SystemClock.elapsedRealtime(); - final long now = getCurrentTimeMillis(); - synchronized (mLock) { - mAgent.stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now); - } - } - } - - private class ConfigObserver extends ContentObserver - implements DeviceConfig.OnPropertiesChangedListener { - private static final String KEY_ENABLE_TIP3 = "enable_tip3"; - private static final String KEY_TARGET_BACKGROUND_BATTERY_LIFE_HOURS = - "target_bg_battery_life_hrs"; - - private static final boolean DEFAULT_ENABLE_TIP3 = true; - - /** Use a target background battery drain rate to determine consumption limits. */ - public boolean ENABLE_TIP3 = DEFAULT_ENABLE_TIP3; - - private final ContentResolver mContentResolver; - - ConfigObserver(Handler handler, Context context) { - super(handler); - mContentResolver = context.getContentResolver(); - } - - public void start() { - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_TARE, - TareHandlerThread.getExecutor(), this); - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this); - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS), false, this); - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS), false, this); - 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))) { - updateEnabledStatus(); - } else if (uri.equals(Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS)) - || uri.equals(Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS))) { - updateEconomicPolicy(); - } - } - - @Override - public void onPropertiesChanged(DeviceConfig.Properties properties) { - boolean economicPolicyUpdated = false; - synchronized (mLock) { - for (String name : properties.getKeyset()) { - if (name == null) { - continue; - } - switch (name) { - case EconomyManager.KEY_ENABLE_TARE_MODE: - updateEnabledStatus(); - break; - case KEY_ENABLE_TIP3: - ENABLE_TIP3 = properties.getBoolean(name, DEFAULT_ENABLE_TIP3); - break; - case KEY_TARGET_BACKGROUND_BATTERY_LIFE_HOURS: - synchronized (mLock) { - mTargetBackgroundBatteryLifeHours = properties.getInt(name, - mDefaultTargetBackgroundBatteryLifeHours); - maybeAdjustDesiredStockLevelLocked(); - } - break; - default: - if (!economicPolicyUpdated - && (name.startsWith("am") || name.startsWith("js") - || name.startsWith("enable_policy"))) { - updateEconomicPolicy(); - economicPolicyUpdated = true; - } - } - } - } - } - - private void updateEnabledStatus() { - // User setting should override DeviceConfig setting. - final int tareEnabledModeDC = DeviceConfig.getInt(DeviceConfig.NAMESPACE_TARE, - EconomyManager.KEY_ENABLE_TARE_MODE, EconomyManager.DEFAULT_ENABLE_TARE_MODE); - final int tareEnabledModeConfig = isTareSupported() - ? Settings.Global.getInt(mContentResolver, - Settings.Global.ENABLE_TARE, tareEnabledModeDC) - : ENABLED_MODE_OFF; - final int enabledMode; - if (tareEnabledModeConfig == ENABLED_MODE_OFF - || tareEnabledModeConfig == ENABLED_MODE_ON - || tareEnabledModeConfig == ENABLED_MODE_SHADOW) { - // Config has a valid enabled mode. - enabledMode = tareEnabledModeConfig; - } else { - enabledMode = EconomyManager.DEFAULT_ENABLE_TARE_MODE; - } - if (mEnabledMode != enabledMode) { - // A full change where we've gone from OFF to {SHADOW or ON}, or vie versa. - // With this transition, we'll have to set up or tear down. - final boolean fullEnableChange = - mEnabledMode == ENABLED_MODE_OFF || enabledMode == ENABLED_MODE_OFF; - mEnabledMode = enabledMode; - if (fullEnableChange) { - if (mEnabledMode != ENABLED_MODE_OFF) { - setupEverything(); - } else { - tearDownEverything(); - } - } - mHandler.obtainMessage( - MSG_NOTIFY_STATE_CHANGE_LISTENERS, EconomicPolicy.ALL_POLICIES, 0) - .sendToTarget(); - } - } - - private void updateEconomicPolicy() { - synchronized (mLock) { - final long minLimit = mCompleteEconomicPolicy.getMinSatiatedConsumptionLimit(); - final long maxLimit = mCompleteEconomicPolicy.getMaxSatiatedConsumptionLimit(); - final int oldEnabledPolicies = mCompleteEconomicPolicy.getEnabledPolicyIds(); - mCompleteEconomicPolicy.tearDown(); - mCompleteEconomicPolicy = new CompleteEconomicPolicy(InternalResourceService.this); - if (mEnabledMode != ENABLED_MODE_OFF - && mBootPhase >= PHASE_THIRD_PARTY_APPS_CAN_START) { - mCompleteEconomicPolicy.setup(getAllDeviceConfigProperties()); - if (minLimit != mCompleteEconomicPolicy.getMinSatiatedConsumptionLimit() - || maxLimit - != mCompleteEconomicPolicy.getMaxSatiatedConsumptionLimit()) { - // Reset the consumption limit since several factors may have changed. - mScribe.setConsumptionLimitLocked( - mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit()); - } - mAgent.onPricingChangedLocked(); - final int newEnabledPolicies = mCompleteEconomicPolicy.getEnabledPolicyIds(); - if (oldEnabledPolicies != newEnabledPolicies) { - final int changedPolicies = oldEnabledPolicies ^ newEnabledPolicies; - mHandler.obtainMessage( - MSG_NOTIFY_STATE_CHANGE_LISTENERS, changedPolicies, 0) - .sendToTarget(); - } - } - } - } - } - - // Shell command infrastructure - int executeClearVip(@NonNull PrintWriter pw) { - synchronized (mLock) { - final SparseSetArray<String> changedPkgs = new SparseSetArray<>(); - for (int u = mVipOverrides.numMaps() - 1; u >= 0; --u) { - final int userId = mVipOverrides.keyAt(u); - - for (int p = mVipOverrides.numElementsForKeyAt(u) - 1; p >= 0; --p) { - changedPkgs.add(userId, mVipOverrides.keyAt(u, p)); - } - } - mVipOverrides.clear(); - if (mEnabledMode != ENABLED_MODE_OFF) { - mAgent.onVipStatusChangedLocked(changedPkgs); - } - } - pw.println("Cleared all VIP statuses"); - return TareShellCommand.COMMAND_SUCCESS; - } - - int executeSetVip(@NonNull PrintWriter pw, - int userId, @NonNull String pkgName, @Nullable Boolean newVipState) { - final boolean changed; - synchronized (mLock) { - final boolean wasVip = isVip(userId, pkgName); - if (newVipState == null) { - mVipOverrides.delete(userId, pkgName); - } else { - mVipOverrides.add(userId, pkgName, newVipState); - } - changed = isVip(userId, pkgName) != wasVip; - if (mEnabledMode != ENABLED_MODE_OFF && changed) { - mAgent.onVipStatusChangedLocked(userId, pkgName); - } - } - pw.println(appToString(userId, pkgName) + " VIP status set to " + newVipState + "." - + " Final VIP state changed? " + changed); - return TareShellCommand.COMMAND_SUCCESS; - } - - // Dump infrastructure - private static void dumpHelp(PrintWriter pw) { - pw.println("Resource Economy (economy) dump options:"); - pw.println(" [-h|--help] [package] ..."); - pw.println(" -h | --help: print this help"); - pw.println(" [package] is an optional package name to limit the output to."); - } - - private void dumpInternal(final IndentingPrintWriter pw, final boolean dumpAll) { - if (!isTareSupported()) { - pw.print("Unsupported by device"); - return; - } - synchronized (mLock) { - pw.print("Enabled mode: "); - pw.println(enabledModeToString(mEnabledMode)); - - pw.print("Current battery level: "); - pw.println(mCurrentBatteryLevel); - - final long consumptionLimit = getConsumptionLimitLocked(); - pw.print("Consumption limit (current/initial-satiated/current-satiated): "); - pw.print(cakeToString(consumptionLimit)); - pw.print("/"); - pw.print(cakeToString(mCompleteEconomicPolicy.getInitialSatiatedConsumptionLimit())); - pw.print("/"); - pw.println(cakeToString(mScribe.getSatiatedConsumptionLimitLocked())); - - pw.print("Target bg battery life (hours): "); - pw.print(mTargetBackgroundBatteryLifeHours); - pw.print(" ("); - pw.print(String.format("%.2f", 100f / mTargetBackgroundBatteryLifeHours)); - pw.println("%/hr)"); - - final long remainingConsumable = mScribe.getRemainingConsumableCakesLocked(); - pw.print("Goods remaining: "); - pw.print(cakeToString(remainingConsumable)); - pw.print(" ("); - pw.print(String.format("%.2f", 100f * remainingConsumable / consumptionLimit)); - pw.println("% of current limit)"); - - pw.print("Device wealth: "); - pw.println(cakeToString(mScribe.getCakesInCirculationForLoggingLocked())); - - pw.println(); - pw.print("Exempted apps", mExemptedApps); - pw.println(); - - pw.println(); - pw.print("Wellbeing app="); - pw.println(mWellbeingPackage == null ? "None" : mWellbeingPackage); - - boolean printedVips = false; - pw.println(); - pw.print("VIPs:"); - pw.increaseIndent(); - for (int u = 0; u < mVipOverrides.numMaps(); ++u) { - final int userId = mVipOverrides.keyAt(u); - - for (int p = 0; p < mVipOverrides.numElementsForKeyAt(u); ++p) { - final String pkgName = mVipOverrides.keyAt(u, p); - - printedVips = true; - pw.println(); - pw.print(appToString(userId, pkgName)); - pw.print("="); - pw.print(mVipOverrides.valueAt(u, p)); - } - } - if (printedVips) { - pw.println(); - } else { - pw.print(" None"); - } - pw.decreaseIndent(); - pw.println(); - - boolean printedTempVips = false; - pw.println(); - pw.print("Temp VIPs:"); - pw.increaseIndent(); - for (int u = 0; u < mTemporaryVips.numMaps(); ++u) { - final int userId = mTemporaryVips.keyAt(u); - - for (int p = 0; p < mTemporaryVips.numElementsForKeyAt(u); ++p) { - final String pkgName = mTemporaryVips.keyAt(u, p); - - printedTempVips = true; - pw.println(); - pw.print(appToString(userId, pkgName)); - pw.print("="); - pw.print(mTemporaryVips.valueAt(u, p)); - } - } - if (printedTempVips) { - pw.println(); - } else { - pw.print(" None"); - } - pw.decreaseIndent(); - pw.println(); - - pw.println(); - pw.println("Installers:"); - pw.increaseIndent(); - for (int u = 0; u < mInstallers.numMaps(); ++u) { - final int userId = mInstallers.keyAt(u); - - for (int p = 0; p < mInstallers.numElementsForKeyAt(u); ++p) { - final String pkgName = mInstallers.keyAt(u, p); - - pw.print(appToString(userId, pkgName)); - pw.print(": "); - pw.print(mInstallers.valueAt(u, p).size()); - pw.println(" apps"); - } - } - pw.decreaseIndent(); - - pw.println(); - mCompleteEconomicPolicy.dump(pw); - - pw.println(); - mScribe.dumpLocked(pw, dumpAll); - - pw.println(); - mAgent.dumpLocked(pw); - - pw.println(); - mAnalyst.dump(pw); - - // Put this at the end since this may be a lot and we want to have the earlier - // information easily accessible. - boolean printedInterestingIpos = false; - pw.println(); - pw.print("Interesting apps:"); - pw.increaseIndent(); - for (int u = 0; u < mPkgCache.numMaps(); ++u) { - for (int p = 0; p < mPkgCache.numElementsForKeyAt(u); ++p) { - final InstalledPackageInfo ipo = mPkgCache.valueAt(u, p); - - // Printing out every single app will be too much. Only print apps that - // have some interesting characteristic. - final boolean isInteresting = ipo.hasCode - && ipo.isHeadlessSystemApp - && !UserHandle.isCore(ipo.uid); - if (!isInteresting) { - continue; - } - - printedInterestingIpos = true; - pw.println(); - pw.print(ipo); - } - } - if (printedInterestingIpos) { - pw.println(); - } else { - pw.print(" None"); - } - pw.decreaseIndent(); - pw.println(); - } - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java deleted file mode 100644 index 69e57365b6e0..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java +++ /dev/null @@ -1,483 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -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_INITIAL_CONSUMPTION_LIMIT_CAKES; -import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES; -import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES; -import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_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_HEADLESS_SYSTEM_APP_CAKES; -import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES; -import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES; -import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_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; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_CTP; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_CTP; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_CTP; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_START_CTP; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_CTP; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_START_CTP; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_CTP; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_CTP; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE; -import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP; -import static android.app.tare.EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT; -import static android.app.tare.EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT; -import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE; -import static android.app.tare.EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT; -import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED; -import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP; -import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER; -import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_MAX; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_ONGOING; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_MAX; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_INSTANT; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_MAX; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_ONGOING; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_MAX; -import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING; -import static android.app.tare.EconomyManager.arcToCake; -import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS; - -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.appToString; -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.util.IndentingPrintWriter; -import android.util.Slog; -import android.util.SparseArray; - -/** - * Policy defining pricing information and daily ARC requirements and suggestions for - * JobScheduler. - */ -public class JobSchedulerEconomicPolicy extends EconomicPolicy { - private static final String TAG = "TARE- " + JobSchedulerEconomicPolicy.class.getSimpleName(); - - public static final int ACTION_JOB_MAX_START = TYPE_ACTION | POLICY_JOB | 0; - public static final int ACTION_JOB_MAX_RUNNING = TYPE_ACTION | POLICY_JOB | 1; - public static final int ACTION_JOB_HIGH_START = TYPE_ACTION | POLICY_JOB | 2; - public static final int ACTION_JOB_HIGH_RUNNING = TYPE_ACTION | POLICY_JOB | 3; - public static final int ACTION_JOB_DEFAULT_START = TYPE_ACTION | POLICY_JOB | 4; - public static final int ACTION_JOB_DEFAULT_RUNNING = TYPE_ACTION | POLICY_JOB | 5; - public static final int ACTION_JOB_LOW_START = TYPE_ACTION | POLICY_JOB | 6; - public static final int ACTION_JOB_LOW_RUNNING = TYPE_ACTION | POLICY_JOB | 7; - public static final int ACTION_JOB_MIN_START = TYPE_ACTION | POLICY_JOB | 8; - public static final int ACTION_JOB_MIN_RUNNING = TYPE_ACTION | POLICY_JOB | 9; - public static final int ACTION_JOB_TIMEOUT = TYPE_ACTION | POLICY_JOB | 10; - - public static final int REWARD_APP_INSTALL = TYPE_REWARD | POLICY_JOB | 0; - - private static final int[] COST_MODIFIERS = new int[]{ - COST_MODIFIER_CHARGING, - COST_MODIFIER_DEVICE_IDLE, - COST_MODIFIER_POWER_SAVE_MODE, - COST_MODIFIER_PROCESS_STATE - }; - - private long mMinSatiatedBalanceExempted; - private long mMinSatiatedBalanceHeadlessSystemApp; - private long mMinSatiatedBalanceOther; - private long mMinSatiatedBalanceIncrementalAppUpdater; - private long mMaxSatiatedBalance; - private long mInitialSatiatedConsumptionLimit; - private long mMinSatiatedConsumptionLimit; - private long mMaxSatiatedConsumptionLimit; - - private final Injector mInjector; - - private final SparseArray<Action> mActions = new SparseArray<>(); - private final SparseArray<Reward> mRewards = new SparseArray<>(); - - JobSchedulerEconomicPolicy(InternalResourceService irs, Injector injector) { - super(irs); - mInjector = injector; - loadConstants("", null); - } - - @Override - void setup(@NonNull DeviceConfig.Properties properties) { - super.setup(properties); - final ContentResolver resolver = mIrs.getContext().getContentResolver(); - loadConstants(mInjector.getSettingsGlobalString(resolver, TARE_JOB_SCHEDULER_CONSTANTS), - properties); - } - - @Override - long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) { - if (mIrs.isPackageRestricted(userId, pkgName)) { - return 0; - } - - final long baseBalance; - if (mIrs.isPackageExempted(userId, pkgName)) { - baseBalance = mMinSatiatedBalanceExempted; - } else if (mIrs.isHeadlessSystemApp(userId, pkgName)) { - baseBalance = mMinSatiatedBalanceHeadlessSystemApp; - } else { - baseBalance = mMinSatiatedBalanceOther; - } - - long minBalance = baseBalance; - - final int updateResponsibilityCount = mIrs.getAppUpdateResponsibilityCount(userId, pkgName); - minBalance += updateResponsibilityCount * mMinSatiatedBalanceIncrementalAppUpdater; - - return Math.min(minBalance, mMaxSatiatedBalance); - } - - @Override - long getMaxSatiatedBalance(int userId, @NonNull String pkgName) { - if (mIrs.isPackageRestricted(userId, pkgName)) { - return 0; - } - final InstalledPackageInfo ipo = mIrs.getInstalledPackageInfo(userId, pkgName); - if (ipo == null) { - Slog.wtfStack(TAG, - "Tried to get max balance of invalid app: " + appToString(userId, pkgName)); - } else { - // A system installer's max balance is elevated for some time after first boot so - // they can use jobs to download and install apps. - if (ipo.isSystemInstaller) { - final long timeSinceFirstSetupMs = mIrs.getRealtimeSinceFirstSetupMs(); - final boolean stillExempted = timeSinceFirstSetupMs - < InternalResourceService.INSTALLER_FIRST_SETUP_GRACE_PERIOD_MS; - if (stillExempted) { - return mMaxSatiatedConsumptionLimit; - } - } - } - return mMaxSatiatedBalance; - } - - @Override - long getInitialSatiatedConsumptionLimit() { - return mInitialSatiatedConsumptionLimit; - } - - @Override - long getMinSatiatedConsumptionLimit() { - return mMinSatiatedConsumptionLimit; - } - - @Override - long getMaxSatiatedConsumptionLimit() { - return mMaxSatiatedConsumptionLimit; - } - - @NonNull - @Override - int[] getCostModifiers() { - return COST_MODIFIERS; - } - - @Nullable - @Override - Action getAction(@AppAction int actionId) { - return mActions.get(actionId); - } - - @Nullable - @Override - Reward getReward(@UtilityReward int rewardId) { - return mRewards.get(rewardId); - } - - private void loadConstants(String policyValuesString, - @Nullable DeviceConfig.Properties properties) { - mActions.clear(); - mRewards.clear(); - - try { - mUserSettingDeviceConfigMediator.setSettingsString(policyValuesString); - mUserSettingDeviceConfigMediator.setDeviceConfigProperties(properties); - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Global setting key incorrect: ", e); - } - - mMinSatiatedBalanceOther = getConstantAsCake( - KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES); - mMinSatiatedBalanceHeadlessSystemApp = getConstantAsCake( - KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, - DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES, - mMinSatiatedBalanceOther); - mMinSatiatedBalanceExempted = getConstantAsCake( - KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, - DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES, - mMinSatiatedBalanceHeadlessSystemApp); - mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake( - KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER, - DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES); - mMaxSatiatedBalance = getConstantAsCake( - KEY_JS_MAX_SATIATED_BALANCE, DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, - Math.max(arcToCake(1), mMinSatiatedBalanceExempted)); - mMinSatiatedConsumptionLimit = getConstantAsCake( - KEY_JS_MIN_CONSUMPTION_LIMIT, DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES, - arcToCake(1)); - mInitialSatiatedConsumptionLimit = getConstantAsCake( - KEY_JS_INITIAL_CONSUMPTION_LIMIT, DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES, - mMinSatiatedConsumptionLimit); - mMaxSatiatedConsumptionLimit = getConstantAsCake( - KEY_JS_MAX_CONSUMPTION_LIMIT, DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES, - mInitialSatiatedConsumptionLimit); - - mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START, - getConstantAsCake( - KEY_JS_ACTION_JOB_MAX_START_CTP, - DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_ACTION_JOB_MAX_RUNNING_CTP, - DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_ACTION_JOB_HIGH_START_CTP, - DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP, - DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_ACTION_JOB_DEFAULT_START_CTP, - DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP, - DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_ACTION_JOB_LOW_START_CTP, - DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_ACTION_JOB_LOW_RUNNING_CTP, - DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_ACTION_JOB_MIN_START_CTP, - DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_ACTION_JOB_MIN_RUNNING_CTP, - DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP, - DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_REWARD_TOP_ACTIVITY_INSTANT, - DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES), - getConstantAsCake( - KEY_JS_REWARD_TOP_ACTIVITY_ONGOING, - DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES), - getConstantAsCake( - KEY_JS_REWARD_TOP_ACTIVITY_MAX, - DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES))); - mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN, - getConstantAsCake( - KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT, - DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES), - getConstantAsCake( - KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING, - DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES), - getConstantAsCake( - KEY_JS_REWARD_NOTIFICATION_SEEN_MAX, - DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES))); - mRewards.put(REWARD_NOTIFICATION_INTERACTION, - new Reward(REWARD_NOTIFICATION_INTERACTION, - getConstantAsCake( - KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT, - DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES), - getConstantAsCake( - KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING, - DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES), - getConstantAsCake( - KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX, - DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES))); - mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION, - getConstantAsCake( - KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT, - DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES), - getConstantAsCake( - KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING, - DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES), - getConstantAsCake( - 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, - getConstantAsCake( - KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT, - DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES), - getConstantAsCake( - KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING, - DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES), - getConstantAsCake( - KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX, - DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES))); - mRewards.put(REWARD_APP_INSTALL, - new Reward(REWARD_APP_INSTALL, - getConstantAsCake( - KEY_JS_REWARD_APP_INSTALL_INSTANT, - DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES), - getConstantAsCake( - KEY_JS_REWARD_APP_INSTALL_ONGOING, - DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES), - getConstantAsCake( - KEY_JS_REWARD_APP_INSTALL_MAX, - DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES))); - } - - @Override - void dump(IndentingPrintWriter pw) { - pw.println("Min satiated balance:"); - pw.increaseIndent(); - pw.print("Exempted", cakeToString(mMinSatiatedBalanceExempted)).println(); - pw.print("Other", cakeToString(mMinSatiatedBalanceOther)).println(); - pw.print("+App Updater", cakeToString(mMinSatiatedBalanceIncrementalAppUpdater)).println(); - pw.decreaseIndent(); - pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println(); - pw.print("Consumption limits: ["); - pw.print(cakeToString(mMinSatiatedConsumptionLimit)); - pw.print(", "); - pw.print(cakeToString(mInitialSatiatedConsumptionLimit)); - pw.print(", "); - pw.print(cakeToString(mMaxSatiatedConsumptionLimit)); - pw.println("]"); - - pw.println(); - pw.println("Actions:"); - pw.increaseIndent(); - for (int i = 0; i < mActions.size(); ++i) { - dumpAction(pw, mActions.valueAt(i)); - } - pw.decreaseIndent(); - - pw.println(); - pw.println("Rewards:"); - pw.increaseIndent(); - for (int i = 0; i < mRewards.size(); ++i) { - dumpReward(pw, mRewards.valueAt(i)); - } - pw.decreaseIndent(); - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java deleted file mode 100644 index 92b21e10d142..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static android.text.format.DateUtils.HOUR_IN_MILLIS; -import static android.util.TimeUtils.dumpTime; - -import static com.android.server.tare.TareUtils.cakeToString; -import static com.android.server.tare.TareUtils.getCurrentTimeMillis; - -import android.annotation.CurrentTimeMillisLong; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Build; -import android.util.IndentingPrintWriter; -import android.util.Log; -import android.util.SparseLongArray; -import android.util.TimeUtils; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.ArrayList; -import java.util.List; - -/** - * Ledger to track the last recorded balance and recent activities of an app. - */ -class Ledger { - private static final String TAG = "TARE-" + Ledger.class.getSimpleName(); - private static final boolean DEBUG = InternalResourceService.DEBUG - || Log.isLoggable(TAG, Log.DEBUG); - - /** The window size within which rewards will be counted and used towards reward limiting. */ - private static final long TOTAL_REWARD_WINDOW_MS = 24 * HOUR_IN_MILLIS; - /** The number of buckets to split {@link #TOTAL_REWARD_WINDOW_MS} into. */ - @VisibleForTesting - static final int NUM_REWARD_BUCKET_WINDOWS = 4; - /** - * The duration size of each bucket resulting from splitting {@link #TOTAL_REWARD_WINDOW_MS} - * into smaller buckets. - */ - private static final long REWARD_BUCKET_WINDOW_SIZE_MS = - TOTAL_REWARD_WINDOW_MS / NUM_REWARD_BUCKET_WINDOWS; - /** The maximum number of transactions to retain in memory at any one time. */ - @VisibleForTesting - static final int MAX_TRANSACTION_COUNT = Build.IS_ENG || Build.IS_USERDEBUG || DEBUG ? 32 : 4; - - static class Transaction { - public final long startTimeMs; - public final long endTimeMs; - public final int eventId; - @Nullable - public final String tag; - public final long delta; - public final long ctp; - - Transaction(long startTimeMs, long endTimeMs, - int eventId, @Nullable String tag, long delta, long ctp) { - this.startTimeMs = startTimeMs; - this.endTimeMs = endTimeMs; - this.eventId = eventId; - this.tag = tag == null ? null : tag.intern(); - this.delta = delta; - this.ctp = ctp; - } - } - - static class RewardBucket { - @CurrentTimeMillisLong - public long startTimeMs; - public final SparseLongArray cumulativeDelta = new SparseLongArray(); - - private void reset() { - startTimeMs = 0; - cumulativeDelta.clear(); - } - } - - /** Last saved balance. This doesn't take currently ongoing events into account. */ - private long mCurrentBalance = 0; - private final Transaction[] mTransactions = new Transaction[MAX_TRANSACTION_COUNT]; - /** Index within {@link #mTransactions} where the next transaction should be placed. */ - private int mTransactionIndex = 0; - private final RewardBucket[] mRewardBuckets = new RewardBucket[NUM_REWARD_BUCKET_WINDOWS]; - /** Index within {@link #mRewardBuckets} of the current active bucket. */ - private int mRewardBucketIndex = 0; - - Ledger() { - } - - Ledger(long currentBalance, @NonNull List<Transaction> transactions, - @NonNull List<RewardBucket> rewardBuckets) { - mCurrentBalance = currentBalance; - - final int numTxs = transactions.size(); - for (int i = Math.max(0, numTxs - MAX_TRANSACTION_COUNT); i < numTxs; ++i) { - mTransactions[mTransactionIndex++] = transactions.get(i); - } - mTransactionIndex %= MAX_TRANSACTION_COUNT; - - final int numBuckets = rewardBuckets.size(); - if (numBuckets > 0) { - // Set the index to -1 so that we put the first bucket in index 0. - mRewardBucketIndex = -1; - for (int i = Math.max(0, numBuckets - NUM_REWARD_BUCKET_WINDOWS); i < numBuckets; ++i) { - mRewardBuckets[++mRewardBucketIndex] = rewardBuckets.get(i); - } - } - } - - long getCurrentBalance() { - return mCurrentBalance; - } - - @Nullable - Transaction getEarliestTransaction() { - for (int t = 0; t < mTransactions.length; ++t) { - final Transaction transaction = - mTransactions[(mTransactionIndex + t) % mTransactions.length]; - if (transaction != null) { - return transaction; - } - } - return null; - } - - @NonNull - List<RewardBucket> getRewardBuckets() { - final long cutoffMs = getCurrentTimeMillis() - TOTAL_REWARD_WINDOW_MS; - final List<RewardBucket> list = new ArrayList<>(NUM_REWARD_BUCKET_WINDOWS); - for (int i = 1; i <= NUM_REWARD_BUCKET_WINDOWS; ++i) { - final int idx = (mRewardBucketIndex + i) % NUM_REWARD_BUCKET_WINDOWS; - final RewardBucket rewardBucket = mRewardBuckets[idx]; - if (rewardBucket != null) { - if (cutoffMs <= rewardBucket.startTimeMs) { - list.add(rewardBucket); - } else { - rewardBucket.reset(); - } - } - } - return list; - } - - @NonNull - List<Transaction> getTransactions() { - final List<Transaction> list = new ArrayList<>(MAX_TRANSACTION_COUNT); - for (int i = 0; i < MAX_TRANSACTION_COUNT; ++i) { - final int idx = (mTransactionIndex + i) % MAX_TRANSACTION_COUNT; - final Transaction transaction = mTransactions[idx]; - if (transaction != null) { - list.add(transaction); - } - } - return list; - } - - void recordTransaction(@NonNull Transaction transaction) { - mTransactions[mTransactionIndex] = transaction; - mCurrentBalance += transaction.delta; - mTransactionIndex = (mTransactionIndex + 1) % MAX_TRANSACTION_COUNT; - - if (EconomicPolicy.isReward(transaction.eventId)) { - final RewardBucket bucket = getCurrentRewardBucket(); - bucket.cumulativeDelta.put(transaction.eventId, - bucket.cumulativeDelta.get(transaction.eventId, 0) + transaction.delta); - } - } - - @NonNull - private RewardBucket getCurrentRewardBucket() { - RewardBucket bucket = mRewardBuckets[mRewardBucketIndex]; - final long now = getCurrentTimeMillis(); - if (bucket == null) { - bucket = new RewardBucket(); - bucket.startTimeMs = now; - mRewardBuckets[mRewardBucketIndex] = bucket; - return bucket; - } - - if (now - bucket.startTimeMs < REWARD_BUCKET_WINDOW_SIZE_MS) { - return bucket; - } - - mRewardBucketIndex = (mRewardBucketIndex + 1) % NUM_REWARD_BUCKET_WINDOWS; - bucket = mRewardBuckets[mRewardBucketIndex]; - if (bucket == null) { - bucket = new RewardBucket(); - mRewardBuckets[mRewardBucketIndex] = bucket; - } - bucket.reset(); - // Using now as the start time means there will be some gaps between sequential buckets, - // but makes processing of large gaps between events easier. - bucket.startTimeMs = now; - return bucket; - } - - long get24HourSum(int eventId, final long now) { - final long windowStartTime = now - 24 * HOUR_IN_MILLIS; - long sum = 0; - for (int i = 0; i < mRewardBuckets.length; ++i) { - final RewardBucket bucket = mRewardBuckets[i]; - if (bucket != null - && bucket.startTimeMs >= windowStartTime && bucket.startTimeMs < now) { - sum += bucket.cumulativeDelta.get(eventId, 0); - } - } - return sum; - } - - /** - * Deletes transactions that are older than {@code minAgeMs}. - * @return The earliest transaction in the ledger, or {@code null} if there are no more - * transactions. - */ - @Nullable - Transaction removeOldTransactions(long minAgeMs) { - final long cutoff = getCurrentTimeMillis() - minAgeMs; - for (int t = 0; t < mTransactions.length; ++t) { - final int idx = (mTransactionIndex + t) % mTransactions.length; - final Transaction transaction = mTransactions[idx]; - if (transaction == null) { - continue; - } - if (transaction.endTimeMs <= cutoff) { - mTransactions[idx] = null; - } else { - // Everything we look at after this transaction will also be within the window, - // so no need to go further. - return transaction; - } - } - return null; - } - - void dump(IndentingPrintWriter pw, int numRecentTransactions) { - pw.print("Current balance", cakeToString(getCurrentBalance())).println(); - pw.println(); - - boolean printedTransactionTitle = false; - for (int t = 0; t < Math.min(MAX_TRANSACTION_COUNT, numRecentTransactions); ++t) { - final int idx = (mTransactionIndex + t) % MAX_TRANSACTION_COUNT; - final Transaction transaction = mTransactions[idx]; - if (transaction == null) { - continue; - } - - if (!printedTransactionTitle) { - pw.println("Transactions:"); - pw.increaseIndent(); - printedTransactionTitle = true; - } - - dumpTime(pw, transaction.startTimeMs); - pw.print("--"); - dumpTime(pw, transaction.endTimeMs); - pw.print(": "); - pw.print(EconomicPolicy.eventToString(transaction.eventId)); - if (transaction.tag != null) { - pw.print("("); - pw.print(transaction.tag); - pw.print(")"); - } - pw.print(" --> "); - pw.print(cakeToString(transaction.delta)); - pw.print(" (ctp="); - pw.print(cakeToString(transaction.ctp)); - pw.println(")"); - } - if (printedTransactionTitle) { - pw.decreaseIndent(); - pw.println(); - } - - final long now = getCurrentTimeMillis(); - boolean printedBucketTitle = false; - for (int b = 0; b < NUM_REWARD_BUCKET_WINDOWS; ++b) { - final int idx = (mRewardBucketIndex - b + NUM_REWARD_BUCKET_WINDOWS) - % NUM_REWARD_BUCKET_WINDOWS; - final RewardBucket rewardBucket = mRewardBuckets[idx]; - if (rewardBucket == null || rewardBucket.startTimeMs == 0) { - continue; - } - - if (!printedBucketTitle) { - pw.println("Reward buckets:"); - pw.increaseIndent(); - printedBucketTitle = true; - } - - dumpTime(pw, rewardBucket.startTimeMs); - pw.print(" ("); - TimeUtils.formatDuration(now - rewardBucket.startTimeMs, pw); - pw.println(" ago):"); - pw.increaseIndent(); - for (int r = 0; r < rewardBucket.cumulativeDelta.size(); ++r) { - pw.print(EconomicPolicy.eventToString(rewardBucket.cumulativeDelta.keyAt(r))); - pw.print(": "); - pw.println(cakeToString(rewardBucket.cumulativeDelta.valueAt(r))); - } - pw.decreaseIndent(); - } - if (printedBucketTitle) { - pw.decreaseIndent(); - pw.println(); - } - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java b/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java deleted file mode 100644 index 311b6cb7910f..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import android.annotation.IntDef; -import android.util.IndentingPrintWriter; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Base class of a modifier that can affect end pricing. - */ -abstract class Modifier { - static final int COST_MODIFIER_CHARGING = 0; - static final int COST_MODIFIER_DEVICE_IDLE = 1; - static final int COST_MODIFIER_POWER_SAVE_MODE = 2; - static final int COST_MODIFIER_PROCESS_STATE = 3; - static final int NUM_COST_MODIFIERS = COST_MODIFIER_PROCESS_STATE + 1; - - @IntDef({ - COST_MODIFIER_CHARGING, - COST_MODIFIER_DEVICE_IDLE, - COST_MODIFIER_POWER_SAVE_MODE, - COST_MODIFIER_PROCESS_STATE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface CostModifier { - } - - /** - * Returns a modified cost to produce based on the modifier's state. - * - * @param ctp Current cost to produce - */ - long getModifiedCostToProduce(long ctp) { - return ctp; - } - - /** - * Returns a modified price based on the modifier's state. - * - * @param price Current price - */ - long getModifiedPrice(long price) { - return price; - } - - void setup() { - } - - void tearDown() { - } - - abstract void dump(IndentingPrintWriter pw); -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/OWNERS b/apex/jobscheduler/service/java/com/android/server/tare/OWNERS deleted file mode 100644 index 96ec75f39e4e..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/OWNERS +++ /dev/null @@ -1,5 +0,0 @@ -dplotnikov@google.com -kwekua@google.com -mwachens@google.com -suprabh@google.com -yamasani@google.com
\ No newline at end of file diff --git a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java deleted file mode 100644 index 542bfd12e5c7..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import android.annotation.NonNull; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.PowerManager; -import android.os.SystemClock; -import android.util.IndentingPrintWriter; -import android.util.Log; -import android.util.Slog; - -/** Modifier that makes things more expensive in adaptive and full battery saver are active. */ -class PowerSaveModeModifier extends Modifier { - private static final String TAG = "TARE-" + PowerSaveModeModifier.class.getSimpleName(); - private static final boolean DEBUG = InternalResourceService.DEBUG - || Log.isLoggable(TAG, Log.DEBUG); - - private final InternalResourceService mIrs; - private final PowerSaveModeTracker mPowerSaveModeTracker; - - PowerSaveModeModifier(@NonNull InternalResourceService irs) { - super(); - mIrs = irs; - mPowerSaveModeTracker = new PowerSaveModeTracker(); - } - - @Override - public void setup() { - mPowerSaveModeTracker.startTracking(mIrs.getContext()); - } - - @Override - public void tearDown() { - mPowerSaveModeTracker.stopTracking(mIrs.getContext()); - } - - @Override - long getModifiedCostToProduce(long ctp) { - if (mPowerSaveModeTracker.mPowerSaveModeEnabled) { - return (long) (1.5 * ctp); - } - // TODO: get adaptive power save mode - if (mPowerSaveModeTracker.mPowerSaveModeEnabled) { - return (long) (1.25 * ctp); - } - return ctp; - } - - @Override - void dump(IndentingPrintWriter pw) { - pw.print("power save="); - pw.println(mPowerSaveModeTracker.mPowerSaveModeEnabled); - } - - // TODO: migrate to relying on PowerSaveState and ServiceType.TARE - private final class PowerSaveModeTracker extends BroadcastReceiver { - private boolean mIsSetup = false; - - private final PowerManager mPowerManager; - private volatile boolean mPowerSaveModeEnabled; - - private PowerSaveModeTracker() { - mPowerManager = mIrs.getContext().getSystemService(PowerManager.class); - } - - public void startTracking(@NonNull Context context) { - if (mIsSetup) { - return; - } - - final IntentFilter filter = new IntentFilter(); - filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); - context.registerReceiver(this, filter); - - // Initialise tracker state. - mPowerSaveModeEnabled = mPowerManager.isPowerSaveMode(); - - mIsSetup = true; - } - - public void stopTracking(@NonNull Context context) { - if (!mIsSetup) { - return; - } - - context.unregisterReceiver(this); - mIsSetup = false; - } - - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) { - final boolean enabled = mPowerManager.isPowerSaveMode(); - if (DEBUG) { - Slog.d(TAG, "Power save mode changed to " + enabled - + ", fired @ " + SystemClock.elapsedRealtime()); - } - if (mPowerSaveModeEnabled != enabled) { - mPowerSaveModeEnabled = enabled; - mIrs.onDeviceStateChanged(); - } - } - } - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java deleted file mode 100644 index 585366755191..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.app.ActivityManager; -import android.app.IUidObserver; -import android.app.UidObserver; -import android.os.RemoteException; -import android.util.IndentingPrintWriter; -import android.util.Slog; -import android.util.SparseArrayMap; -import android.util.SparseIntArray; - -import com.android.internal.annotations.GuardedBy; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** Modifier that makes things more cheaper based on an app's process state. */ -class ProcessStateModifier extends Modifier { - private static final String TAG = "TARE-" + ProcessStateModifier.class.getSimpleName(); - - private static final int PROC_STATE_BUCKET_NONE = 0; - private static final int PROC_STATE_BUCKET_TOP = 1; - private static final int PROC_STATE_BUCKET_FGS = 2; - private static final int PROC_STATE_BUCKET_BFGS = 3; - private static final int PROC_STATE_BUCKET_BG = 4; - - @IntDef(prefix = {"PROC_STATE_BUCKET_"}, value = { - PROC_STATE_BUCKET_NONE, - PROC_STATE_BUCKET_TOP, - PROC_STATE_BUCKET_FGS, - PROC_STATE_BUCKET_BFGS, - PROC_STATE_BUCKET_BG - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ProcStateBucket { - } - - private final Object mLock = new Object(); - private final InternalResourceService mIrs; - - /** Cached mapping of userId+package to their UIDs (for all users) */ - private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>(); - - @GuardedBy("mLock") - private final SparseIntArray mUidProcStateBucketCache = new SparseIntArray(); - - private final IUidObserver mUidObserver = new UidObserver() { - @Override - public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { - final int newBucket = getProcStateBucket(procState); - synchronized (mLock) { - final int curBucket = mUidProcStateBucketCache.get(uid); - if (curBucket != newBucket) { - mUidProcStateBucketCache.put(uid, newBucket); - } - notifyStateChangedLocked(uid); - } - } - - @Override - public void onUidGone(int uid, boolean disabled) { - synchronized (mLock) { - if (mUidProcStateBucketCache.indexOfKey(uid) < 0) { - Slog.e(TAG, "UID " + uid + " marked gone but wasn't in cache."); - return; - } - mUidProcStateBucketCache.delete(uid); - notifyStateChangedLocked(uid); - } - } - }; - - ProcessStateModifier(@NonNull InternalResourceService irs) { - super(); - mIrs = irs; - } - - @Override - @GuardedBy("mLock") - void setup() { - try { - ActivityManager.getService().registerUidObserver(mUidObserver, - ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE, - ActivityManager.PROCESS_STATE_UNKNOWN, null); - } catch (RemoteException e) { - // ignored; both services live in system_server - } - } - - @Override - @GuardedBy("mLock") - void tearDown() { - try { - ActivityManager.getService().unregisterUidObserver(mUidObserver); - } catch (RemoteException e) { - // ignored; both services live in system_server - } - mPackageToUidCache.clear(); - mUidProcStateBucketCache.clear(); - } - - /** - * Get the final modified price based on an app's process state. - * - * @param ctp Cost to produce. @see EconomicPolicy.Action#costToProduce - * @param price Current price - */ - long getModifiedPrice(final int userId, @NonNull final String pkgName, - final long ctp, final long price) { - final int procState; - synchronized (mLock) { - procState = mUidProcStateBucketCache.get( - mIrs.getUid(userId, pkgName), PROC_STATE_BUCKET_NONE); - } - switch (procState) { - case PROC_STATE_BUCKET_TOP: - return 0; - case PROC_STATE_BUCKET_FGS: - // Can't get notification priority. Just use CTP for now. - return Math.min(ctp, price); - case PROC_STATE_BUCKET_BFGS: - if (price <= ctp) { - return price; - } - return (long) (ctp + .5 * (price - ctp)); - case PROC_STATE_BUCKET_BG: - default: - return price; - } - } - - @Override - @GuardedBy("mLock") - void dump(IndentingPrintWriter pw) { - pw.print("Proc state bucket cache = "); - pw.println(mUidProcStateBucketCache); - } - - @ProcStateBucket - private int getProcStateBucket(int procState) { - if (procState <= ActivityManager.PROCESS_STATE_TOP) { - return PROC_STATE_BUCKET_TOP; - } - if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { - return PROC_STATE_BUCKET_FGS; - } - if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { - return PROC_STATE_BUCKET_BFGS; - } - return PROC_STATE_BUCKET_BG; - } - - @GuardedBy("mLock") - private void notifyStateChangedLocked(final int uid) { - // Never call out to the IRS with the local lock held. - TareHandlerThread.getHandler().post(() -> mIrs.onUidStateChanged(uid)); - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/README.md b/apex/jobscheduler/service/java/com/android/server/tare/README.md deleted file mode 100644 index 8d25ecce8431..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/README.md +++ /dev/null @@ -1,153 +0,0 @@ -# Overview - -Welcome to The Android Resource Economy (TARE for short). If you're reading this, you may be -wondering what all of this code is for and what it means. TARE is an attempt to apply economic -principles to resource (principally battery) management. It acknowledges that battery is a limited -resource on mobile devices and that the system must allocate and apportion those resources -accordingly. Every action (running a job, firing an alarm, using the network, using the CPU, etc.) -has a cost. Once that action has been performed and that bit of battery has been drained, it's no -longer available for someone else (another app) to use until the user charges the device again. - -The key tenets of TARE are: - -1. Charge for actions --- when an app performs an action, reduce its access to resources in the - future. This should help remind everyone that everything they do has a cost. -1. Reward for good actions --- reward and encourage behavior that provides value to the user -1. Fine bad actions --- fine and discourage behavior that is bad for the user - -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 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. - -## How do ARCs work? - -ARCs are required to perform any action while in the background. Some actions may have a fixed cost. -Others may be more dynamic (some may even allow apps to bid higher ARCs for some actions to have -them prioritized). If the app doesn't have enough ARCs, the action can't be performed. Apps are -granted ARCs (below a certain threshold) as the device charges. Apps are also granted ARCs for -providing user value (eg. for doing things that engage the user). - -ARCs will be used across the entire system as one unified concept. When an app performs an action, -it pulls from the same account, regardless of the action. This means that apps can choose to do more -of one action in lieu of being able to do as much of another. For example, an app can choose to use -all of its ARCs for jobs if it doesn't want to schedule any alarms. - -### Scaling - -With the ARC system, we can limit the total number of ARCs in circulation, thus limiting how much -total work can be done, regardless of how many apps the user has installed. - -## EconomicPolicy - -An EconomicPolicy defines the actions and rewards a specific subsystem makes use of. Each subsystem -will likely have a unique set of actions that apps can perform, and may choose to reward apps for -certain behaviors. Generally, the app should be rewarded with ARCs for behaviors that indicate that -the app provided value to the user. The current set of behaviors that apps may be rewarded for -include 1) a user seeing a notification, 2) a user interacting with a notification, 3) the user -opening the app and/or staying in the app for some period of time, 4) the user interacting with a -widget, and 5) the user explicitly interacting with the app in some other way. These behaviors may -change as we determine better ways of identifying providing value to the user and/or user desire for -the app to perform the actions it's requesting. - -### Consumption Limit - -The consumption limit represents the maximum amount of resources available to be consumed. When the -battery is satiated (at 100%), then the amount of resources available to be consumed is equal to the -consumption limit. Each action has a cost to produce that action. When the action is performed, -those resources are consumed. Thus, when an action is performed, the action's CTP is deducted from -the remaining amount of resources available. In keeping with the tenet that resources are limited -and ARCs are a proxy for battery consumption, the amount of resources available to be consumed are -adjusted as the battery level changes. That is, the consumption limit is scaled based on the current -battery level, and if the amount currently available to be consumed is greater than the scaled -consumption limit, then the available resources are decreased to match the scaled limit. - -### Regulation - -Regulations are unique events invoked by the ~~government~~ system in order to get the whole economy -moving smoothly. - -# Significant Changes - -## Tare Improvement Proposal #1 (TIP1) - -The initial implementation/proposal combined the supply of resources with the allocation in a single -mechanism. It defined the maximum number of resources (ARCs) available at a time, and then divided -(allocated) that number among the installed apps, intending to have some left over that could be -allocated as part of the rewards. There were several problems with that mechanism: - -1. Not all apps used their credits, which meant that allocating credits to those packages - effectively permanently reduced the number of usable/re-allocatable ARCs. -1. Having a global maximum circulation spread across multiple apps meant that as more apps were - installed, the allocation to each app decreased. Eventually (with enough apps installed), no app - would be given enough credits to perform any actions. - -These problems effectively meant that misallocation was a big problem, demand wasn't well reflected, -and some apps may not have been able to perform work even though they otherwise should have been. - -TIP1 separated allocation (to apps) from supply (by the system) and -allowed apps to accrue credits as appropriate while still limiting the total number of credits -consumed. - -## Tare Improvement Proposal #3 (TIP3) - -TIP1 introduced Consumption Limits, which control the total number of ARCs that can be used to -perform actions, based on the production costs of each action. The Consumption Limits were initially -determined manually, but could increase in the system if apps used the full consumption limit before -the device had drained to 50% battery. As with any system that relies on manually deciding -parameters, the only mechanism to identify an optimal value is through experimentation, which can -take many iterations and requires extended periods of time to observe results. The limits are also -chosen and adjusted without consideration of the resulting battery drain of each possible value. In -addition, having the system potentially increase the limit without considering a decrease introduced -potential for battery life to get worse as time goes on and the user installed more background-work -demanding apps. - -TIP3 uses a target background battery drain rate to dynamically adjust the Consumption Limit. - -# Potential Future Changes - -These are some ideas for further changes. There's no guarantee that they'll be implemented. - -* 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. -* 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/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java deleted file mode 100644 index 87e12495c4d8..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java +++ /dev/null @@ -1,827 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static android.app.tare.EconomyManager.ENABLED_MODE_OFF; -import static android.text.format.DateUtils.HOUR_IN_MILLIS; - -import static com.android.server.tare.TareUtils.appToString; -import static com.android.server.tare.TareUtils.cakeToString; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Environment; -import android.os.SystemClock; -import android.os.UserHandle; -import android.util.ArraySet; -import android.util.AtomicFile; -import android.util.IndentingPrintWriter; -import android.util.Log; -import android.util.Pair; -import android.util.Slog; -import android.util.SparseArray; -import android.util.SparseArrayMap; -import android.util.SparseLongArray; -import android.util.Xml; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.modules.utils.TypedXmlPullParser; -import com.android.modules.utils.TypedXmlSerializer; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * Maintains the current TARE state and handles writing it to disk and reading it back from disk. - */ -public class Scribe { - private static final String TAG = "TARE-" + Scribe.class.getSimpleName(); - private static final boolean DEBUG = InternalResourceService.DEBUG - || Log.isLoggable(TAG, Log.DEBUG); - - /** The maximum number of transactions to dump per ledger. */ - private static final int MAX_NUM_TRANSACTION_DUMP = 25; - /** - * The maximum amount of time we'll keep a transaction around for. - */ - private static final long MAX_TRANSACTION_AGE_MS = 8 * 24 * HOUR_IN_MILLIS; - - private static final String XML_TAG_HIGH_LEVEL_STATE = "irs-state"; - private static final String XML_TAG_LEDGER = "ledger"; - private static final String XML_TAG_TARE = "tare"; - private static final String XML_TAG_TRANSACTION = "transaction"; - private static final String XML_TAG_REWARD_BUCKET = "rewardBucket"; - private static final String XML_TAG_USER = "user"; - private static final String XML_TAG_PERIOD_REPORT = "report"; - - private static final String XML_ATTR_CTP = "ctp"; - private static final String XML_ATTR_DELTA = "delta"; - private static final String XML_ATTR_EVENT_ID = "eventId"; - private static final String XML_ATTR_TAG = "tag"; - private static final String XML_ATTR_START_TIME = "startTime"; - private static final String XML_ATTR_END_TIME = "endTime"; - private static final String XML_ATTR_PACKAGE_NAME = "pkgName"; - private static final String XML_ATTR_CURRENT_BALANCE = "currentBalance"; - private static final String XML_ATTR_USER_ID = "userId"; - private static final String XML_ATTR_VERSION = "version"; - private static final String XML_ATTR_LAST_RECLAMATION_TIME = "lastReclamationTime"; - private static final String XML_ATTR_LAST_STOCK_RECALCULATION_TIME = - "lastStockRecalculationTime"; - private static final String XML_ATTR_REMAINING_CONSUMABLE_CAKES = "remainingConsumableCakes"; - private static final String XML_ATTR_CONSUMPTION_LIMIT = "consumptionLimit"; - private static final String XML_ATTR_TIME_SINCE_FIRST_SETUP_MS = "timeSinceFirstSetup"; - private static final String XML_ATTR_PR_DISCHARGE = "discharge"; - private static final String XML_ATTR_PR_BATTERY_LEVEL = "batteryLevel"; - private static final String XML_ATTR_PR_PROFIT = "profit"; - private static final String XML_ATTR_PR_NUM_PROFIT = "numProfits"; - private static final String XML_ATTR_PR_LOSS = "loss"; - private static final String XML_ATTR_PR_NUM_LOSS = "numLoss"; - private static final String XML_ATTR_PR_REWARDS = "rewards"; - private static final String XML_ATTR_PR_NUM_REWARDS = "numRewards"; - private static final String XML_ATTR_PR_POS_REGULATIONS = "posRegulations"; - private static final String XML_ATTR_PR_NUM_POS_REGULATIONS = "numPosRegulations"; - private static final String XML_ATTR_PR_NEG_REGULATIONS = "negRegulations"; - private static final String XML_ATTR_PR_NUM_NEG_REGULATIONS = "numNegRegulations"; - private static final String XML_ATTR_PR_SCREEN_OFF_DURATION_MS = "screenOffDurationMs"; - private static final String XML_ATTR_PR_SCREEN_OFF_DISCHARGE_MAH = "screenOffDischargeMah"; - - /** Version of the file schema. */ - private static final int STATE_FILE_VERSION = 0; - /** Minimum amount of time between consecutive writes. */ - private static final long WRITE_DELAY = 30_000L; - - private final AtomicFile mStateFile; - private final InternalResourceService mIrs; - private final Analyst mAnalyst; - - /** - * The value of elapsed realtime since TARE was first setup that was read from disk. - * This will only be changed when the persisted file is read. - */ - private long mLoadedTimeSinceFirstSetup; - @GuardedBy("mIrs.getLock()") - private long mLastReclamationTime; - @GuardedBy("mIrs.getLock()") - private long mLastStockRecalculationTime; - @GuardedBy("mIrs.getLock()") - private long mSatiatedConsumptionLimit; - @GuardedBy("mIrs.getLock()") - private long mRemainingConsumableCakes; - @GuardedBy("mIrs.getLock()") - private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>(); - /** Offsets used to calculate the total realtime since each user was added. */ - @GuardedBy("mIrs.getLock()") - private final SparseLongArray mRealtimeSinceUsersAddedOffsets = new SparseLongArray(); - - private final Runnable mCleanRunnable = this::cleanupLedgers; - private final Runnable mWriteRunnable = this::writeState; - - Scribe(InternalResourceService irs, Analyst analyst) { - this(irs, analyst, Environment.getDataSystemDirectory()); - } - - @VisibleForTesting - Scribe(InternalResourceService irs, Analyst analyst, File dataDir) { - mIrs = irs; - mAnalyst = analyst; - - final File tareDir = new File(dataDir, "tare"); - //noinspection ResultOfMethodCallIgnored - tareDir.mkdirs(); - mStateFile = new AtomicFile(new File(tareDir, "state.xml"), "tare"); - } - - @GuardedBy("mIrs.getLock()") - void adjustRemainingConsumableCakesLocked(long delta) { - final long staleCakes = mRemainingConsumableCakes; - mRemainingConsumableCakes += delta; - if (mRemainingConsumableCakes < 0) { - Slog.w(TAG, "Overdrew consumable cakes by " + cakeToString(-mRemainingConsumableCakes)); - // A negative value would interfere with allowing free actions, so set the minimum as 0. - mRemainingConsumableCakes = 0; - } - if (mRemainingConsumableCakes != staleCakes) { - // No point doing any work if there was no functional change. - postWrite(); - } - } - - @GuardedBy("mIrs.getLock()") - void discardLedgerLocked(final int userId, @NonNull final String pkgName) { - mLedgers.delete(userId, pkgName); - postWrite(); - } - - @GuardedBy("mIrs.getLock()") - void onUserRemovedLocked(final int userId) { - mLedgers.delete(userId); - mRealtimeSinceUsersAddedOffsets.delete(userId); - postWrite(); - } - - @GuardedBy("mIrs.getLock()") - long getSatiatedConsumptionLimitLocked() { - return mSatiatedConsumptionLimit; - } - - @GuardedBy("mIrs.getLock()") - long getLastReclamationTimeLocked() { - return mLastReclamationTime; - } - - @GuardedBy("mIrs.getLock()") - long getLastStockRecalculationTimeLocked() { - return mLastStockRecalculationTime; - } - - @GuardedBy("mIrs.getLock()") - @NonNull - Ledger getLedgerLocked(final int userId, @NonNull final String pkgName) { - Ledger ledger = mLedgers.get(userId, pkgName); - if (ledger == null) { - ledger = new Ledger(); - mLedgers.add(userId, pkgName, ledger); - } - return ledger; - } - - @GuardedBy("mIrs.getLock()") - @NonNull - SparseArrayMap<String, Ledger> getLedgersLocked() { - return mLedgers; - } - - /** - * Returns the sum of credits granted to all apps on the system. This is expensive so don't - * call it for normal operation. - */ - @GuardedBy("mIrs.getLock()") - long getCakesInCirculationForLoggingLocked() { - long sum = 0; - for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) { - for (int pIdx = mLedgers.numElementsForKeyAt(uIdx) - 1; pIdx >= 0; --pIdx) { - sum += mLedgers.valueAt(uIdx, pIdx).getCurrentBalance(); - } - } - return sum; - } - - /** Returns the cumulative elapsed realtime since TARE was first setup. */ - long getRealtimeSinceFirstSetupMs(long nowElapsed) { - return mLoadedTimeSinceFirstSetup + nowElapsed; - } - - /** Returns the total amount of cakes that remain to be consumed. */ - @GuardedBy("mIrs.getLock()") - long getRemainingConsumableCakesLocked() { - return mRemainingConsumableCakes; - } - - @GuardedBy("mIrs.getLock()") - SparseLongArray getRealtimeSinceUsersAddedLocked(long nowElapsed) { - final SparseLongArray realtimes = new SparseLongArray(); - for (int i = mRealtimeSinceUsersAddedOffsets.size() - 1; i >= 0; --i) { - realtimes.put(mRealtimeSinceUsersAddedOffsets.keyAt(i), - mRealtimeSinceUsersAddedOffsets.valueAt(i) + nowElapsed); - } - return realtimes; - } - - @GuardedBy("mIrs.getLock()") - void loadFromDiskLocked() { - mLedgers.clear(); - if (!recordExists()) { - mSatiatedConsumptionLimit = mIrs.getInitialSatiatedConsumptionLimitLocked(); - mRemainingConsumableCakes = mIrs.getConsumptionLimitLocked(); - return; - } - mSatiatedConsumptionLimit = 0; - mRemainingConsumableCakes = 0; - - final SparseArray<ArraySet<String>> installedPackagesPerUser = new SparseArray<>(); - final SparseArrayMap<String, InstalledPackageInfo> installedPackages = - mIrs.getInstalledPackages(); - for (int uIdx = installedPackages.numMaps() - 1; uIdx >= 0; --uIdx) { - final int userId = installedPackages.keyAt(uIdx); - - for (int pIdx = installedPackages.numElementsForKeyAt(uIdx) - 1; pIdx >= 0; --pIdx) { - final InstalledPackageInfo packageInfo = installedPackages.valueAt(uIdx, pIdx); - if (packageInfo.uid != InstalledPackageInfo.NO_UID) { - ArraySet<String> pkgsForUser = installedPackagesPerUser.get(userId); - if (pkgsForUser == null) { - pkgsForUser = new ArraySet<>(); - installedPackagesPerUser.put(userId, pkgsForUser); - } - pkgsForUser.add(packageInfo.packageName); - } - } - } - - final List<Analyst.Report> reports = new ArrayList<>(); - try (FileInputStream fis = mStateFile.openRead()) { - TypedXmlPullParser parser = Xml.resolvePullParser(fis); - - int eventType = parser.getEventType(); - while (eventType != XmlPullParser.START_TAG - && eventType != XmlPullParser.END_DOCUMENT) { - eventType = parser.next(); - } - if (eventType == XmlPullParser.END_DOCUMENT) { - if (DEBUG) { - Slog.w(TAG, "No persisted state."); - } - return; - } - - String tagName = parser.getName(); - if (XML_TAG_TARE.equals(tagName)) { - final int version = parser.getAttributeInt(null, XML_ATTR_VERSION); - if (version < 0 || version > STATE_FILE_VERSION) { - Slog.e(TAG, "Invalid version number (" + version + "), aborting file read"); - return; - } - } - - final long now = System.currentTimeMillis(); - final long endTimeCutoff = now - MAX_TRANSACTION_AGE_MS; - long earliestEndTime = Long.MAX_VALUE; - for (eventType = parser.next(); eventType != XmlPullParser.END_DOCUMENT; - eventType = parser.next()) { - if (eventType != XmlPullParser.START_TAG) { - continue; - } - tagName = parser.getName(); - if (tagName == null) { - continue; - } - - switch (tagName) { - case XML_TAG_HIGH_LEVEL_STATE: - mLastReclamationTime = - parser.getAttributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME); - mLastStockRecalculationTime = parser.getAttributeLong(null, - XML_ATTR_LAST_STOCK_RECALCULATION_TIME, 0); - mLoadedTimeSinceFirstSetup = - parser.getAttributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS, - // If there's no recorded time since first setup, then - // offset the current elapsed time so it doesn't shift the - // timing too much. - -SystemClock.elapsedRealtime()); - mSatiatedConsumptionLimit = - parser.getAttributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, - mIrs.getInitialSatiatedConsumptionLimitLocked()); - final long consumptionLimit = mIrs.getConsumptionLimitLocked(); - mRemainingConsumableCakes = Math.min(consumptionLimit, - parser.getAttributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_CAKES, - consumptionLimit)); - break; - case XML_TAG_USER: - earliestEndTime = Math.min(earliestEndTime, - readUserFromXmlLocked( - parser, installedPackagesPerUser, endTimeCutoff)); - break; - case XML_TAG_PERIOD_REPORT: - reports.add(readReportFromXml(parser)); - break; - default: - Slog.e(TAG, "Unexpected tag: " + tagName); - break; - } - } - mAnalyst.loadReports(reports); - scheduleCleanup(earliestEndTime); - } catch (IOException | XmlPullParserException e) { - Slog.wtf(TAG, "Error reading state from disk", e); - } - } - - @VisibleForTesting - void postWrite() { - TareHandlerThread.getHandler().postDelayed(mWriteRunnable, WRITE_DELAY); - } - - boolean recordExists() { - return mStateFile.exists(); - } - - @GuardedBy("mIrs.getLock()") - void setConsumptionLimitLocked(long limit) { - if (mRemainingConsumableCakes > limit) { - mRemainingConsumableCakes = limit; - } else if (limit > mSatiatedConsumptionLimit) { - final long diff = mSatiatedConsumptionLimit - mRemainingConsumableCakes; - mRemainingConsumableCakes = (limit - diff); - } - mSatiatedConsumptionLimit = limit; - postWrite(); - } - - @GuardedBy("mIrs.getLock()") - void setLastReclamationTimeLocked(long time) { - mLastReclamationTime = time; - postWrite(); - } - - @GuardedBy("mIrs.getLock()") - void setLastStockRecalculationTimeLocked(long time) { - mLastStockRecalculationTime = time; - postWrite(); - } - - @GuardedBy("mIrs.getLock()") - void setUserAddedTimeLocked(int userId, long timeElapsed) { - // Use the current time as an offset so that when we persist the time, it correctly persists - // as "time since now". - mRealtimeSinceUsersAddedOffsets.put(userId, -timeElapsed); - } - - @GuardedBy("mIrs.getLock()") - void tearDownLocked() { - TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable); - TareHandlerThread.getHandler().removeCallbacks(mWriteRunnable); - mLedgers.clear(); - mRemainingConsumableCakes = 0; - mSatiatedConsumptionLimit = 0; - mLastReclamationTime = 0; - } - - @VisibleForTesting - void writeImmediatelyForTesting() { - mWriteRunnable.run(); - } - - private void cleanupLedgers() { - synchronized (mIrs.getLock()) { - TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable); - long earliestEndTime = Long.MAX_VALUE; - for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) { - final int userId = mLedgers.keyAt(uIdx); - - for (int pIdx = mLedgers.numElementsForKey(userId) - 1; pIdx >= 0; --pIdx) { - final String pkgName = mLedgers.keyAt(uIdx, pIdx); - final Ledger ledger = mLedgers.get(userId, pkgName); - final Ledger.Transaction transaction = - ledger.removeOldTransactions(MAX_TRANSACTION_AGE_MS); - if (transaction != null) { - earliestEndTime = Math.min(earliestEndTime, transaction.endTimeMs); - } - } - } - scheduleCleanup(earliestEndTime); - } - } - - /** Returns the {@link String#intern() interned} String if it's not null. */ - @Nullable - private static String intern(@Nullable String val) { - return val == null ? null : val.intern(); - } - - /** - * @param parser Xml parser at the beginning of a "<ledger/>" tag. The next "parser.next()" call - * will take the parser into the body of the ledger tag. - * @return Newly instantiated ledger holding all the information we just read out of the xml - * tag, and the package name associated with the ledger. - */ - @Nullable - private static Pair<String, Ledger> readLedgerFromXml(TypedXmlPullParser parser, - ArraySet<String> validPackages, long endTimeCutoff) - throws XmlPullParserException, IOException { - final String pkgName; - final long curBalance; - final List<Ledger.Transaction> transactions = new ArrayList<>(); - final List<Ledger.RewardBucket> rewardBuckets = new ArrayList<>(); - - pkgName = intern(parser.getAttributeValue(null, XML_ATTR_PACKAGE_NAME)); - curBalance = parser.getAttributeLong(null, XML_ATTR_CURRENT_BALANCE); - - final boolean isInstalled = validPackages.contains(pkgName); - if (!isInstalled) { - // Don't return early since we need to go through all the transaction tags and get - // to the end of the ledger tag. - Slog.w(TAG, "Invalid pkg " + pkgName + " is saved to disk"); - } - - for (int eventType = parser.next(); eventType != XmlPullParser.END_DOCUMENT; - eventType = parser.next()) { - final String tagName = parser.getName(); - if (eventType == XmlPullParser.END_TAG) { - if (XML_TAG_LEDGER.equals(tagName)) { - // We've reached the end of the ledger tag. - break; - } - continue; - } - if (eventType != XmlPullParser.START_TAG || tagName == null) { - Slog.e(TAG, "Unexpected event: (" + eventType + ") " + tagName); - return null; - } - if (!isInstalled) { - continue; - } - if (DEBUG) { - Slog.d(TAG, "Starting ledger tag: " + tagName); - } - switch (tagName) { - case XML_TAG_TRANSACTION: - final long endTime = parser.getAttributeLong(null, XML_ATTR_END_TIME); - if (endTime <= endTimeCutoff) { - if (DEBUG) { - Slog.d(TAG, "Skipping event because it's too old."); - } - continue; - } - final String tag = intern(parser.getAttributeValue(null, XML_ATTR_TAG)); - final long startTime = parser.getAttributeLong(null, XML_ATTR_START_TIME); - final int eventId = parser.getAttributeInt(null, XML_ATTR_EVENT_ID); - final long delta = parser.getAttributeLong(null, XML_ATTR_DELTA); - final long ctp = parser.getAttributeLong(null, XML_ATTR_CTP); - transactions.add( - new Ledger.Transaction(startTime, endTime, eventId, tag, delta, ctp)); - break; - case XML_TAG_REWARD_BUCKET: - rewardBuckets.add(readRewardBucketFromXml(parser)); - break; - default: - // Expecting only "transaction" and "rewardBucket" tags. - Slog.e(TAG, "Unexpected event: (" + eventType + ") " + tagName); - return null; - } - } - - if (!isInstalled) { - return null; - } - return Pair.create(pkgName, new Ledger(curBalance, transactions, rewardBuckets)); - } - - /** - * @param parser Xml parser at the beginning of a "<user>" tag. The next "parser.next()" call - * will take the parser into the body of the user tag. - * @return The earliest valid transaction end time found for the user. - */ - @GuardedBy("mIrs.getLock()") - private long readUserFromXmlLocked(TypedXmlPullParser parser, - SparseArray<ArraySet<String>> installedPackagesPerUser, - long endTimeCutoff) throws XmlPullParserException, IOException { - int curUser = parser.getAttributeInt(null, XML_ATTR_USER_ID); - final ArraySet<String> installedPackages = installedPackagesPerUser.get(curUser); - if (installedPackages == null) { - Slog.w(TAG, "Invalid user " + curUser + " is saved to disk"); - curUser = UserHandle.USER_NULL; - // Don't return early since we need to go through all the ledger tags and get to the end - // of the user tag. - } - if (curUser != UserHandle.USER_NULL) { - mRealtimeSinceUsersAddedOffsets.put(curUser, - parser.getAttributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS, - // If there's no recorded time since first setup, then - // offset the current elapsed time so it doesn't shift the - // timing too much. - -SystemClock.elapsedRealtime())); - } - long earliestEndTime = Long.MAX_VALUE; - - for (int eventType = parser.next(); eventType != XmlPullParser.END_DOCUMENT; - eventType = parser.next()) { - final String tagName = parser.getName(); - if (eventType == XmlPullParser.END_TAG) { - if (XML_TAG_USER.equals(tagName)) { - // We've reached the end of the user tag. - break; - } - continue; - } - if (XML_TAG_LEDGER.equals(tagName)) { - if (curUser == UserHandle.USER_NULL) { - continue; - } - final Pair<String, Ledger> ledgerData = - readLedgerFromXml(parser, installedPackages, endTimeCutoff); - if (ledgerData == null) { - continue; - } - final Ledger ledger = ledgerData.second; - if (ledger != null) { - mLedgers.add(curUser, ledgerData.first, ledger); - final Ledger.Transaction transaction = ledger.getEarliestTransaction(); - if (transaction != null) { - earliestEndTime = Math.min(earliestEndTime, transaction.endTimeMs); - } - } - } else { - Slog.e(TAG, "Unknown tag: " + tagName); - } - } - - return earliestEndTime; - } - - /** - * @param parser Xml parser at the beginning of a {@link #XML_TAG_PERIOD_REPORT} tag. The next - * "parser.next()" call will take the parser into the body of the report tag. - * @return Newly instantiated Report holding all the information we just read out of the xml tag - */ - @NonNull - private static Analyst.Report readReportFromXml(TypedXmlPullParser parser) - throws XmlPullParserException, IOException { - final Analyst.Report report = new Analyst.Report(); - - report.cumulativeBatteryDischarge = parser.getAttributeInt(null, XML_ATTR_PR_DISCHARGE); - report.currentBatteryLevel = parser.getAttributeInt(null, XML_ATTR_PR_BATTERY_LEVEL); - report.cumulativeProfit = parser.getAttributeLong(null, XML_ATTR_PR_PROFIT); - report.numProfitableActions = parser.getAttributeInt(null, XML_ATTR_PR_NUM_PROFIT); - report.cumulativeLoss = parser.getAttributeLong(null, XML_ATTR_PR_LOSS); - report.numUnprofitableActions = parser.getAttributeInt(null, XML_ATTR_PR_NUM_LOSS); - report.cumulativeRewards = parser.getAttributeLong(null, XML_ATTR_PR_REWARDS); - report.numRewards = parser.getAttributeInt(null, XML_ATTR_PR_NUM_REWARDS); - report.cumulativePositiveRegulations = - parser.getAttributeLong(null, XML_ATTR_PR_POS_REGULATIONS); - report.numPositiveRegulations = - parser.getAttributeInt(null, XML_ATTR_PR_NUM_POS_REGULATIONS); - report.cumulativeNegativeRegulations = - parser.getAttributeLong(null, XML_ATTR_PR_NEG_REGULATIONS); - report.numNegativeRegulations = - parser.getAttributeInt(null, XML_ATTR_PR_NUM_NEG_REGULATIONS); - report.screenOffDurationMs = - parser.getAttributeLong(null, XML_ATTR_PR_SCREEN_OFF_DURATION_MS, 0); - report.screenOffDischargeMah = - parser.getAttributeLong(null, XML_ATTR_PR_SCREEN_OFF_DISCHARGE_MAH, 0); - - return report; - } - - /** - * @param parser Xml parser at the beginning of a {@value #XML_TAG_REWARD_BUCKET} tag. The next - * "parser.next()" call will take the parser into the body of the tag. - * @return Newly instantiated {@link Ledger.RewardBucket} holding all the information we just - * read out of the xml tag. - */ - @Nullable - private static Ledger.RewardBucket readRewardBucketFromXml(TypedXmlPullParser parser) - throws XmlPullParserException, IOException { - - final Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket(); - - rewardBucket.startTimeMs = parser.getAttributeLong(null, XML_ATTR_START_TIME); - - for (int eventType = parser.next(); eventType != XmlPullParser.END_DOCUMENT; - eventType = parser.next()) { - final String tagName = parser.getName(); - if (eventType == XmlPullParser.END_TAG) { - if (XML_TAG_REWARD_BUCKET.equals(tagName)) { - // We've reached the end of the rewardBucket tag. - break; - } - continue; - } - if (eventType != XmlPullParser.START_TAG || !XML_ATTR_DELTA.equals(tagName)) { - // Expecting only delta tags. - Slog.e(TAG, "Unexpected event: (" + eventType + ") " + tagName); - return null; - } - - final int eventId = parser.getAttributeInt(null, XML_ATTR_EVENT_ID); - final long delta = parser.getAttributeLong(null, XML_ATTR_DELTA); - rewardBucket.cumulativeDelta.put(eventId, delta); - } - - return rewardBucket; - } - - private void scheduleCleanup(long earliestEndTime) { - if (earliestEndTime == Long.MAX_VALUE) { - return; - } - // This is just cleanup to manage memory. We don't need to do it too often or at the exact - // intended real time, so the delay that comes from using the Handler (and is limited - // to uptime) should be fine. - final long delayMs = Math.max(HOUR_IN_MILLIS, - earliestEndTime + MAX_TRANSACTION_AGE_MS - System.currentTimeMillis()); - TareHandlerThread.getHandler().postDelayed(mCleanRunnable, delayMs); - } - - private void writeState() { - synchronized (mIrs.getLock()) { - TareHandlerThread.getHandler().removeCallbacks(mWriteRunnable); - // Remove mCleanRunnable callbacks since we're going to clean up the ledgers before - // writing anyway. - TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable); - if (mIrs.getEnabledMode() == ENABLED_MODE_OFF) { - // If it's no longer enabled, we would have cleared all the data in memory and would - // accidentally write an empty file, thus deleting all the history. - return; - } - long earliestStoredEndTime = Long.MAX_VALUE; - try (FileOutputStream fos = mStateFile.startWrite()) { - TypedXmlSerializer out = Xml.resolveSerializer(fos); - out.startDocument(null, true); - - out.startTag(null, XML_TAG_TARE); - out.attributeInt(null, XML_ATTR_VERSION, STATE_FILE_VERSION); - - out.startTag(null, XML_TAG_HIGH_LEVEL_STATE); - out.attributeLong(null, XML_ATTR_LAST_RECLAMATION_TIME, mLastReclamationTime); - out.attributeLong(null, - XML_ATTR_LAST_STOCK_RECALCULATION_TIME, mLastStockRecalculationTime); - out.attributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS, - mLoadedTimeSinceFirstSetup + SystemClock.elapsedRealtime()); - out.attributeLong(null, XML_ATTR_CONSUMPTION_LIMIT, mSatiatedConsumptionLimit); - out.attributeLong(null, XML_ATTR_REMAINING_CONSUMABLE_CAKES, - mRemainingConsumableCakes); - out.endTag(null, XML_TAG_HIGH_LEVEL_STATE); - - for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) { - final int userId = mLedgers.keyAt(uIdx); - earliestStoredEndTime = Math.min(earliestStoredEndTime, - writeUserLocked(out, userId)); - } - - List<Analyst.Report> reports = mAnalyst.getReports(); - for (int i = 0, size = reports.size(); i < size; ++i) { - writeReport(out, reports.get(i)); - } - - out.endTag(null, XML_TAG_TARE); - - out.endDocument(); - mStateFile.finishWrite(fos); - } catch (IOException e) { - Slog.e(TAG, "Error writing state to disk", e); - } - scheduleCleanup(earliestStoredEndTime); - } - } - - @GuardedBy("mIrs.getLock()") - private long writeUserLocked(@NonNull TypedXmlSerializer out, final int userId) - throws IOException { - final int uIdx = mLedgers.indexOfKey(userId); - long earliestStoredEndTime = Long.MAX_VALUE; - - out.startTag(null, XML_TAG_USER); - out.attributeInt(null, XML_ATTR_USER_ID, userId); - out.attributeLong(null, XML_ATTR_TIME_SINCE_FIRST_SETUP_MS, - mRealtimeSinceUsersAddedOffsets.get(userId, mLoadedTimeSinceFirstSetup) - + SystemClock.elapsedRealtime()); - for (int pIdx = mLedgers.numElementsForKey(userId) - 1; pIdx >= 0; --pIdx) { - final String pkgName = mLedgers.keyAt(uIdx, pIdx); - final Ledger ledger = mLedgers.get(userId, pkgName); - // Remove old transactions so we don't waste space storing them. - ledger.removeOldTransactions(MAX_TRANSACTION_AGE_MS); - - out.startTag(null, XML_TAG_LEDGER); - out.attribute(null, XML_ATTR_PACKAGE_NAME, pkgName); - out.attributeLong(null, - XML_ATTR_CURRENT_BALANCE, ledger.getCurrentBalance()); - - final List<Ledger.Transaction> transactions = ledger.getTransactions(); - for (int t = 0; t < transactions.size(); ++t) { - Ledger.Transaction transaction = transactions.get(t); - if (t == 0) { - earliestStoredEndTime = Math.min(earliestStoredEndTime, transaction.endTimeMs); - } - writeTransaction(out, transaction); - } - - final List<Ledger.RewardBucket> rewardBuckets = ledger.getRewardBuckets(); - for (int r = 0; r < rewardBuckets.size(); ++r) { - writeRewardBucket(out, rewardBuckets.get(r)); - } - out.endTag(null, XML_TAG_LEDGER); - } - out.endTag(null, XML_TAG_USER); - - return earliestStoredEndTime; - } - - private static void writeTransaction(@NonNull TypedXmlSerializer out, - @NonNull Ledger.Transaction transaction) throws IOException { - out.startTag(null, XML_TAG_TRANSACTION); - out.attributeLong(null, XML_ATTR_START_TIME, transaction.startTimeMs); - out.attributeLong(null, XML_ATTR_END_TIME, transaction.endTimeMs); - out.attributeInt(null, XML_ATTR_EVENT_ID, transaction.eventId); - if (transaction.tag != null) { - out.attribute(null, XML_ATTR_TAG, transaction.tag); - } - out.attributeLong(null, XML_ATTR_DELTA, transaction.delta); - out.attributeLong(null, XML_ATTR_CTP, transaction.ctp); - out.endTag(null, XML_TAG_TRANSACTION); - } - - private static void writeRewardBucket(@NonNull TypedXmlSerializer out, - @NonNull Ledger.RewardBucket rewardBucket) throws IOException { - final int numEvents = rewardBucket.cumulativeDelta.size(); - if (numEvents == 0) { - return; - } - out.startTag(null, XML_TAG_REWARD_BUCKET); - out.attributeLong(null, XML_ATTR_START_TIME, rewardBucket.startTimeMs); - for (int i = 0; i < numEvents; ++i) { - out.startTag(null, XML_ATTR_DELTA); - out.attributeInt(null, XML_ATTR_EVENT_ID, rewardBucket.cumulativeDelta.keyAt(i)); - out.attributeLong(null, XML_ATTR_DELTA, rewardBucket.cumulativeDelta.valueAt(i)); - out.endTag(null, XML_ATTR_DELTA); - } - out.endTag(null, XML_TAG_REWARD_BUCKET); - } - - private static void writeReport(@NonNull TypedXmlSerializer out, - @NonNull Analyst.Report report) throws IOException { - out.startTag(null, XML_TAG_PERIOD_REPORT); - out.attributeInt(null, XML_ATTR_PR_DISCHARGE, report.cumulativeBatteryDischarge); - out.attributeInt(null, XML_ATTR_PR_BATTERY_LEVEL, report.currentBatteryLevel); - out.attributeLong(null, XML_ATTR_PR_PROFIT, report.cumulativeProfit); - out.attributeInt(null, XML_ATTR_PR_NUM_PROFIT, report.numProfitableActions); - out.attributeLong(null, XML_ATTR_PR_LOSS, report.cumulativeLoss); - out.attributeInt(null, XML_ATTR_PR_NUM_LOSS, report.numUnprofitableActions); - out.attributeLong(null, XML_ATTR_PR_REWARDS, report.cumulativeRewards); - out.attributeInt(null, XML_ATTR_PR_NUM_REWARDS, report.numRewards); - out.attributeLong(null, XML_ATTR_PR_POS_REGULATIONS, report.cumulativePositiveRegulations); - out.attributeInt(null, XML_ATTR_PR_NUM_POS_REGULATIONS, report.numPositiveRegulations); - out.attributeLong(null, XML_ATTR_PR_NEG_REGULATIONS, report.cumulativeNegativeRegulations); - out.attributeInt(null, XML_ATTR_PR_NUM_NEG_REGULATIONS, report.numNegativeRegulations); - out.attributeLong(null, XML_ATTR_PR_SCREEN_OFF_DURATION_MS, report.screenOffDurationMs); - out.attributeLong(null, XML_ATTR_PR_SCREEN_OFF_DISCHARGE_MAH, report.screenOffDischargeMah); - out.endTag(null, XML_TAG_PERIOD_REPORT); - } - - @GuardedBy("mIrs.getLock()") - void dumpLocked(IndentingPrintWriter pw, boolean dumpAll) { - pw.println("Ledgers:"); - pw.increaseIndent(); - mLedgers.forEach((userId, pkgName, ledger) -> { - pw.print(appToString(userId, pkgName)); - if (mIrs.isSystem(userId, pkgName)) { - pw.print(" (system)"); - } - pw.println(); - pw.increaseIndent(); - ledger.dump(pw, dumpAll ? Integer.MAX_VALUE : MAX_NUM_TRANSACTION_DUMP); - pw.decreaseIndent(); - }); - pw.decreaseIndent(); - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING deleted file mode 100644 index e194b8dbe33d..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING +++ /dev/null @@ -1,32 +0,0 @@ -{ - "presubmit": [ - { - "name": "FrameworksMockingServicesTests", - "options": [ - {"include-filter": "com.android.server.tare"}, - {"exclude-annotation": "androidx.test.filters.FlakyTest"} - ] - }, - { - "name": "FrameworksServicesTests", - "options": [ - {"include-filter": "com.android.server.tare"}, - {"exclude-annotation": "androidx.test.filters.FlakyTest"} - ] - } - ], - "postsubmit": [ - { - "name": "FrameworksMockingServicesTests", - "options": [ - {"include-filter": "com.android.server.tare"} - ] - }, - { - "name": "FrameworksServicesTests", - "options": [ - {"include-filter": "com.android.server.tare"} - ] - } - ] -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java deleted file mode 100644 index 65ef8bfe0d06..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import android.os.Handler; -import android.os.HandlerExecutor; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Trace; - -import java.util.concurrent.Executor; - -/** - * Singleton thread for all of TARE. - * - * @see com.android.internal.os.BackgroundThread - */ -final class TareHandlerThread extends HandlerThread { - - private static TareHandlerThread sInstance; - private static Executor sHandlerExecutor; - private static Handler sHandler; - - private TareHandlerThread() { - super("tare"); - } - - private static void ensureThreadLocked() { - if (sInstance == null) { - sInstance = new TareHandlerThread(); - sInstance.start(); - final Looper looper = sInstance.getLooper(); - looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER); - sHandler = new Handler(sInstance.getLooper()); - sHandlerExecutor = new HandlerExecutor(sHandler); - } - } - - static TareHandlerThread get() { - synchronized (TareHandlerThread.class) { - ensureThreadLocked(); - } - return sInstance; - } - - /** Returns the singleton handler executor for TareHandlerThread */ - public static Executor getExecutor() { - synchronized (TareHandlerThread.class) { - ensureThreadLocked(); - return sHandlerExecutor; - } - } - - /** Returns the singleton handler for TareHandlerThread. */ - public static Handler getHandler() { - synchronized (TareHandlerThread.class) { - ensureThreadLocked(); - } - return sHandler; - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareShellCommand.java b/apex/jobscheduler/service/java/com/android/server/tare/TareShellCommand.java deleted file mode 100644 index 5e380b408d01..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/TareShellCommand.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import android.Manifest; -import android.annotation.NonNull; -import android.content.pm.PackageManager; -import android.os.Binder; - -import com.android.modules.utils.BasicShellCommandHandler; - -import java.io.PrintWriter; - -/** - * Shell command handler for TARE. - */ -public class TareShellCommand extends BasicShellCommandHandler { - static final int COMMAND_ERROR = -1; - static final int COMMAND_SUCCESS = 0; - - private final InternalResourceService mIrs; - - public TareShellCommand(@NonNull InternalResourceService irs) { - mIrs = irs; - } - - @Override - public int onCommand(String cmd) { - final PrintWriter pw = getOutPrintWriter(); - try { - switch (cmd != null ? cmd : "") { - case "clear-vip": - return runClearVip(pw); - case "set-vip": - return runSetVip(pw); - default: - return handleDefaultCommands(cmd); - } - } catch (Exception e) { - pw.println("Exception: " + e); - } - return COMMAND_ERROR; - } - - @Override - public void onHelp() { - final PrintWriter pw = getOutPrintWriter(); - - pw.println("TARE commands:"); - pw.println(" help"); - pw.println(" Print this help text."); - pw.println(" clear-vip"); - pw.println(" Clears all VIP settings resulting from previous calls using `set-vip` and"); - pw.println(" resets them all to default."); - pw.println(" set-vip <USER_ID> <PACKAGE> <true|false|default>"); - pw.println(" Designate the app as a Very Important Package or not. A VIP is allowed to"); - pw.println(" do as much work as it wants, regardless of TARE state."); - pw.println(" The user ID must be an explicit user ID. USER_ALL, CURRENT, etc. are not"); - pw.println(" supported."); - pw.println(); - } - - private void checkPermission(@NonNull String operation) throws Exception { - final int perm = mIrs.getContext() - .checkCallingOrSelfPermission(Manifest.permission.CHANGE_APP_IDLE_STATE); - if (perm != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Uid " + Binder.getCallingUid() - + " not permitted to " + operation); - } - } - - private int runClearVip(@NonNull PrintWriter pw) throws Exception { - checkPermission("clear vip"); - - final long ident = Binder.clearCallingIdentity(); - try { - return mIrs.executeClearVip(pw); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - private int runSetVip(@NonNull PrintWriter pw) throws Exception { - checkPermission("modify vip"); - - final int userId = Integer.parseInt(getNextArgRequired()); - final String pkgName = getNextArgRequired(); - final String vipState = getNextArgRequired(); - final Boolean isVip = "default".equals(vipState) ? null : Boolean.valueOf(vipState); - - final long ident = Binder.clearCallingIdentity(); - try { - return mIrs.executeSetVip(pw, userId, pkgName, isVip); - } finally { - Binder.restoreCallingIdentity(ident); - } - } -} diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java deleted file mode 100644 index aa4c75a0be80..000000000000 --- a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static android.app.tare.EconomyManager.CAKE_IN_ARC; - -import android.annotation.NonNull; - -import com.android.internal.annotations.VisibleForTesting; - -import java.time.Clock; - -class TareUtils { - @VisibleForTesting - static Clock sSystemClock = Clock.systemUTC(); - - static long getCurrentTimeMillis() { - return sSystemClock.millis(); - } - - static int cakeToArc(long cakes) { - return (int) (cakes / CAKE_IN_ARC); - } - - @NonNull - static String cakeToString(long cakes) { - if (cakes == 0) { - return "0 ARCs"; - } - final long sub = cakes % CAKE_IN_ARC; - final long arcs = cakeToArc(cakes); - if (arcs == 0) { - return sub == 1 - ? sub + " cake" - : sub + " cakes"; - } - StringBuilder sb = new StringBuilder(); - sb.append(arcs); - if (sub != 0) { - sb.append(".").append(String.format("%03d", Math.abs(sub) / (CAKE_IN_ARC / 1000))); - } - sb.append(" ARC"); - if (arcs != 1 || sub != 0) { - sb.append("s"); - } - return sb.toString(); - } - - /** Returns a standardized format for printing userId+pkgName combinations. */ - @NonNull - static String appToString(int userId, String pkgName) { - return "<" + userId + ">" + pkgName; - } -} diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 61897030a991..40ee57e46943 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -846,17 +846,6 @@ package android.app.prediction { } -package android.app.tare { - - public class EconomyManager { - method public int getEnabledMode(); - field public static final int ENABLED_MODE_OFF = 0; // 0x0 - field public static final int ENABLED_MODE_SHADOW = 2; // 0x2 - field public static final String KEY_ENABLE_TARE_MODE = "enable_tare_mode"; - } - -} - package android.app.usage { public class StorageStatsManager { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index c0c91cbdbc35..b706cae17547 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5914,15 +5914,6 @@ public abstract class Context { public static final String JOB_SCHEDULER_SERVICE = "jobscheduler"; /** - * Use with {@link #getSystemService(String)} to retrieve a - * {@link android.app.tare.EconomyManager} instance for understanding economic standing. - * @see #getSystemService(String) - * @hide - * @see android.app.tare.EconomyManager - */ - public static final String RESOURCE_ECONOMY_SERVICE = "tare"; - - /** * Use with {@link #getSystemService(String)} to retrieve a {@link * android.service.persistentdata.PersistentDataBlockManager} instance * for interacting with a storage device that lives across factory resets. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7b3dee7ea8e7..aa2f85dd2287 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -15938,41 +15938,6 @@ public final class Settings { = "forced_app_standby_for_small_battery_enabled"; /** - * Whether to enable the TARE subsystem or not. - * Valid values are - * {@link android.app.tare.EconomyManager#ENABLE_TARE_ON EconomyManager.ENABLE_TARE_*}. - * - * @hide - */ - public static final String ENABLE_TARE = "enable_tare"; - - /** - * Whether to show the TARE page in Developer Options or not. - * 1 = true, everything else = false - * - * @hide - */ - public static final String SHOW_TARE_DEVELOPER_OPTIONS = "show_tare_developer_options"; - - /** - * Settings for AlarmManager's TARE EconomicPolicy (list of its economic factors). - * - * Keys are listed in {@link android.app.tare.EconomyManager}. - * - * @hide - */ - public static final String TARE_ALARM_MANAGER_CONSTANTS = "tare_alarm_manager_constants"; - - /** - * Settings for JobScheduler's TARE EconomicPolicy (list of its economic factors). - * - * Keys are listed in {@link android.app.tare.EconomyManager}. - * - * @hide - */ - public static final String TARE_JOB_SCHEDULER_CONSTANTS = "tare_job_scheduler_constants"; - - /** * Whether or not to enable the User Absent, Radios Off feature on small battery devices. * Type: int (0 for false, 1 for true) * Default: 0 diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index 6def40be7977..c274534c8d36 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -161,10 +161,6 @@ public class GlobalSettingsValidators { Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, PERCENTAGE_INTEGER_VALIDATOR); VALIDATORS.put(Global.BLUETOOTH_ON, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.CLOCKWORK_HOME_READY, ANY_STRING_VALIDATOR); - VALIDATORS.put(Global.ENABLE_TARE, - new DiscreteValueValidator(new String[] {"0", "1", "2"})); - VALIDATORS.put(Global.TARE_ALARM_MANAGER_CONSTANTS, ANY_STRING_VALIDATOR); - VALIDATORS.put(Global.TARE_JOB_SCHEDULER_CONSTANTS, ANY_STRING_VALIDATOR); VALIDATORS.put(Global.PRIVATE_DNS_MODE, ANY_STRING_VALIDATOR); VALIDATORS.put(Global.PRIVATE_DNS_SPECIFIER, ANY_STRING_VALIDATOR); VALIDATORS.put(Global.SOFT_AP_TIMEOUT_ENABLED, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 8cafe5faaa09..f42efe224e4b 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -223,7 +223,6 @@ public class SettingsBackupTest { Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE, Settings.Global.ENABLE_DISKSTATS_LOGGING, Settings.Global.ENABLE_EPHEMERAL_FEATURE, - Settings.Global.ENABLE_TARE, Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED, Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS, @@ -408,7 +407,6 @@ public class SettingsBackupTest { Settings.Global.SHOW_PEOPLE_SPACE, Settings.Global.SHOW_NEW_NOTIF_DISMISS, Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG, - Settings.Global.SHOW_TARE_DEVELOPER_OPTIONS, Settings.Global.SHOW_TEMPERATURE_WARNING, Settings.Global.SHOW_USB_TEMPERATURE_ALARM, Settings.Global.SIGNED_CONFIG_VERSION, @@ -435,8 +433,6 @@ public class SettingsBackupTest { Settings.Global.SYS_UIDCPUPOWER, Settings.Global.SYS_TRACED, Settings.Global.FPS_DEVISOR, - Settings.Global.TARE_ALARM_MANAGER_CONSTANTS, - Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS, Settings.Global.TCP_DEFAULT_INIT_RWND, Settings.Global.TETHER_DUN_APN, Settings.Global.TETHER_DUN_REQUIRED, diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e202bbf022bc..0a7f49da0c31 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -324,8 +324,6 @@ public final class SystemServer implements Dumpable { "com.android.server.job.JobSchedulerService"; private static final String LOCK_SETTINGS_SERVICE_CLASS = "com.android.server.locksettings.LockSettingsService$Lifecycle"; - private static final String RESOURCE_ECONOMY_SERVICE_CLASS = - "com.android.server.tare.InternalResourceService"; private static final String STORAGE_MANAGER_SERVICE_CLASS = "com.android.server.StorageManagerService$Lifecycle"; private static final String STORAGE_STATS_SERVICE_CLASS = @@ -1640,11 +1638,6 @@ public final class SystemServer implements Dumpable { } // TODO(aml-jobscheduler): Think about how to do it properly. - t.traceBegin("StartResourceEconomy"); - mSystemServiceManager.startService(RESOURCE_ECONOMY_SERVICE_CLASS); - t.traceEnd(); - - // TODO(aml-jobscheduler): Think about how to do it properly. t.traceBegin("StartAlarmManagerService"); mSystemServiceManager.startService(ALARM_MANAGER_SERVICE_CLASS); t.traceEnd(); diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index ce5cee0b6113..c359412b6ccd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -69,7 +69,6 @@ import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REFRESH_ import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_ALARMS; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED; -import static com.android.server.alarm.AlarmManagerService.AlarmHandler.TARE_AFFORDABILITY_CHANGED; import static com.android.server.alarm.AlarmManagerService.AlarmHandler.TEMPORARY_QUOTA_CHANGED; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA; import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW; @@ -126,7 +125,6 @@ import android.app.IAlarmListener; import android.app.IAlarmManager; import android.app.PendingIntent; import android.app.compat.CompatChanges; -import android.app.tare.EconomyManager; import android.app.usage.UsageStatsManagerInternal; import android.companion.virtual.VirtualDeviceManager; import android.content.ContentResolver; @@ -181,8 +179,6 @@ import com.android.server.SystemService; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.AndroidPackage; -import com.android.server.tare.AlarmManagerEconomicPolicy; -import com.android.server.tare.EconomyManagerInternal; import com.android.server.usage.AppStandbyInternal; import libcore.util.EmptyArray; @@ -264,8 +260,6 @@ public final class AlarmManagerServiceTest { @Mock private PowerManager.WakeLock mWakeLock; @Mock - private EconomyManagerInternal mEconomyManagerInternal; - @Mock DeviceConfig.Properties mDeviceConfigProperties; HashSet<String> mDeviceConfigKeys = new HashSet<>(); @@ -449,8 +443,6 @@ public final class AlarmManagerServiceTest { doReturn(mIActivityManager).when(ActivityManager::getService); doReturn(mDeviceIdleInternal).when( () -> LocalServices.getService(DeviceIdleInternal.class)); - doReturn(mEconomyManagerInternal).when( - () -> LocalServices.getService(EconomyManagerInternal.class)); doReturn(mPermissionManagerInternal).when( () -> LocalServices.getService(PermissionManagerServiceInternal.class)); doReturn(mActivityManagerInternal).when( @@ -544,7 +536,6 @@ public final class AlarmManagerServiceTest { mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); verify(mBatteryManager).isCharging(); - setTareEnabled(EconomyManager.ENABLED_MODE_OFF); mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW; mAllowWhileIdleWindow = mService.mConstants.ALLOW_WHILE_IDLE_WINDOW; @@ -720,12 +711,6 @@ public final class AlarmManagerServiceTest { mService.mConstants.onPropertiesChanged(mDeviceConfigProperties); } - private void setTareEnabled(int enabledMode) { - when(mEconomyManagerInternal.getEnabledMode(eq(AlarmManagerEconomicPolicy.POLICY_ALARM))) - .thenReturn(enabledMode); - mService.mConstants.onTareEnabledModeChanged(enabledMode); - } - /** * Lowers quotas to make testing feasible. Careful while calling as this will replace any * existing settings for the calling test. @@ -2101,44 +2086,6 @@ public final class AlarmManagerServiceTest { } @Test - public void tareThrottling() { - setTareEnabled(EconomyManager.ENABLED_MODE_ON); - final ArgumentCaptor<EconomyManagerInternal.AffordabilityChangeListener> listenerCaptor = - ArgumentCaptor.forClass(EconomyManagerInternal.AffordabilityChangeListener.class); - final ArgumentCaptor<EconomyManagerInternal.ActionBill> billCaptor = - ArgumentCaptor.forClass(EconomyManagerInternal.ActionBill.class); - - when(mEconomyManagerInternal - .canPayFor(eq(TEST_CALLING_USER), eq(TEST_CALLING_PACKAGE), billCaptor.capture())) - .thenReturn(false); - - final PendingIntent alarmPi = getNewMockPendingIntent(); - setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 15, alarmPi); - assertEquals(mNowElapsedTest + INDEFINITE_DELAY, mTestTimer.getElapsed()); - - final EconomyManagerInternal.ActionBill bill = billCaptor.getValue(); - verify(mEconomyManagerInternal).registerAffordabilityChangeListener( - eq(TEST_CALLING_USER), eq(TEST_CALLING_PACKAGE), - listenerCaptor.capture(), eq(bill)); - final EconomyManagerInternal.AffordabilityChangeListener listener = - listenerCaptor.getValue(); - - when(mEconomyManagerInternal - .canPayFor(eq(TEST_CALLING_USER), eq(TEST_CALLING_PACKAGE), eq(bill))) - .thenReturn(true); - listener.onAffordabilityChanged(TEST_CALLING_USER, TEST_CALLING_PACKAGE, bill, true); - assertAndHandleMessageSync(TARE_AFFORDABILITY_CHANGED); - assertEquals(mNowElapsedTest + 15, mTestTimer.getElapsed()); - - when(mEconomyManagerInternal - .canPayFor(eq(TEST_CALLING_USER), eq(TEST_CALLING_PACKAGE), eq(bill))) - .thenReturn(false); - listener.onAffordabilityChanged(TEST_CALLING_USER, TEST_CALLING_PACKAGE, bill, false); - assertAndHandleMessageSync(TARE_AFFORDABILITY_CHANGED); - assertEquals(mNowElapsedTest + INDEFINITE_DELAY, mTestTimer.getElapsed()); - } - - @Test public void dispatchOrder() throws Exception { setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0); @@ -3414,32 +3361,6 @@ public final class AlarmManagerServiceTest { } @Test - public void tareEventPushed_on() throws Exception { - setTareEnabled(EconomyManager.ENABLED_MODE_ON); - runTareEventPushed(); - } - - @Test - public void tareEventPushed_shadow() throws Exception { - setTareEnabled(EconomyManager.ENABLED_MODE_SHADOW); - runTareEventPushed(); - } - - private void runTareEventPushed() throws Exception { - for (int i = 0; i < 10; i++) { - final int type = (i % 2 == 1) ? ELAPSED_REALTIME : ELAPSED_REALTIME_WAKEUP; - setTestAlarm(type, mNowElapsedTest + i, getNewMockPendingIntent()); - } - - final ArrayList<Alarm> alarms = mService.mAlarmStore.remove((alarm) -> { - return alarm.creatorUid == TEST_CALLING_UID; - }); - mService.deliverAlarmsLocked(alarms, mNowElapsedTest); - verify(mEconomyManagerInternal, times(10)).noteInstantaneousEvent( - eq(TEST_CALLING_USER), eq(TEST_CALLING_PACKAGE), anyInt(), any()); - } - - @Test public void setTimeZoneImpl() { final long durationMs = 20000L; when(mActivityManagerInternal.getBootTimeTempAllowListDuration()).thenReturn(durationMs); diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java index 246b0f04171e..d802b9605d52 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java @@ -28,7 +28,6 @@ import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX; import static com.android.server.alarm.Alarm.NUM_POLICIES; import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX; import static com.android.server.alarm.AlarmManagerService.isExemptFromAppStandby; -import static com.android.server.alarm.AlarmManagerService.isExemptFromTare; import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE; import static com.android.server.alarm.Constants.TEST_CALLING_UID; @@ -196,22 +195,6 @@ public class AlarmTest { } @Test - public void testIsExemptFromTare() { - final long anything = 54321; // Arbitrary number, doesn't matter for this test. - - assertFalse("Basic alarm exempt", isExemptFromTare( - createDefaultAlarm(anything, anything, 0))); - assertFalse("FLAG_ALLOW_WHILE_IDLE_COMPAT exempt", isExemptFromTare( - createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_COMPAT))); - assertFalse("ALLOW_WHILE_IDLE exempt", isExemptFromTare( - createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE))); - - assertTrue("ALLOW_WHILE_IDLE_UNRESTRICTED not exempt", isExemptFromTare( - createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED))); - assertTrue("Alarm clock not exempt", isExemptFromTare(createAlarmClock(anything))); - } - - @Test public void snapshotImmutable() { final Alarm a = createDefaultAlarm(0, 0, 0); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index fab761057286..11f20e35b4b1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -85,7 +85,6 @@ import com.android.server.SystemServiceManager; import com.android.server.job.controllers.ConnectivityController; import com.android.server.job.controllers.JobStatus; import com.android.server.job.controllers.QuotaController; -import com.android.server.job.controllers.TareController; import com.android.server.pm.UserManagerInternal; import com.android.server.usage.AppStandbyInternal; @@ -617,12 +616,8 @@ public class JobSchedulerServiceTest { QuotaController quotaController = mService.getQuotaController(); spyOn(quotaController); - TareController tareController = mService.getTareController(); - spyOn(tareController); doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); - doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) - .when(tareController).getMaxJobExecutionTimeMsLocked(any()); grantRunUserInitiatedJobsPermission(true); assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, @@ -655,12 +650,8 @@ public class JobSchedulerServiceTest { QuotaController quotaController = mService.getQuotaController(); spyOn(quotaController); - TareController tareController = mService.getTareController(); - spyOn(tareController); doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); - doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) - .when(tareController).getMaxJobExecutionTimeMsLocked(any()); mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = false; mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; @@ -784,12 +775,8 @@ public class JobSchedulerServiceTest { QuotaController quotaController = mService.getQuotaController(); spyOn(quotaController); - TareController tareController = mService.getTareController(); - spyOn(tareController); doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); - doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) - .when(tareController).getMaxJobExecutionTimeMsLocked(any()); mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = true; mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java index c6608e61fc62..2d0f4b69e2fe 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java @@ -1345,13 +1345,11 @@ public class JobStatusTest { private void markExpeditedQuotaApproved(JobStatus job, boolean isApproved) { if (job.isRequestedExpeditedJob()) { job.setExpeditedJobQuotaApproved(sElapsedRealtimeClock.millis(), isApproved); - job.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), isApproved); } } private void markImplicitConstraintsSatisfied(JobStatus job, boolean isSatisfied) { job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied); - job.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied); job.setDeviceNotDozingConstraintSatisfied( sElapsedRealtimeClock.millis(), isSatisfied, false); job.setBackgroundNotRestrictedConstraintSatisfied( diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java index 1a95d66dd2b0..da5cbab3f7b1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java @@ -200,9 +200,7 @@ public class PrefetchControllerTest { js.setBackgroundNotRestrictedConstraintSatisfied( sElapsedRealtimeClock.millis(), true, false); js.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), true); - js.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), true); js.setExpeditedJobQuotaApproved(sElapsedRealtimeClock.millis(), true); - js.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), true); js.setFlexibilityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); return js; } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 07027645411d..6df4907af93c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -33,6 +33,7 @@ import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sSystemClock; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -400,8 +401,6 @@ public class QuotaControllerTest { /* state */ true, /* allowlisted */false); js.setBackgroundNotRestrictedConstraintSatisfied( sElapsedRealtimeClock.millis(), true, false); - js.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), true); - js.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), true); js.setFlexibilityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); return js; } diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java deleted file mode 100644 index a3917765e4b8..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.server.tare.TareTestUtils.assertLedgersEqual; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; - -import android.app.AlarmManager; -import android.content.Context; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.LocalServices; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -/** Tests various aspects of the Agent. */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class AgentTest { - private MockitoSession mMockingSession; - @Mock - private CompleteEconomicPolicy mEconomicPolicy; - @Mock - private Analyst mAnalyst; - @Mock - private Context mContext; - @Mock - private InternalResourceService mIrs; - - private Agent mAgent; - private Scribe mScribe; - - private static class MockScribe extends Scribe { - MockScribe(InternalResourceService irs, Analyst analyst) { - super(irs, analyst); - } - - @Override - void postWrite() { - // Do nothing - } - } - - @Before - public void setUp() { - mMockingSession = mockitoSession() - .initMocks(this) - .strictness(Strictness.LENIENT) - .mockStatic(LocalServices.class) - .startMocking(); - doReturn(mContext).when(mIrs).getContext(); - doReturn(mEconomicPolicy).when(mIrs).getCompleteEconomicPolicyLocked(); - doReturn(mIrs).when(mIrs).getLock(); - doReturn(mock(AlarmManager.class)).when(mContext).getSystemService(Context.ALARM_SERVICE); - mScribe = new MockScribe(mIrs, mAnalyst); - mAgent = new Agent(mIrs, mScribe, mAnalyst); - } - - @After - public void tearDown() { - mAgent.tearDownLocked(); - - if (mMockingSession != null) { - mMockingSession.finishMocking(); - } - } - - @Test - public void testAppRemoval() { - final long consumptionLimit = 1_000_000L; - final long remainingCakes = consumptionLimit / 2; - mScribe.setConsumptionLimitLocked(consumptionLimit); - mScribe.adjustRemainingConsumableCakesLocked(remainingCakes - consumptionLimit); - assertEquals(remainingCakes, mScribe.getRemainingConsumableCakesLocked()); - - final int userId = 0; - final String pkgName = "com.test"; - final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName); - - doReturn(consumptionLimit).when(mIrs).getConsumptionLimitLocked(); - doReturn(consumptionLimit).when(mEconomicPolicy) - .getMaxSatiatedBalance(anyInt(), anyString()); - - Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 10); - mAgent.recordTransactionLocked(userId, pkgName, ledger, transaction, false); - assertEquals(5, ledger.getCurrentBalance()); - assertEquals(remainingCakes - 10, mScribe.getRemainingConsumableCakesLocked()); - - mAgent.onPackageRemovedLocked(userId, pkgName); - assertEquals(remainingCakes - 10, mScribe.getRemainingConsumableCakesLocked()); - assertLedgersEqual(new Ledger(), mScribe.getLedgerLocked(userId, pkgName)); - } - - @Test - public void testRecordTransaction_UnderMax() { - Ledger ledger = new Ledger(); - - doReturn(1_000_000L).when(mIrs).getConsumptionLimitLocked(); - doReturn(1_000_000L).when(mEconomicPolicy).getMaxSatiatedBalance(anyInt(), anyString()); - - Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(5, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(1000, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(500, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L, 500); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(1_000_000L, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, -1_000_001L, 1000); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(-1, ledger.getCurrentBalance()); - } - - @Test - public void testRecordTransaction_MaxConsumptionLimit() { - Ledger ledger = new Ledger(); - - doReturn(1000L).when(mIrs).getConsumptionLimitLocked(); - doReturn(1_000_000L).when(mEconomicPolicy).getMaxSatiatedBalance(anyInt(), anyString()); - - Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(5, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(1000, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(500, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, 2000, 0); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(2500, ledger.getCurrentBalance()); - - // ConsumptionLimit can change as the battery level changes. Ledger balances shouldn't be - // affected. - doReturn(900L).when(mIrs).getConsumptionLimitLocked(); - - transaction = new Ledger.Transaction(0, 0, 0, null, 100, 0); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(2600, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, -50, 50); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(2550, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, -200, 100); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(2350, ledger.getCurrentBalance()); - - doReturn(800L).when(mIrs).getConsumptionLimitLocked(); - - transaction = new Ledger.Transaction(0, 0, 0, null, 100, 0); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(2450, ledger.getCurrentBalance()); - } - - @Test - public void testRecordTransaction_MaxSatiatedBalance() { - Ledger ledger = new Ledger(); - - doReturn(1_000_000L).when(mIrs).getConsumptionLimitLocked(); - doReturn(1000L).when(mEconomicPolicy).getMaxSatiatedBalance(anyInt(), anyString()); - - Ledger.Transaction transaction = new Ledger.Transaction(0, 0, 0, null, 5, 0); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(5, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, 995, 0); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(1000, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, -500, 250); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(500, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, 999_500L, 1000); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(1_000, ledger.getCurrentBalance()); - - // Shouldn't change in normal operation, but adding test case in case it does. - doReturn(900L).when(mEconomicPolicy).getMaxSatiatedBalance(anyInt(), anyString()); - - transaction = new Ledger.Transaction(0, 0, 0, null, 500, 0); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(1_000, ledger.getCurrentBalance()); - - transaction = new Ledger.Transaction(0, 0, 0, null, -1001, 500); - mAgent.recordTransactionLocked(0, "com.test", ledger, transaction, false); - assertEquals(-1, ledger.getCurrentBalance()); - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java deleted file mode 100644 index 799a7fe3a3db..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTrendCalculatorTest.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.util.ArraySet; -import android.util.SparseLongArray; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.tare.Agent.ActionAffordabilityNote; -import com.android.server.tare.Agent.OngoingEvent; -import com.android.server.tare.Agent.TrendCalculator; -import com.android.server.tare.EconomyManagerInternal.ActionBill; -import com.android.server.tare.EconomyManagerInternal.AffordabilityChangeListener; -import com.android.server.tare.EconomyManagerInternal.AnticipatedAction; - -import libcore.util.EmptyArray; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.List; - -/** Tests the TrendCalculator in the Agent. */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class AgentTrendCalculatorTest { - - private MockEconomicPolicy mEconomicPolicy; - - private static class MockEconomicPolicy extends EconomicPolicy { - private final SparseLongArray mEventCosts = new SparseLongArray(); - - MockEconomicPolicy(InternalResourceService irs) { - super(irs); - } - - @Override - long getMinSatiatedBalance(int userId, String pkgName) { - return 0; - } - - @Override - long getMaxSatiatedBalance(int userId, String pkgName) { - return 0; - } - - @Override - long getInitialSatiatedConsumptionLimit() { - return 0; - } - - @Override - long getMinSatiatedConsumptionLimit() { - return 0; - } - - @Override - long getMaxSatiatedConsumptionLimit() { - return 0; - } - - @Override - int[] getCostModifiers() { - return EmptyArray.INT; - } - - @Override - Action getAction(int actionId) { - if (mEventCosts.indexOfKey(actionId) < 0) { - return null; - } - return new Action(actionId, 0, mEventCosts.get(actionId)); - } - - @Override - Reward getReward(int rewardId) { - if (mEventCosts.indexOfKey(rewardId) < 0) { - return null; - } - return new Reward(rewardId, mEventCosts.get(rewardId), mEventCosts.get(rewardId), - 10 * mEventCosts.get(rewardId)); - } - } - - @Before - public void setUp() { - final InternalResourceService irs = mock(InternalResourceService.class); - when(irs.isVip(anyInt(), anyString(), anyLong())).thenReturn(false); - mEconomicPolicy = new MockEconomicPolicy(irs); - } - - @Test - public void testNoOngoingEvents() { - TrendCalculator trendCalculator = new TrendCalculator(); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 20); - - trendCalculator.reset(0, 0, null); - assertEquals("Expected not to cross lower threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - - ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>(); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - for (ActionAffordabilityNote note : affordabilityNotes) { - note.recalculateCosts(mEconomicPolicy, 0, "com.test.app"); - } - - trendCalculator.reset(1234, 1234, affordabilityNotes); - assertEquals("Expected not to cross lower threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - } - - @Test - public void testNoAffordabilityNotes() { - TrendCalculator trendCalculator = new TrendCalculator(); - - OngoingEvent[] events = new OngoingEvent[]{ - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1", - 1, new EconomicPolicy.Cost(1, 4)), - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2", - 2, new EconomicPolicy.Cost(3, 6)), - new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "3", 3, - new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 0, 3, 3)), - }; - - trendCalculator.reset(0, 100, null); - for (OngoingEvent event : events) { - trendCalculator.accept(event); - } - assertEquals(25_000, trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - - ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>(); - trendCalculator.reset(1234, 1234, affordabilityNotes); - for (OngoingEvent event : events) { - trendCalculator.accept(event); - } - assertEquals(308_000, trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - } - - @Test - public void testNoTrendToThreshold() { - TrendCalculator trendCalculator = new TrendCalculator(); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 10); - - ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>(); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1000))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - for (ActionAffordabilityNote note : affordabilityNotes) { - note.recalculateCosts(mEconomicPolicy, 0, "com.test.app"); - } - - // Balance is already above threshold and events are all positive delta. - // There should be no time to report. - trendCalculator.reset(1234, 1234, affordabilityNotes); - trendCalculator.accept( - new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1, - new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1))); - trendCalculator.accept( - new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2, - new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, - 3, 3, 3))); - - assertEquals("Expected not to cross lower threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - - // Balance is already below threshold and events are all negative delta. - // There should be no time to report. - trendCalculator.reset(1, 0, affordabilityNotes); - trendCalculator.accept( - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1", - 1, new EconomicPolicy.Cost(1, 1))); - trendCalculator.accept( - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2", - 2, new EconomicPolicy.Cost(3, 3))); - - assertEquals("Expected not to cross lower threshold", - 0, - trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - } - - @Test - public void testSimpleTrendToThreshold_Balance() { - TrendCalculator trendCalculator = new TrendCalculator(); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20); - - ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>(); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - for (ActionAffordabilityNote note : affordabilityNotes) { - note.recalculateCosts(mEconomicPolicy, 0, "com.test.app"); - } - - // Balance is below threshold and events are all positive delta. - // Should report the correct time to the upper threshold. - trendCalculator.reset(0, 1000, affordabilityNotes); - trendCalculator.accept( - new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1, - new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1))); - trendCalculator.accept( - new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2, - new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, - 3, 3, 3))); - - assertEquals("Expected not to cross lower threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals(5_000, trendCalculator.getTimeToCrossUpperThresholdMs()); - - // Balance is above the threshold and events are all negative delta. - // Should report the correct time to the lower threshold. - trendCalculator.reset(40, 100, affordabilityNotes); - trendCalculator.accept( - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1", - 1, new EconomicPolicy.Cost(1, 1))); - trendCalculator.accept( - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2", - 2, new EconomicPolicy.Cost(3, 3))); - - assertEquals(5_000, trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - } - - @Test - public void testSelectCorrectThreshold_Balance() { - TrendCalculator trendCalculator = new TrendCalculator(); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 15); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 10); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 5); - - ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>(); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - for (ActionAffordabilityNote note : affordabilityNotes) { - note.recalculateCosts(mEconomicPolicy, 0, "com.test.app"); - } - - // Balance is below threshold and events are all positive delta. - // Should report the correct time to the correct upper threshold. - trendCalculator.reset(0, 10_000, affordabilityNotes); - trendCalculator.accept( - new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1, - new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1))); - - assertEquals("Expected not to cross lower threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals(5_000, trendCalculator.getTimeToCrossUpperThresholdMs()); - - // Balance is above the threshold and events are all negative delta. - // Should report the correct time to the correct lower threshold. - trendCalculator.reset(30, 500, affordabilityNotes); - trendCalculator.accept( - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1", - 1, new EconomicPolicy.Cost(1, 1))); - - assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - } - - @Test - public void testTrendsToBothThresholds_Balance() { - TrendCalculator trendCalculator = new TrendCalculator(); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20); - mEconomicPolicy.mEventCosts.put(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 50); - - ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>(); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - for (ActionAffordabilityNote note : affordabilityNotes) { - note.recalculateCosts(mEconomicPolicy, 0, "com.test.app"); - } - - // Balance is between both thresholds and events are mixed positive/negative delta. - // Should report the correct time to each threshold. - trendCalculator.reset(35, 10_000, affordabilityNotes); - trendCalculator.accept( - new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1, - new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 3, 3, 3))); - trendCalculator.accept( - new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2", 2, - new EconomicPolicy.Reward(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, 2, - 2, 2))); - trendCalculator.accept( - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING, "3", - 3, new EconomicPolicy.Cost(2, 2))); - trendCalculator.accept( - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "4", - 4, new EconomicPolicy.Cost(3, 3))); - - assertEquals(3_000, trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals(3_000, trendCalculator.getTimeToCrossUpperThresholdMs()); - } - - @Test - public void testSimpleTrendToThreshold_ConsumptionLimit() { - TrendCalculator trendCalculator = new TrendCalculator(); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20); - - ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>(); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - for (ActionAffordabilityNote note : affordabilityNotes) { - note.recalculateCosts(mEconomicPolicy, 0, "com.test.app"); - } - - // Events are all negative delta. Consumable credits will run out before app's balance. - // Should report the correct time to the lower threshold. - trendCalculator.reset(10000, 40, affordabilityNotes); - trendCalculator.accept( - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1", - 1, new EconomicPolicy.Cost(1, 10))); - trendCalculator.accept( - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2", - 2, new EconomicPolicy.Cost(3, 40))); - - assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - } - - @Test - public void testSelectCorrectThreshold() { - TrendCalculator trendCalculator = new TrendCalculator(); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 15); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 10); - mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 5); - - ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>(); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of( - new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 1, 0))), - mock(AffordabilityChangeListener.class), mEconomicPolicy)); - for (ActionAffordabilityNote note : affordabilityNotes) { - note.recalculateCosts(mEconomicPolicy, 0, "com.test.app"); - } - - // Balance is above threshold, consumable credits is 0, and events are all positive delta. - // There should be no time to the upper threshold since consumable credits is the limiting - // factor. - trendCalculator.reset(10_000, 0, affordabilityNotes); - trendCalculator.accept( - new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1", 1, - new EconomicPolicy.Reward(EconomicPolicy.REWARD_TOP_ACTIVITY, 1, 1, 1))); - - assertEquals("Expected not to cross lower threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - - // Balance is above threshold, consumable credits is low, and events are all negative delta. - trendCalculator.reset(10_000, 4, affordabilityNotes); - trendCalculator.accept( - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1", - 1, new EconomicPolicy.Cost(1, 10))); - - assertEquals(4000, trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - - // Balance is above threshold, consumable credits is 0, and events are all negative delta. - // Time to the lower threshold should be 0 since consumable credits is already 0. - trendCalculator.reset(10_000, 0, affordabilityNotes); - trendCalculator.accept( - new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1", - 1, new EconomicPolicy.Cost(1, 10))); - - assertEquals(0, trendCalculator.getTimeToCrossLowerThresholdMs()); - assertEquals("Expected not to cross upper threshold", - TrendCalculator.WILL_NOT_CROSS_THRESHOLD, - trendCalculator.getTimeToCrossUpperThresholdMs()); - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java deleted file mode 100644 index 77723d77c121..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/tare/AlarmManagerEconomicPolicyTest.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static android.app.tare.EconomyManager.arcToCake; -import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; - -import android.app.ActivityManager; -import android.app.IActivityManager; -import android.app.tare.EconomyManager; -import android.content.ContentResolver; -import android.content.Context; -import android.os.BatteryManager; -import android.os.Looper; -import android.os.PowerManager; -import android.os.RemoteException; -import android.provider.DeviceConfig; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentMatchers; -import org.mockito.Mock; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; -import org.mockito.stubbing.Answer; - -@RunWith(AndroidJUnit4.class) -public class AlarmManagerEconomicPolicyTest { - private AlarmManagerEconomicPolicy mEconomicPolicy; - private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder; - private EconomicPolicy.Injector mInjector = new InjectorForTest(); - - private MockitoSession mMockingSession; - @Mock - private Context mContext; - @Mock - private InternalResourceService mIrs; - - private static class InjectorForTest extends EconomicPolicy.Injector { - public String settingsConstant; - - @Nullable - @Override - String getSettingsGlobalString(@NonNull ContentResolver resolver, @NonNull String name) { - return TARE_ALARM_MANAGER_CONSTANTS.equals(name) ? settingsConstant : null; - } - } - - @Before - public void setUp() { - mMockingSession = mockitoSession() - .initMocks(this) - .strictness(Strictness.LENIENT) - .spyStatic(DeviceConfig.class) - .startMocking(); - - when(mIrs.getContext()).thenReturn(mContext); - when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); - when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class)); - // Called by Modifiers. - when(mContext.getSystemService(BatteryManager.class)) - .thenReturn(mock(BatteryManager.class)); - when(mContext.getSystemService(PowerManager.class)) - .thenReturn(mock(PowerManager.class)); - IActivityManager activityManager = ActivityManager.getService(); - spyOn(activityManager); - try { - doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any()); - } catch (RemoteException e) { - fail("registerUidObserver threw exception: " + e.getMessage()); - } - - mDeviceConfigPropertiesBuilder = - new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_TARE); - doAnswer( - (Answer<DeviceConfig.Properties>) invocationOnMock - -> mDeviceConfigPropertiesBuilder.build()) - .when(() -> DeviceConfig.getProperties( - eq(DeviceConfig.NAMESPACE_TARE), ArgumentMatchers.<String>any())); - - // Initialize real objects. - // Capture the listeners. - mEconomicPolicy = new AlarmManagerEconomicPolicy(mIrs, mInjector); - } - - @After - public void tearDown() { - if (mMockingSession != null) { - mMockingSession.finishMocking(); - } - } - - private void setDeviceConfigCakes(String key, long valCakes) { - mDeviceConfigPropertiesBuilder.setString(key, valCakes + "c"); - mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build()); - } - - @Test - public void testDefaults() { - assertEquals(EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(EconomyManager.DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(EconomyManager.DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - - final String pkgRestricted = "com.pkg.restricted"; - when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); - assertEquals(0, mEconomicPolicy.getMinSatiatedBalance(0, pkgRestricted)); - assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - - final String pkgExempted = "com.pkg.exempted"; - when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); - assertEquals(EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - assertEquals(EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES, - mEconomicPolicy.getMaxSatiatedBalance(0, pkgExempted)); - - final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); - assertEquals(EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); - assertEquals(EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES, - mEconomicPolicy.getMaxSatiatedBalance(0, pkgHeadlessSystemApp)); - - assertEquals(EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - assertEquals(EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES, - mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - } - - @Test - public void testConstantsUpdating_ValidValues() { - setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(5)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT, arcToCake(3)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT, arcToCake(25)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(10)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(9)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, - arcToCake(8)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(7)); - - assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(arcToCake(3), mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(arcToCake(25), mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - final String pkgRestricted = "com.pkg.restricted"; - when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); - assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - assertEquals(arcToCake(10), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - final String pkgExempted = "com.pkg.exempted"; - when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); - assertEquals(arcToCake(9), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); - assertEquals(arcToCake(8), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); - assertEquals(arcToCake(7), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - } - - @Test - public void testConstantsUpdating_InvalidValues() { - // Test negatives. - setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(-5)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT, arcToCake(-5)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT, arcToCake(-5)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(-1)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(-2)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, - arcToCake(-3)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(-3)); - - assertEquals(arcToCake(1), mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(arcToCake(1), mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(arcToCake(1), mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - final String pkgRestricted = "com.pkg.restricted"; - when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); - assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - assertEquals(arcToCake(1), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - final String pkgExempted = "com.pkg.exempted"; - when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); - assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); - assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); - assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - - // Test min+max reversed. - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT, arcToCake(5)); - setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(4)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT, arcToCake(3)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(10)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(11)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, - arcToCake(12)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(13)); - - assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(arcToCake(5), mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - assertEquals(arcToCake(13), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); - assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java deleted file mode 100644 index c5fdb6f1cf2e..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/tare/CompleteEconomicPolicyTest.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static android.app.tare.EconomyManager.arcToCake; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; - -import android.app.ActivityManager; -import android.app.IActivityManager; -import android.app.tare.EconomyManager; -import android.content.ContentResolver; -import android.content.Context; -import android.os.BatteryManager; -import android.os.Looper; -import android.os.PowerManager; -import android.os.RemoteException; -import android.provider.DeviceConfig; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentMatchers; -import org.mockito.Mock; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; -import org.mockito.stubbing.Answer; - -@RunWith(AndroidJUnit4.class) -public class CompleteEconomicPolicyTest { - private CompleteEconomicPolicy mEconomicPolicy; - private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder; - private final CompleteEconomicPolicy.CompleteInjector mInjector = new InjectorForTest(); - - private MockitoSession mMockingSession; - @Mock - private Context mContext; - @Mock - private InternalResourceService mIrs; - - private static class InjectorForTest extends CompleteEconomicPolicy.CompleteInjector { - public String settingsConstant; - - @Nullable - @Override - String getSettingsGlobalString(@NonNull ContentResolver resolver, @NonNull String name) { - return settingsConstant; - } - - @Override - boolean isPolicyEnabled(int policy, @Nullable DeviceConfig.Properties properties) { - // Use a limited set of policies so that the test doesn't need to be updated whenever - // a policy is added or removed. - if (policy == EconomicPolicy.POLICY_ALARM || policy == EconomicPolicy.POLICY_JOB) { - return super.isPolicyEnabled(policy, properties); - } - return false; - } - } - - @Before - public void setUp() { - mMockingSession = mockitoSession() - .initMocks(this) - .strictness(Strictness.LENIENT) - .spyStatic(DeviceConfig.class) - .startMocking(); - - when(mIrs.getContext()).thenReturn(mContext); - when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); - when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class)); - // Called by Modifiers. - when(mContext.getSystemService(BatteryManager.class)) - .thenReturn(mock(BatteryManager.class)); - when(mContext.getSystemService(PowerManager.class)) - .thenReturn(mock(PowerManager.class)); - IActivityManager activityManager = ActivityManager.getService(); - spyOn(activityManager); - try { - doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any()); - } catch (RemoteException e) { - fail("registerUidObserver threw exception: " + e.getMessage()); - } - - mDeviceConfigPropertiesBuilder = - new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_TARE); - doAnswer( - (Answer<DeviceConfig.Properties>) invocationOnMock - -> mDeviceConfigPropertiesBuilder.build()) - .when(() -> DeviceConfig.getProperties( - eq(DeviceConfig.NAMESPACE_TARE), ArgumentMatchers.<String>any())); - mDeviceConfigPropertiesBuilder - .setBoolean(EconomyManager.KEY_ENABLE_POLICY_ALARM, true) - .setBoolean(EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER, true); - - // Initialize real objects. - // Capture the listeners. - mEconomicPolicy = new CompleteEconomicPolicy(mIrs, mInjector); - mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build()); - } - - @After - public void tearDown() { - if (mMockingSession != null) { - mMockingSession.finishMocking(); - } - } - - private void setDeviceConfigBoolean(String key, boolean val) { - mDeviceConfigPropertiesBuilder.setBoolean(key, val); - mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build()); - } - - private void setDeviceConfigCakes(String key, long valCakes) { - mDeviceConfigPropertiesBuilder.setString(key, valCakes + "c"); - mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build()); - } - - @Test - public void testDefaults() { - assertEquals(EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES - + EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES - + EconomyManager.DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES - + EconomyManager.DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - final String pkgRestricted = "com.pkg.restricted"; - when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); - assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES - + EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES, - mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - final String pkgExempted = "com.pkg.exempted"; - when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); - assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES - + EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES - + EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - } - - @Test - public void testConstantsUpdated() { - setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(4)); - setDeviceConfigCakes(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT, arcToCake(6)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT, arcToCake(2)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT, arcToCake(3)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT, arcToCake(24)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT, arcToCake(26)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(9)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE, arcToCake(11)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(8)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(5)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, - arcToCake(6)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, - arcToCake(4)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(3)); - setDeviceConfigCakes(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(2)); - - assertEquals(arcToCake(10), mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(arcToCake(50), mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - final String pkgRestricted = "com.pkg.restricted"; - when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); - assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - assertEquals(arcToCake(20), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - final String pkgExempted = "com.pkg.exempted"; - when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); - assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); - assertEquals(arcToCake(10), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); - assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - } - - - @Test - public void testPolicyToggling() { - setDeviceConfigBoolean(EconomyManager.KEY_ENABLE_POLICY_ALARM, true); - setDeviceConfigBoolean(EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER, false); - assertEquals(EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(EconomyManager.DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(EconomyManager.DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - final String pkgRestricted = "com.pkg.restricted"; - when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); - assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - assertEquals(EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES, - mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - final String pkgExempted = "com.pkg.exempted"; - when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); - assertEquals(EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - assertEquals(EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - assertNotNull(mEconomicPolicy.getAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK)); - assertNull(mEconomicPolicy.getAction(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START)); - assertEquals(EconomicPolicy.POLICY_ALARM, mEconomicPolicy.getEnabledPolicyIds()); - assertTrue(mEconomicPolicy.isPolicyEnabled(EconomicPolicy.POLICY_ALARM)); - assertFalse(mEconomicPolicy.isPolicyEnabled(EconomicPolicy.POLICY_JOB)); - - setDeviceConfigBoolean(EconomyManager.KEY_ENABLE_POLICY_ALARM, false); - setDeviceConfigBoolean(EconomyManager.KEY_ENABLE_POLICY_JOB_SCHEDULER, true); - assertEquals(EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); - assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, - mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); - assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - assertNull(mEconomicPolicy.getAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK)); - assertNotNull(mEconomicPolicy.getAction(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START)); - assertEquals(EconomicPolicy.POLICY_JOB, mEconomicPolicy.getEnabledPolicyIds()); - assertFalse(mEconomicPolicy.isPolicyEnabled(EconomicPolicy.POLICY_ALARM)); - assertTrue(mEconomicPolicy.isPolicyEnabled(EconomicPolicy.POLICY_JOB)); - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/EconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/EconomicPolicyTest.java deleted file mode 100644 index 29bddfc32ff7..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/tare/EconomicPolicyTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static org.junit.Assert.assertEquals; - -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class EconomicPolicyTest { - - @Test - public void testMasksDisjoint() { - assertEquals(-1, - (-1 & EconomicPolicy.MASK_TYPE) - + (-1 & EconomicPolicy.MASK_POLICY) - + (-1 & EconomicPolicy.MASK_EVENT)); - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java deleted file mode 100644 index d41c93ba2ea9..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static android.app.tare.EconomyManager.arcToCake; -import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; - -import android.app.ActivityManager; -import android.app.IActivityManager; -import android.app.tare.EconomyManager; -import android.content.ContentResolver; -import android.content.Context; -import android.os.BatteryManager; -import android.os.Looper; -import android.os.PowerManager; -import android.os.RemoteException; -import android.provider.DeviceConfig; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentMatchers; -import org.mockito.Mock; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; -import org.mockito.stubbing.Answer; - -@RunWith(AndroidJUnit4.class) -public class JobSchedulerEconomicPolicyTest { - private JobSchedulerEconomicPolicy mEconomicPolicy; - private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder; - private final EconomicPolicy.Injector mInjector = new InjectorForTest(); - - private MockitoSession mMockingSession; - @Mock - private Context mContext; - @Mock - private InternalResourceService mIrs; - - private static class InjectorForTest extends EconomicPolicy.Injector { - public String settingsConstant; - - @Nullable - @Override - String getSettingsGlobalString(@NonNull ContentResolver resolver, @NonNull String name) { - return TARE_JOB_SCHEDULER_CONSTANTS.equals(name) ? settingsConstant : null; - } - } - - @Before - public void setUp() { - mMockingSession = mockitoSession() - .initMocks(this) - .strictness(Strictness.LENIENT) - .spyStatic(DeviceConfig.class) - .startMocking(); - - when(mIrs.getContext()).thenReturn(mContext); - when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); - when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class)); - // Called by Modifiers. - when(mContext.getSystemService(BatteryManager.class)) - .thenReturn(mock(BatteryManager.class)); - when(mContext.getSystemService(PowerManager.class)) - .thenReturn(mock(PowerManager.class)); - IActivityManager activityManager = ActivityManager.getService(); - spyOn(activityManager); - try { - doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any()); - } catch (RemoteException e) { - fail("registerUidObserver threw exception: " + e.getMessage()); - } - - mDeviceConfigPropertiesBuilder = - new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_TARE); - doAnswer( - (Answer<DeviceConfig.Properties>) invocationOnMock - -> mDeviceConfigPropertiesBuilder.build()) - .when(() -> DeviceConfig.getProperties( - eq(DeviceConfig.NAMESPACE_TARE), ArgumentMatchers.<String>any())); - - // Initialize real objects. - // Capture the listeners. - mEconomicPolicy = new JobSchedulerEconomicPolicy(mIrs, mInjector); - } - - @After - public void tearDown() { - if (mMockingSession != null) { - mMockingSession.finishMocking(); - } - } - - private void setDeviceConfigCakes(String key, long valCakes) { - mDeviceConfigPropertiesBuilder.setString(key, valCakes + "c"); - mEconomicPolicy.setup(mDeviceConfigPropertiesBuilder.build()); - } - - @Test - public void testDefaults() { - assertEquals(EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES, - mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - - final String pkgRestricted = "com.pkg.restricted"; - when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); - assertEquals(0, mEconomicPolicy.getMinSatiatedBalance(0, pkgRestricted)); - assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - - final String pkgExempted = "com.pkg.exempted"; - when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); - assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, - mEconomicPolicy.getMaxSatiatedBalance(0, pkgExempted)); - - final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); - assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); - assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, - mEconomicPolicy.getMaxSatiatedBalance(0, pkgHeadlessSystemApp)); - - final String pkgUpdater = "com.pkg.updater"; - when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(5); - assertEquals(5 * EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES - + EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater)); - assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, - mEconomicPolicy.getMaxSatiatedBalance(0, pkgUpdater)); - // Make sure it doesn't suggest a min balance greater than max. - final int updateCount = (int) (EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES - / EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES); - when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))) - .thenReturn(updateCount); - assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater)); - - assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES, - mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES, - mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - } - - @Test - public void testConstantsUpdating_ValidValues() { - setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT, arcToCake(2)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT, arcToCake(25)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(10)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(6)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, - arcToCake(5)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(4)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER, - arcToCake(1)); - - assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(arcToCake(2), mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(arcToCake(25), mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - final String pkgRestricted = "com.pkg.restricted"; - when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); - assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - assertEquals(arcToCake(10), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - final String pkgExempted = "com.pkg.exempted"; - when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); - assertEquals(arcToCake(6), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); - assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); - assertEquals(arcToCake(4), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - final String pkgUpdater = "com.pkg.updater"; - when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(3); - assertEquals(arcToCake(4) + 3 * arcToCake(1), - mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater)); - } - - @Test - public void testConstantsUpdating_InvalidValues() { - // Test negatives. - setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(-5)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT, arcToCake(-5)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT, arcToCake(-5)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(-1)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(-2)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, - arcToCake(-3)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(-3)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER, - arcToCake(-4)); - - assertEquals(arcToCake(1), mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(arcToCake(1), mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(arcToCake(1), mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - final String pkgRestricted = "com.pkg.restricted"; - when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true); - assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - assertEquals(arcToCake(1), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - final String pkgExempted = "com.pkg.exempted"; - when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true); - assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - final String pkgHeadlessSystemApp = "com.pkg.headless_system_app"; - when(mIrs.isHeadlessSystemApp(anyInt(), eq(pkgHeadlessSystemApp))).thenReturn(true); - assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); - assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - final String pkgUpdater = "com.pkg.updater"; - when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(5); - assertEquals(arcToCake(0) + 5 * arcToCake(0), - mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater)); - - // Test min+max reversed. - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT, arcToCake(5)); - setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(4)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT, arcToCake(3)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(10)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(11)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP, - arcToCake(12)); - setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(13)); - - assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit()); - assertEquals(arcToCake(5), mEconomicPolicy.getMinSatiatedConsumptionLimit()); - assertEquals(arcToCake(5), mEconomicPolicy.getMaxSatiatedConsumptionLimit()); - assertEquals(arcToCake(0), mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted)); - assertEquals(arcToCake(13), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app")); - assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted)); - assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, pkgHeadlessSystemApp)); - assertEquals(arcToCake(13), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app")); - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS b/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS deleted file mode 100644 index 217a5edff08b..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java deleted file mode 100644 index e81b63c8c9b1..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/tare/ScribeTest.java +++ /dev/null @@ -1,409 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.server.tare.TareTestUtils.assertLedgersEqual; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.when; - -import android.app.tare.EconomyManager; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.os.UserHandle; -import android.util.Log; -import android.util.SparseArrayMap; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.LocalServices; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -/** - * Tests for various Scribe behavior, including reading and writing correctly from file. - * - * atest FrameworksServicesTests:ScribeTest - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ScribeTest { - private static final String TAG = "ScribeTest"; - - private static final int TEST_USER_ID = 27; - private static final String TEST_PACKAGE = "com.android.test"; - - private MockitoSession mMockingSession; - private Scribe mScribeUnderTest; - private File mTestFileDir; - private final SparseArrayMap<String, InstalledPackageInfo> mInstalledPackages = - new SparseArrayMap<>(); - private final List<Analyst.Report> mReports = new ArrayList<>(); - - @Mock - private Analyst mAnalyst; - @Mock - private InternalResourceService mIrs; - - private Context getContext() { - return InstrumentationRegistry.getContext(); - } - - @Before - public void setUp() throws Exception { - mMockingSession = mockitoSession() - .initMocks(this) - .strictness(Strictness.LENIENT) - .mockStatic(LocalServices.class) - .startMocking(); - when(mIrs.getLock()).thenReturn(new Object()); - when(mIrs.getEnabledMode()).thenReturn(EconomyManager.ENABLED_MODE_ON); - when(mIrs.getInstalledPackages()).thenReturn(mInstalledPackages); - when(mAnalyst.getReports()).thenReturn(mReports); - mTestFileDir = new File(getContext().getFilesDir(), "scribe_test"); - //noinspection ResultOfMethodCallIgnored - mTestFileDir.mkdirs(); - Log.d(TAG, "Saving data to '" + mTestFileDir + "'"); - mScribeUnderTest = new Scribe(mIrs, mAnalyst, mTestFileDir); - - addInstalledPackage(TEST_USER_ID, TEST_PACKAGE); - } - - @After - public void tearDown() throws Exception { - mScribeUnderTest.tearDownLocked(); - if (mTestFileDir.exists() && !mTestFileDir.delete()) { - Log.w(TAG, "Failed to delete test file directory"); - } - if (mMockingSession != null) { - mMockingSession.finishMocking(); - } - } - - @Test - public void testWritingAnalystReportsToDisk() { - ArgumentCaptor<List<Analyst.Report>> reportCaptor = - ArgumentCaptor.forClass(List.class); - - InOrder inOrder = inOrder(mAnalyst); - - // Empty set - mReports.clear(); - mScribeUnderTest.writeImmediatelyForTesting(); - mScribeUnderTest.loadFromDiskLocked(); - inOrder.verify(mAnalyst).loadReports(reportCaptor.capture()); - List<Analyst.Report> result = reportCaptor.getValue(); - assertReportListsEqual(mReports, result); - - Analyst.Report report1 = new Analyst.Report(); - report1.cumulativeBatteryDischarge = 1; - report1.currentBatteryLevel = 2; - report1.cumulativeProfit = 3; - report1.numProfitableActions = 4; - report1.cumulativeLoss = 5; - report1.numUnprofitableActions = 6; - report1.cumulativeRewards = 7; - report1.numRewards = 8; - report1.cumulativePositiveRegulations = 9; - report1.numPositiveRegulations = 10; - report1.cumulativeNegativeRegulations = 11; - report1.numNegativeRegulations = 12; - report1.screenOffDurationMs = 13; - report1.screenOffDischargeMah = 14; - mReports.add(report1); - mScribeUnderTest.writeImmediatelyForTesting(); - mScribeUnderTest.loadFromDiskLocked(); - inOrder.verify(mAnalyst).loadReports(reportCaptor.capture()); - result = reportCaptor.getValue(); - assertReportListsEqual(mReports, result); - - Analyst.Report report2 = new Analyst.Report(); - report2.cumulativeBatteryDischarge = 10; - report2.currentBatteryLevel = 20; - report2.cumulativeProfit = 30; - report2.numProfitableActions = 40; - report2.cumulativeLoss = 50; - report2.numUnprofitableActions = 60; - report2.cumulativeRewards = 70; - report2.numRewards = 80; - report2.cumulativePositiveRegulations = 90; - report2.numPositiveRegulations = 100; - report2.cumulativeNegativeRegulations = 110; - report2.numNegativeRegulations = 120; - report2.screenOffDurationMs = 130; - report2.screenOffDischargeMah = 140; - mReports.add(report2); - mScribeUnderTest.writeImmediatelyForTesting(); - mScribeUnderTest.loadFromDiskLocked(); - inOrder.verify(mAnalyst).loadReports(reportCaptor.capture()); - result = reportCaptor.getValue(); - assertReportListsEqual(mReports, result); - } - - @Test - public void testWriteHighLevelStateToDisk() { - long lastReclamationTime = System.currentTimeMillis(); - long remainingConsumableCakes = 2000L; - long consumptionLimit = 500_000L; - when(mIrs.getConsumptionLimitLocked()).thenReturn(consumptionLimit); - - Ledger ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE); - ledger.recordTransaction( - new Ledger.Transaction(0, 1000L, EconomicPolicy.TYPE_REWARD | 1, null, 2000, 0)); - // Negative ledger balance shouldn't affect the total circulation value. - ledger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID + 1, TEST_PACKAGE); - ledger.recordTransaction( - new Ledger.Transaction(0, 1000L, - EconomicPolicy.TYPE_ACTION | 1, null, -5000, 3000)); - mScribeUnderTest.setLastReclamationTimeLocked(lastReclamationTime); - mScribeUnderTest.setConsumptionLimitLocked(consumptionLimit); - mScribeUnderTest.adjustRemainingConsumableCakesLocked( - remainingConsumableCakes - consumptionLimit); - - assertEquals(lastReclamationTime, mScribeUnderTest.getLastReclamationTimeLocked()); - assertEquals(remainingConsumableCakes, - mScribeUnderTest.getRemainingConsumableCakesLocked()); - assertEquals(consumptionLimit, mScribeUnderTest.getSatiatedConsumptionLimitLocked()); - - mScribeUnderTest.writeImmediatelyForTesting(); - mScribeUnderTest.loadFromDiskLocked(); - - assertEquals(lastReclamationTime, mScribeUnderTest.getLastReclamationTimeLocked()); - assertEquals(remainingConsumableCakes, - mScribeUnderTest.getRemainingConsumableCakesLocked()); - assertEquals(consumptionLimit, mScribeUnderTest.getSatiatedConsumptionLimitLocked()); - } - - @Test - public void testWritingEmptyLedgerToDisk() { - final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE); - mScribeUnderTest.writeImmediatelyForTesting(); - - mScribeUnderTest.loadFromDiskLocked(); - assertLedgersEqual(ogLedger, mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE)); - } - - @Test - public void testWritingPopulatedLedgerToDisk() { - final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE); - ogLedger.recordTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REWARD | 1, null, 51, 0)); - ogLedger.recordTransaction( - new Ledger.Transaction(1500, 2000, - EconomicPolicy.TYPE_REWARD | 2, "green", 52, -1)); - ogLedger.recordTransaction( - new Ledger.Transaction(2500, 3000, EconomicPolicy.TYPE_REWARD | 3, "blue", 3, 12)); - mScribeUnderTest.writeImmediatelyForTesting(); - - mScribeUnderTest.loadFromDiskLocked(); - assertLedgersEqual(ogLedger, mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE)); - } - - @Test - public void testWritingMultipleLedgersToDisk() { - final SparseArrayMap<String, Ledger> ledgers = new SparseArrayMap<>(); - final int numUsers = 3; - final int numLedgers = 5; - for (int u = 0; u < numUsers; ++u) { - final int userId = TEST_USER_ID + u; - for (int l = 0; l < numLedgers; ++l) { - final String pkgName = TEST_PACKAGE + l; - addInstalledPackage(userId, pkgName); - final Ledger ledger = mScribeUnderTest.getLedgerLocked(userId, pkgName); - ledger.recordTransaction(new Ledger.Transaction( - 0, 1000L * u + l, EconomicPolicy.TYPE_ACTION | 1, null, -51L * u + l, 50)); - ledger.recordTransaction(new Ledger.Transaction( - 1500L * u + l, 2000L * u + l, - EconomicPolicy.TYPE_REWARD | 2 * u + l, "green" + u + l, 52L * u + l, 0)); - ledger.recordTransaction(new Ledger.Transaction( - 2500L * u + l, 3000L * u + l, - EconomicPolicy.TYPE_REWARD | 3 * u + l, "blue" + u + l, 3L * u + l, 0)); - ledgers.add(userId, pkgName, ledger); - } - } - mScribeUnderTest.writeImmediatelyForTesting(); - - mScribeUnderTest.loadFromDiskLocked(); - ledgers.forEach((userId, pkgName, ledger) - -> assertLedgersEqual(ledger, mScribeUnderTest.getLedgerLocked(userId, pkgName))); - } - - @Test - public void testDiscardLedgerFromDisk() { - final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE); - ogLedger.recordTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REWARD | 1, null, 51, 1)); - ogLedger.recordTransaction( - new Ledger.Transaction(1500, 2000, EconomicPolicy.TYPE_REWARD | 2, "green", 52, 0)); - ogLedger.recordTransaction( - new Ledger.Transaction(2500, 3000, EconomicPolicy.TYPE_REWARD | 3, "blue", 3, 1)); - mScribeUnderTest.writeImmediatelyForTesting(); - - mScribeUnderTest.loadFromDiskLocked(); - assertLedgersEqual(ogLedger, mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE)); - - mScribeUnderTest.discardLedgerLocked(TEST_USER_ID, TEST_PACKAGE); - mScribeUnderTest.writeImmediatelyForTesting(); - - // Make sure there's no more saved ledger. - mScribeUnderTest.loadFromDiskLocked(); - assertLedgersEqual(new Ledger(), - mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE)); - } - - @Test - public void testLoadingMissingPackageFromDisk() { - final String pkgName = TEST_PACKAGE + ".uninstalled"; - final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, pkgName); - ogLedger.recordTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REGULATION | 1, null, 51, 1)); - ogLedger.recordTransaction( - new Ledger.Transaction(1500, 2000, EconomicPolicy.TYPE_REWARD | 2, "green", 52, 2)); - ogLedger.recordTransaction( - new Ledger.Transaction(2500, 3000, EconomicPolicy.TYPE_ACTION | 3, "blue", -3, 3)); - mScribeUnderTest.writeImmediatelyForTesting(); - - // Package isn't installed, so make sure it's not saved to memory after loading. - mScribeUnderTest.loadFromDiskLocked(); - assertLedgersEqual(new Ledger(), mScribeUnderTest.getLedgerLocked(TEST_USER_ID, pkgName)); - } - - @Test - public void testLoadingMissingUserFromDisk() { - final int userId = TEST_USER_ID + 1; - final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE); - ogLedger.recordTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REWARD | 1, null, 51, 0)); - ogLedger.recordTransaction( - new Ledger.Transaction(1500, 2000, EconomicPolicy.TYPE_REWARD | 2, "green", 52, 1)); - ogLedger.recordTransaction( - new Ledger.Transaction(2500, 3000, - EconomicPolicy.TYPE_REGULATION | 3, "blue", 3, 3)); - mScribeUnderTest.writeImmediatelyForTesting(); - - // User doesn't show up with any packages, so make sure nothing is saved after loading. - mScribeUnderTest.loadFromDiskLocked(); - assertLedgersEqual(new Ledger(), mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE)); - } - - @Test - public void testChangingConsumable() { - assertEquals(0, mScribeUnderTest.getSatiatedConsumptionLimitLocked()); - assertEquals(0, mScribeUnderTest.getRemainingConsumableCakesLocked()); - - // Limit increased, so remaining value should be adjusted as well - mScribeUnderTest.setConsumptionLimitLocked(1000); - assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked()); - assertEquals(1000, mScribeUnderTest.getRemainingConsumableCakesLocked()); - - // Limit decreased below remaining, so remaining value should be adjusted as well - mScribeUnderTest.setConsumptionLimitLocked(500); - assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked()); - assertEquals(500, mScribeUnderTest.getRemainingConsumableCakesLocked()); - - mScribeUnderTest.adjustRemainingConsumableCakesLocked(-100); - assertEquals(500, mScribeUnderTest.getSatiatedConsumptionLimitLocked()); - assertEquals(400, mScribeUnderTest.getRemainingConsumableCakesLocked()); - - // Limit increased, so remaining value should be adjusted by the difference as well - mScribeUnderTest.setConsumptionLimitLocked(1000); - assertEquals(1000, mScribeUnderTest.getSatiatedConsumptionLimitLocked()); - assertEquals(900, mScribeUnderTest.getRemainingConsumableCakesLocked()); - - - // Limit decreased, but above remaining, so remaining value should left alone - mScribeUnderTest.setConsumptionLimitLocked(950); - assertEquals(950, mScribeUnderTest.getSatiatedConsumptionLimitLocked()); - assertEquals(900, mScribeUnderTest.getRemainingConsumableCakesLocked()); - } - - private void assertReportListsEqual(List<Analyst.Report> expected, - List<Analyst.Report> actual) { - if (expected == null) { - assertNull(actual); - return; - } - assertNotNull(actual); - assertEquals(expected.size(), actual.size()); - for (int i = 0; i < expected.size(); ++i) { - Analyst.Report eReport = expected.get(i); - Analyst.Report aReport = actual.get(i); - if (eReport == null) { - assertNull(aReport); - continue; - } - assertNotNull(aReport); - assertEquals("Reports #" + i + " cumulativeBatteryDischarge are not equal", - eReport.cumulativeBatteryDischarge, aReport.cumulativeBatteryDischarge); - assertEquals("Reports #" + i + " currentBatteryLevel are not equal", - eReport.currentBatteryLevel, aReport.currentBatteryLevel); - assertEquals("Reports #" + i + " cumulativeProfit are not equal", - eReport.cumulativeProfit, aReport.cumulativeProfit); - assertEquals("Reports #" + i + " numProfitableActions are not equal", - eReport.numProfitableActions, aReport.numProfitableActions); - assertEquals("Reports #" + i + " cumulativeLoss are not equal", - eReport.cumulativeLoss, aReport.cumulativeLoss); - assertEquals("Reports #" + i + " numUnprofitableActions are not equal", - eReport.numUnprofitableActions, aReport.numUnprofitableActions); - assertEquals("Reports #" + i + " cumulativeRewards are not equal", - eReport.cumulativeRewards, aReport.cumulativeRewards); - assertEquals("Reports #" + i + " numRewards are not equal", - eReport.numRewards, aReport.numRewards); - assertEquals("Reports #" + i + " cumulativePositiveRegulations are not equal", - eReport.cumulativePositiveRegulations, aReport.cumulativePositiveRegulations); - assertEquals("Reports #" + i + " numPositiveRegulations are not equal", - eReport.numPositiveRegulations, aReport.numPositiveRegulations); - assertEquals("Reports #" + i + " cumulativeNegativeRegulations are not equal", - eReport.cumulativeNegativeRegulations, aReport.cumulativeNegativeRegulations); - assertEquals("Reports #" + i + " numNegativeRegulations are not equal", - eReport.numNegativeRegulations, aReport.numNegativeRegulations); - assertEquals("Reports #" + i + " screenOffDurationMs are not equal", - eReport.screenOffDurationMs, aReport.screenOffDurationMs); - assertEquals("Reports #" + i + " screenOffDischargeMah are not equal", - eReport.screenOffDischargeMah, aReport.screenOffDischargeMah); - } - } - - private void addInstalledPackage(int userId, String pkgName) { - PackageInfo pkgInfo = new PackageInfo(); - pkgInfo.packageName = pkgName; - ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.uid = UserHandle.getUid(userId, Math.abs(pkgName.hashCode())); - pkgInfo.applicationInfo = applicationInfo; - mInstalledPackages.add(userId, pkgName, new InstalledPackageInfo(getContext(), userId, - pkgInfo)); - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/TareTestUtils.java b/services/tests/mockingservicestests/src/com/android/server/tare/TareTestUtils.java deleted file mode 100644 index 1e4684b84516..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/tare/TareTestUtils.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import android.util.SparseLongArray; - -import java.util.List; - -public class TareTestUtils { - static void assertLedgersEqual(Ledger expected, Ledger actual) { - if (expected == null) { - assertNull(actual); - return; - } - assertNotNull(actual); - assertEquals(expected.getCurrentBalance(), actual.getCurrentBalance()); - - List<Ledger.Transaction> expectedTransactions = expected.getTransactions(); - List<Ledger.Transaction> actualTransactions = actual.getTransactions(); - assertEquals(expectedTransactions.size(), actualTransactions.size()); - for (int i = 0; i < expectedTransactions.size(); ++i) { - assertTransactionsEqual(expectedTransactions.get(i), actualTransactions.get(i)); - } - - List<Ledger.RewardBucket> expectedRewardBuckets = expected.getRewardBuckets(); - List<Ledger.RewardBucket> actualRewardBuckets = actual.getRewardBuckets(); - assertEquals(expectedRewardBuckets.size(), actualRewardBuckets.size()); - for (int i = 0; i < expectedRewardBuckets.size(); ++i) { - assertRewardBucketsEqual(expectedRewardBuckets.get(i), actualRewardBuckets.get(i)); - } - } - - - static void assertSparseLongArraysEqual(SparseLongArray expected, SparseLongArray actual) { - if (expected == null) { - assertNull(actual); - return; - } - assertNotNull(actual); - final int size = expected.size(); - assertEquals(size, actual.size()); - for (int i = 0; i < size; ++i) { - assertEquals(expected.keyAt(i), actual.keyAt(i)); - assertEquals(expected.valueAt(i), actual.valueAt(i)); - } - } - - static void assertRewardBucketsEqual(Ledger.RewardBucket expected, Ledger.RewardBucket actual) { - if (expected == null) { - assertNull(actual); - return; - } - assertNotNull(actual); - assertEquals(expected.startTimeMs, actual.startTimeMs); - assertSparseLongArraysEqual(expected.cumulativeDelta, actual.cumulativeDelta); - } - - static void assertTransactionsEqual(Ledger.Transaction expected, Ledger.Transaction actual) { - if (expected == null) { - assertNull(actual); - return; - } - assertNotNull(actual); - assertEquals(expected.startTimeMs, actual.startTimeMs); - assertEquals(expected.endTimeMs, actual.endTimeMs); - assertEquals(expected.eventId, actual.eventId); - assertEquals(expected.tag, actual.tag); - assertEquals(expected.delta, actual.delta); - assertEquals(expected.ctp, actual.ctp); - } -} diff --git a/services/tests/servicestests/src/com/android/server/tare/AnalystTest.java b/services/tests/servicestests/src/com/android/server/tare/AnalystTest.java deleted file mode 100644 index a603b93ab307..000000000000 --- a/services/tests/servicestests/src/com/android/server/tare/AnalystTest.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.app.IBatteryStats; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.List; - -/** Test that the Analyst processes transactions correctly. */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class AnalystTest { - - @Test - public void testInitialState() { - final Analyst analyst = new Analyst(); - assertEquals(0, analyst.getReports().size()); - } - - @Test - public void testBatteryLevelChange() { - final Analyst analyst = new Analyst(); - - Analyst.Report expected = new Analyst.Report(); - expected.currentBatteryLevel = 75; - analyst.noteBatteryLevelChange(75); - assertEquals(1, analyst.getReports().size()); - assertReportsEqual(expected, analyst.getReports().get(0)); - - // Discharging - analyst.noteBatteryLevelChange(54); - expected.currentBatteryLevel = 54; - expected.cumulativeBatteryDischarge = 21; - assertEquals(1, analyst.getReports().size()); - assertReportsEqual(expected, analyst.getReports().get(0)); - analyst.noteBatteryLevelChange(50); - expected.currentBatteryLevel = 50; - expected.cumulativeBatteryDischarge = 25; - assertEquals(1, analyst.getReports().size()); - assertReportsEqual(expected, analyst.getReports().get(0)); - - // Charging - analyst.noteBatteryLevelChange(51); - expected.currentBatteryLevel = 51; - assertEquals(1, analyst.getReports().size()); - assertReportsEqual(expected, analyst.getReports().get(0)); - analyst.noteBatteryLevelChange(55); - expected.currentBatteryLevel = 55; - assertEquals(1, analyst.getReports().size()); - assertReportsEqual(expected, analyst.getReports().get(0)); - - // Reset - analyst.noteBatteryLevelChange(100); - assertEquals(2, analyst.getReports().size()); - assertReportsEqual(expected, analyst.getReports().get(0)); - expected.currentBatteryLevel = 100; - expected.cumulativeBatteryDischarge = 0; - assertReportsEqual(expected, analyst.getReports().get(1)); - } - - @Test - public void testTransaction() { - runTestTransactions(new Analyst(), new Analyst.Report(), 1); - } - - @Test - public void testTransaction_PeriodChange() throws Exception { - IBatteryStats iBatteryStats = mock(IBatteryStats.class); - final Analyst analyst = new Analyst(iBatteryStats); - - // Reset from enough discharge. - Analyst.Report expected = new Analyst.Report(); - expected.currentBatteryLevel = 75; - analyst.noteBatteryLevelChange(75); - - runTestTransactions(analyst, expected, 1); - - expected.currentBatteryLevel = 49; - expected.cumulativeBatteryDischarge = 26; - analyst.noteBatteryLevelChange(49); - - runTestTransactions(analyst, expected, 1); - - expected = new Analyst.Report(); - expected.currentBatteryLevel = 90; - analyst.noteBatteryLevelChange(90); - expected.cumulativeBatteryDischarge = 0; - - runTestTransactions(analyst, expected, 2); - - // Reset from report being long enough. - doReturn(Analyst.MIN_REPORT_DURATION_FOR_RESET) - .when(iBatteryStats).computeBatteryScreenOffRealtimeMs(); - expected.currentBatteryLevel = 85; - analyst.noteBatteryLevelChange(85); - expected.cumulativeBatteryDischarge = 5; - expected.screenOffDurationMs = Analyst.MIN_REPORT_DURATION_FOR_RESET; - - runTestTransactions(analyst, expected, 2); - - expected.currentBatteryLevel = 79; - analyst.noteBatteryLevelChange(79); - expected.cumulativeBatteryDischarge = 11; - - runTestTransactions(analyst, expected, 2); - - expected = new Analyst.Report(); - expected.currentBatteryLevel = 80; - analyst.noteBatteryLevelChange(80); - expected.cumulativeBatteryDischarge = 0; - expected.screenOffDurationMs = 0; - - runTestTransactions(analyst, expected, 3); - } - - private void runTestTransactions(Analyst analyst, Analyst.Report lastExpectedReport, - int numExpectedReports) { - Analyst.Report expected = lastExpectedReport; - - // Profit - analyst.noteTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_ACTION, null, -51, 1)); - expected.cumulativeProfit += 50; - expected.numProfitableActions += 1; - assertEquals(numExpectedReports, analyst.getReports().size()); - assertReportsEqual(expected, analyst.getReports().get(numExpectedReports - 1)); - - // Loss - analyst.noteTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_ACTION, null, -51, 100)); - expected.cumulativeLoss += 49; - expected.numUnprofitableActions += 1; - assertEquals(numExpectedReports, analyst.getReports().size()); - assertReportsEqual(expected, analyst.getReports().get(numExpectedReports - 1)); - - // Reward - analyst.noteTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REWARD, null, 51, 0)); - expected.cumulativeRewards += 51; - expected.numRewards += 1; - assertEquals(numExpectedReports, analyst.getReports().size()); - assertReportsEqual(expected, analyst.getReports().get(numExpectedReports - 1)); - - // Regulations - analyst.noteTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REGULATION, null, 25, 0)); - expected.cumulativePositiveRegulations += 25; - expected.numPositiveRegulations += 1; - assertEquals(numExpectedReports, analyst.getReports().size()); - assertReportsEqual(expected, analyst.getReports().get(numExpectedReports - 1)); - analyst.noteTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REGULATION, null, -25, 0)); - expected.cumulativeNegativeRegulations += 25; - expected.numNegativeRegulations += 1; - assertEquals(numExpectedReports, analyst.getReports().size()); - assertReportsEqual(expected, analyst.getReports().get(numExpectedReports - 1)); - - // No-ops - analyst.noteTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_ACTION, null, -100, 100)); - analyst.noteTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REGULATION, null, 0, 0)); - analyst.noteTransaction( - new Ledger.Transaction(0, 1000, EconomicPolicy.TYPE_REWARD, null, 0, 0)); - assertEquals(numExpectedReports, analyst.getReports().size()); - } - - @Test - public void testLoadReports() { - final Analyst analyst = new Analyst(); - - List<Analyst.Report> expected = new ArrayList<>(); - analyst.loadReports(expected); - assertReportListsEqual(expected, analyst.getReports()); - - Analyst.Report report1 = new Analyst.Report(); - report1.cumulativeBatteryDischarge = 1; - report1.currentBatteryLevel = 2; - report1.cumulativeProfit = 3; - report1.numProfitableActions = 4; - report1.cumulativeLoss = 5; - report1.numUnprofitableActions = 6; - report1.cumulativeRewards = 7; - report1.numRewards = 8; - report1.cumulativePositiveRegulations = 9; - report1.numPositiveRegulations = 10; - report1.cumulativeNegativeRegulations = 11; - report1.numNegativeRegulations = 12; - expected.add(report1); - analyst.loadReports(expected); - assertReportListsEqual(expected, analyst.getReports()); - - Analyst.Report report2 = new Analyst.Report(); - report2.cumulativeBatteryDischarge = 10; - report2.currentBatteryLevel = 20; - report2.cumulativeProfit = 30; - report2.numProfitableActions = 40; - report2.cumulativeLoss = 50; - report2.numUnprofitableActions = 60; - report2.cumulativeRewards = 70; - report2.numRewards = 80; - report2.cumulativePositiveRegulations = 90; - report2.numPositiveRegulations = 100; - report2.cumulativeNegativeRegulations = 110; - report2.numNegativeRegulations = 120; - expected.add(report2); - analyst.loadReports(expected); - assertReportListsEqual(expected, analyst.getReports()); - } - - private void assertReportsEqual(Analyst.Report expected, Analyst.Report actual) { - if (expected == null) { - assertNull(actual); - return; - } - assertNotNull(actual); - assertEquals(expected.cumulativeBatteryDischarge, actual.cumulativeBatteryDischarge); - assertEquals(expected.currentBatteryLevel, actual.currentBatteryLevel); - assertEquals(expected.cumulativeProfit, actual.cumulativeProfit); - assertEquals(expected.numProfitableActions, actual.numProfitableActions); - assertEquals(expected.cumulativeLoss, actual.cumulativeLoss); - assertEquals(expected.numUnprofitableActions, actual.numUnprofitableActions); - assertEquals(expected.cumulativeRewards, actual.cumulativeRewards); - assertEquals(expected.numRewards, actual.numRewards); - assertEquals(expected.cumulativePositiveRegulations, actual.cumulativePositiveRegulations); - assertEquals(expected.numPositiveRegulations, actual.numPositiveRegulations); - assertEquals(expected.cumulativeNegativeRegulations, actual.cumulativeNegativeRegulations); - assertEquals(expected.numNegativeRegulations, actual.numNegativeRegulations); - assertEquals(expected.screenOffDurationMs, actual.screenOffDurationMs); - assertEquals(expected.screenOffDischargeMah, actual.screenOffDischargeMah); - } - - private void assertReportListsEqual(List<Analyst.Report> expected, - List<Analyst.Report> actual) { - if (expected == null) { - assertNull(actual); - return; - } - assertNotNull(actual); - assertEquals(expected.size(), actual.size()); - for (int i = 0; i < expected.size(); ++i) { - assertReportsEqual(expected.get(i), actual.get(i)); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java deleted file mode 100644 index 54566c39a8d2..000000000000 --- a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.tare; - -import static android.text.format.DateUtils.HOUR_IN_MILLIS; -import static android.text.format.DateUtils.MINUTE_IN_MILLIS; - -import static com.android.server.tare.TareUtils.getCurrentTimeMillis; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.util.SparseLongArray; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.time.Clock; -import java.time.Duration; -import java.time.ZoneOffset; -import java.util.ArrayList; -import java.util.List; - -/** Test that the ledger records transactions correctly. */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class LedgerTest { - - @Before - public void setUp() { - TareUtils.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); - } - - private void shiftSystemTime(long incrementMs) { - TareUtils.sSystemClock = - Clock.offset(TareUtils.sSystemClock, Duration.ofMillis(incrementMs)); - } - - @Test - public void testInitialState() { - final Ledger ledger = new Ledger(); - assertEquals(0, ledger.getCurrentBalance()); - assertEquals(0, ledger.get24HourSum(0, 0)); - } - - @Test - public void testInitialization_FullLists() { - final long balance = 1234567890L; - List<Ledger.Transaction> transactions = new ArrayList<>(); - List<Ledger.RewardBucket> rewardBuckets = new ArrayList<>(); - - final long now = getCurrentTimeMillis(); - Ledger.Transaction secondTxn = null; - Ledger.RewardBucket remainingBucket = null; - for (int i = 0; i < Ledger.MAX_TRANSACTION_COUNT; ++i) { - final long start = now - 10 * HOUR_IN_MILLIS + i * MINUTE_IN_MILLIS; - Ledger.Transaction transaction = new Ledger.Transaction( - start, start + MINUTE_IN_MILLIS, 1, null, 400, 0); - if (i == 1) { - secondTxn = transaction; - } - transactions.add(transaction); - } - for (int b = 0; b < Ledger.NUM_REWARD_BUCKET_WINDOWS; ++b) { - final long start = now - (Ledger.NUM_REWARD_BUCKET_WINDOWS - b) * 24 * HOUR_IN_MILLIS; - Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket(); - rewardBucket.startTimeMs = start; - for (int r = 0; r < 5; ++r) { - rewardBucket.cumulativeDelta.put(EconomicPolicy.TYPE_REWARD | r, b * start + r); - } - if (b == Ledger.NUM_REWARD_BUCKET_WINDOWS - 1) { - remainingBucket = rewardBucket; - } - rewardBuckets.add(rewardBucket); - } - final Ledger ledger = new Ledger(balance, transactions, rewardBuckets); - assertEquals(balance, ledger.getCurrentBalance()); - assertEquals(transactions, ledger.getTransactions()); - // Everything but the last bucket is old, so the returned list should only contain that - // bucket. - rewardBuckets.clear(); - rewardBuckets.add(remainingBucket); - assertEquals(rewardBuckets, ledger.getRewardBuckets()); - - // Make sure the ledger can properly record new transactions. - final long start = now - MINUTE_IN_MILLIS; - final long delta = 400; - final Ledger.Transaction transaction = new Ledger.Transaction( - start, start + MINUTE_IN_MILLIS, EconomicPolicy.TYPE_REWARD | 1, null, delta, 0); - ledger.recordTransaction(transaction); - assertEquals(balance + delta, ledger.getCurrentBalance()); - transactions = ledger.getTransactions(); - assertEquals(secondTxn, transactions.get(0)); - assertEquals(transaction, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 1)); - final Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket(); - rewardBucket.startTimeMs = now; - rewardBucket.cumulativeDelta.put(EconomicPolicy.TYPE_REWARD | 1, delta); - rewardBuckets = ledger.getRewardBuckets(); - assertRewardBucketsEqual(remainingBucket, rewardBuckets.get(0)); - assertRewardBucketsEqual(rewardBucket, rewardBuckets.get(1)); - } - - @Test - public void testInitialization_OverflowingLists() { - final long balance = 1234567890L; - final List<Ledger.Transaction> transactions = new ArrayList<>(); - final List<Ledger.RewardBucket> rewardBuckets = new ArrayList<>(); - - final long now = getCurrentTimeMillis(); - for (int i = 0; i < 2 * Ledger.MAX_TRANSACTION_COUNT; ++i) { - final long start = now - 20 * HOUR_IN_MILLIS + i * MINUTE_IN_MILLIS; - Ledger.Transaction transaction = new Ledger.Transaction( - start, start + MINUTE_IN_MILLIS, 1, null, 400, 0); - transactions.add(transaction); - } - for (int b = 0; b < 2 * Ledger.NUM_REWARD_BUCKET_WINDOWS; ++b) { - final long start = now - - (2 * Ledger.NUM_REWARD_BUCKET_WINDOWS - b) * 6 * HOUR_IN_MILLIS; - Ledger.RewardBucket rewardBucket = new Ledger.RewardBucket(); - rewardBucket.startTimeMs = start; - for (int r = 0; r < 5; ++r) { - rewardBucket.cumulativeDelta.put(EconomicPolicy.TYPE_REWARD | r, b * start + r); - } - rewardBuckets.add(rewardBucket); - } - final Ledger ledger = new Ledger(balance, transactions, rewardBuckets); - assertEquals(balance, ledger.getCurrentBalance()); - assertEquals(transactions.subList(Ledger.MAX_TRANSACTION_COUNT, - 2 * Ledger.MAX_TRANSACTION_COUNT), - ledger.getTransactions()); - assertEquals(rewardBuckets.subList(Ledger.NUM_REWARD_BUCKET_WINDOWS, - 2 * Ledger.NUM_REWARD_BUCKET_WINDOWS), - ledger.getRewardBuckets()); - } - - @Test - public void testMultipleTransactions() { - final Ledger ledger = new Ledger(); - ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5, 0)); - assertEquals(5, ledger.getCurrentBalance()); - ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25, 0)); - assertEquals(30, ledger.getCurrentBalance()); - ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10, 5)); - assertEquals(20, ledger.getCurrentBalance()); - } - - @Test - public void test24HourSum() { - final long now = getCurrentTimeMillis(); - final long end = now + 24 * HOUR_IN_MILLIS; - final int reward1 = EconomicPolicy.TYPE_REWARD | 1; - final int reward2 = EconomicPolicy.TYPE_REWARD | 2; - final Ledger ledger = new Ledger(); - - // First bucket - assertEquals(0, ledger.get24HourSum(reward1, end)); - ledger.recordTransaction(new Ledger.Transaction(now, now + 1000, reward1, null, 500, 0)); - assertEquals(500, ledger.get24HourSum(reward1, end)); - assertEquals(0, ledger.get24HourSum(reward2, end)); - ledger.recordTransaction( - new Ledger.Transaction(now + 2 * HOUR_IN_MILLIS, now + 3 * HOUR_IN_MILLIS, - reward1, null, 2500, 0)); - assertEquals(3000, ledger.get24HourSum(reward1, end)); - // Second bucket - shiftSystemTime(7 * HOUR_IN_MILLIS); // now + 7 - ledger.recordTransaction( - new Ledger.Transaction(now + 7 * HOUR_IN_MILLIS, now + 7 * HOUR_IN_MILLIS, - reward1, null, 1, 0)); - ledger.recordTransaction( - new Ledger.Transaction(now + 7 * HOUR_IN_MILLIS, now + 7 * HOUR_IN_MILLIS, - reward2, null, 42, 0)); - assertEquals(3001, ledger.get24HourSum(reward1, end)); - assertEquals(42, ledger.get24HourSum(reward2, end)); - // Third bucket - shiftSystemTime(12 * HOUR_IN_MILLIS); // now + 19 - ledger.recordTransaction( - new Ledger.Transaction(now + 12 * HOUR_IN_MILLIS, now + 13 * HOUR_IN_MILLIS, - reward1, null, 300, 0)); - assertEquals(3301, ledger.get24HourSum(reward1, end)); - assertRewardBucketsInOrder(ledger.getRewardBuckets()); - // Older buckets should be excluded - assertEquals(301, ledger.get24HourSum(reward1, end + HOUR_IN_MILLIS)); - assertEquals(301, ledger.get24HourSum(reward1, end + 2 * HOUR_IN_MILLIS)); - // 2nd bucket should still be included since it started at the 7 hour mark - assertEquals(301, ledger.get24HourSum(reward1, end + 6 * HOUR_IN_MILLIS)); - assertEquals(42, ledger.get24HourSum(reward2, end + 6 * HOUR_IN_MILLIS)); - assertEquals(300, ledger.get24HourSum(reward1, end + 7 * HOUR_IN_MILLIS + 1)); - assertEquals(0, ledger.get24HourSum(reward2, end + 8 * HOUR_IN_MILLIS)); - assertEquals(0, ledger.get24HourSum(reward1, end + 19 * HOUR_IN_MILLIS + 1)); - } - - @Test - public void testRemoveOldTransactions() { - final Ledger ledger = new Ledger(); - ledger.removeOldTransactions(24 * HOUR_IN_MILLIS); - assertNull(ledger.getEarliestTransaction()); - - final long now = getCurrentTimeMillis(); - Ledger.Transaction transaction1 = new Ledger.Transaction( - now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800, 0); - Ledger.Transaction transaction2 = new Ledger.Transaction( - now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600, 0); - Ledger.Transaction transaction3 = new Ledger.Transaction( - now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600, 0); - // Instant event - Ledger.Transaction transaction4 = new Ledger.Transaction( - now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500, 0); - // Recent event - Ledger.Transaction transaction5 = new Ledger.Transaction( - now - 5 * MINUTE_IN_MILLIS, now - MINUTE_IN_MILLIS, 1, null, 400, 0); - ledger.recordTransaction(transaction1); - ledger.recordTransaction(transaction2); - ledger.recordTransaction(transaction3); - ledger.recordTransaction(transaction4); - ledger.recordTransaction(transaction5); - - assertEquals(transaction1, ledger.getEarliestTransaction()); - ledger.removeOldTransactions(24 * HOUR_IN_MILLIS); - assertEquals(transaction2, ledger.getEarliestTransaction()); - ledger.removeOldTransactions(23 * HOUR_IN_MILLIS); - assertEquals(transaction3, ledger.getEarliestTransaction()); - // Shouldn't delete transaction3 yet since there's still a piece of it within the min age - // window. - ledger.removeOldTransactions(21 * HOUR_IN_MILLIS + 30 * MINUTE_IN_MILLIS); - assertEquals(transaction3, ledger.getEarliestTransaction()); - // Instant event should be removed as soon as we hit the exact threshold. - ledger.removeOldTransactions(20 * HOUR_IN_MILLIS); - assertEquals(transaction5, ledger.getEarliestTransaction()); - ledger.removeOldTransactions(0); - assertNull(ledger.getEarliestTransaction()); - } - - @Test - public void testTransactionsAlwaysInOrder() { - final Ledger ledger = new Ledger(); - List<Ledger.Transaction> transactions = ledger.getTransactions(); - assertTrue(transactions.isEmpty()); - - final long now = getCurrentTimeMillis(); - Ledger.Transaction transaction1 = new Ledger.Transaction( - now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800, 0); - Ledger.Transaction transaction2 = new Ledger.Transaction( - now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600, 0); - Ledger.Transaction transaction3 = new Ledger.Transaction( - now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600, 0); - // Instant event - Ledger.Transaction transaction4 = new Ledger.Transaction( - now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500, 0); - - Ledger.Transaction transaction5 = new Ledger.Transaction( - now - 15 * HOUR_IN_MILLIS, now - 15 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS, - 1, null, 400, 0); - ledger.recordTransaction(transaction1); - ledger.recordTransaction(transaction2); - ledger.recordTransaction(transaction3); - ledger.recordTransaction(transaction4); - ledger.recordTransaction(transaction5); - - transactions = ledger.getTransactions(); - assertEquals(5, transactions.size()); - assertTransactionsInOrder(transactions); - - for (int i = 0; i < Ledger.MAX_TRANSACTION_COUNT - 5; ++i) { - final long start = now - 10 * HOUR_IN_MILLIS + i * MINUTE_IN_MILLIS; - Ledger.Transaction transaction = new Ledger.Transaction( - start, start + MINUTE_IN_MILLIS, 1, null, 400, 0); - ledger.recordTransaction(transaction); - } - transactions = ledger.getTransactions(); - assertEquals(Ledger.MAX_TRANSACTION_COUNT, transactions.size()); - assertTransactionsInOrder(transactions); - - long start = now - 5 * HOUR_IN_MILLIS; - Ledger.Transaction transactionLast5 = new Ledger.Transaction( - start, start + MINUTE_IN_MILLIS, 1, null, 4800, 0); - start = now - 4 * HOUR_IN_MILLIS; - Ledger.Transaction transactionLast4 = new Ledger.Transaction( - start, start + MINUTE_IN_MILLIS, 1, null, 600, 0); - start = now - 3 * HOUR_IN_MILLIS; - Ledger.Transaction transactionLast3 = new Ledger.Transaction( - start, start + MINUTE_IN_MILLIS, 1, null, 600, 0); - // Instant event - start = now - 2 * HOUR_IN_MILLIS; - Ledger.Transaction transactionLast2 = new Ledger.Transaction( - start, start, 1, null, 500, 0); - Ledger.Transaction transactionLast1 = new Ledger.Transaction( - start, start + MINUTE_IN_MILLIS, 1, null, 400, 0); - ledger.recordTransaction(transactionLast5); - ledger.recordTransaction(transactionLast4); - ledger.recordTransaction(transactionLast3); - ledger.recordTransaction(transactionLast2); - ledger.recordTransaction(transactionLast1); - - transactions = ledger.getTransactions(); - assertEquals(Ledger.MAX_TRANSACTION_COUNT, transactions.size()); - assertTransactionsInOrder(transactions); - assertEquals(transactionLast1, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 1)); - assertEquals(transactionLast2, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 2)); - assertEquals(transactionLast3, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 3)); - assertEquals(transactionLast4, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 4)); - assertEquals(transactionLast5, transactions.get(Ledger.MAX_TRANSACTION_COUNT - 5)); - assertFalse(transactions.contains(transaction1)); - assertFalse(transactions.contains(transaction2)); - assertFalse(transactions.contains(transaction3)); - assertFalse(transactions.contains(transaction4)); - assertFalse(transactions.contains(transaction5)); - } - - private void assertSparseLongArraysEqual(SparseLongArray expected, SparseLongArray actual) { - if (expected == null) { - assertNull(actual); - return; - } - assertNotNull(actual); - final int size = expected.size(); - assertEquals(size, actual.size()); - for (int i = 0; i < size; ++i) { - assertEquals(expected.keyAt(i), actual.keyAt(i)); - assertEquals(expected.valueAt(i), actual.valueAt(i)); - } - } - - private void assertRewardBucketsEqual(Ledger.RewardBucket expected, - Ledger.RewardBucket actual) { - if (expected == null) { - assertNull(actual); - return; - } - assertNotNull(actual); - assertEquals(expected.startTimeMs, actual.startTimeMs); - assertSparseLongArraysEqual(expected.cumulativeDelta, actual.cumulativeDelta); - } - - private void assertRewardBucketsInOrder(List<Ledger.RewardBucket> rewardBuckets) { - assertNotNull(rewardBuckets); - for (int i = 1; i < rewardBuckets.size(); ++i) { - final Ledger.RewardBucket prev = rewardBuckets.get(i - 1); - final Ledger.RewardBucket cur = rewardBuckets.get(i); - assertTrue("Newer bucket stored before older bucket @ index " + i - + ": " + prev.startTimeMs + " vs " + cur.startTimeMs, - prev.startTimeMs <= cur.startTimeMs); - } - } - - private void assertTransactionsInOrder(List<Ledger.Transaction> transactions) { - assertNotNull(transactions); - for (int i = 1; i < transactions.size(); ++i) { - final Ledger.Transaction prev = transactions.get(i - 1); - final Ledger.Transaction cur = transactions.get(i); - assertTrue("Newer transaction stored before older transaction @ index " + i - + ": " + prev.endTimeMs + " vs " + cur.endTimeMs, - prev.endTimeMs <= cur.endTimeMs); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/tare/OWNERS b/services/tests/servicestests/src/com/android/server/tare/OWNERS deleted file mode 100644 index 217a5edff08b..000000000000 --- a/services/tests/servicestests/src/com/android/server/tare/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS
\ No newline at end of file |