diff options
518 files changed, 12923 insertions, 3533 deletions
diff --git a/Android.bp b/Android.bp index 3f1fc4d8a65e..f57acfa4ab60 100644 --- a/Android.bp +++ b/Android.bp @@ -595,6 +595,7 @@ filegroup { name: "framework-ike-shared-srcs", visibility: ["//frameworks/opt/net/ike"], srcs: [ + "core/java/android/annotation/StringDef.java", "core/java/android/net/annotations/PolicyDirection.java", "core/java/com/android/internal/util/IState.java", "core/java/com/android/internal/util/State.java", @@ -1025,6 +1026,7 @@ filegroup { "core/java/android/util/TimeUtils.java", "core/java/com/android/internal/os/SomeArgs.java", "core/java/com/android/internal/util/AsyncChannel.java", + "core/java/com/android/internal/util/AsyncService.java", "core/java/com/android/internal/util/BitwiseInputStream.java", "core/java/com/android/internal/util/FastXmlSerializer.java", "core/java/com/android/internal/util/HexDump.java", diff --git a/ApiDocs.bp b/ApiDocs.bp index e373db66925f..c40004cf8e5c 100644 --- a/ApiDocs.bp +++ b/ApiDocs.bp @@ -121,8 +121,10 @@ framework_docs_only_args = " -android -manifest $(location core/res/AndroidManif doc_defaults { name: "framework-docs-default", - libs: framework_docs_only_libs + - ["stub-annotations"], + libs: framework_docs_only_libs + [ + "stub-annotations", + "unsupportedappusage", + ], html_dirs: [ "docs/html", ], diff --git a/api/current.txt b/api/current.txt index 18e0043aebb1..c30e977f41c5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -29167,7 +29167,7 @@ package android.net { method @NonNull public android.net.NetworkRequest.Builder clearCapabilities(); method public android.net.NetworkRequest.Builder removeCapability(int); method public android.net.NetworkRequest.Builder removeTransportType(int); - method public android.net.NetworkRequest.Builder setNetworkSpecifier(String); + method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String); method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier); } @@ -29266,6 +29266,19 @@ package android.net { method public void onStopped(); } + public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { + method public int describeContents(); + method public int getSubscriptionId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.TelephonyNetworkSpecifier> CREATOR; + } + + public static final class TelephonyNetworkSpecifier.Builder { + ctor public TelephonyNetworkSpecifier.Builder(); + method @NonNull public android.net.TelephonyNetworkSpecifier build(); + method @NonNull public android.net.TelephonyNetworkSpecifier.Builder setSubscriptionId(int); + } + public class TrafficStats { ctor public TrafficStats(); method public static void clearThreadStatsTag(); @@ -43300,6 +43313,7 @@ package android.telecom { method public java.util.List<android.telecom.Call> getChildren(); method public java.util.List<android.telecom.Call> getConferenceableCalls(); method public android.telecom.Call.Details getDetails(); + method @Nullable public android.telecom.Call getGenericConferenceActiveChildCall(); method public android.telecom.Call getParent(); method public String getRemainingPostDialSequence(); method @Nullable public android.telecom.Call.RttCall getRttCall(); @@ -43383,6 +43397,7 @@ package android.telecom { method public int getCallerDisplayNamePresentation(); method public int getCallerNumberVerificationStatus(); method public final long getConnectTimeMillis(); + method @Nullable public String getContactDisplayName(); method public long getCreationTimeMillis(); method public android.telecom.DisconnectCause getDisconnectCause(); method public android.os.Bundle getExtras(); @@ -44403,6 +44418,9 @@ package android.telephony { field public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1; // 0xffffffff field public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX"; field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX"; + field public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int"; + field public static final String KEY_5G_ICON_CONFIGURATION_STRING = "5g_icon_configuration_string"; + field public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT = "5g_icon_display_grace_period_sec_int"; field public static final String KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrp_thresholds_int_array"; field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array"; field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array"; @@ -44416,18 +44434,32 @@ package android.telephony { field public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool"; field public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool"; field public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool"; - field public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; + field public static final String KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL = "allow_video_calling_fallback_bool"; + field public static final String KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL = "always_show_data_rat_icon_bool"; + field @Deprecated public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; + field public static final String KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN = "always_show_primary_signal_bar_in_opportunistic_network_boolean"; field public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool"; + field public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY = "apn_settings_default_apn_types_string_array"; field public static final String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool"; field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool"; field public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL = "call_barring_supports_password_change_bool"; field public static final String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool"; field public static final String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array"; + field public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING = "call_redirection_service_component_name_string"; + field public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL = "carrier_allow_deflect_ims_call_bool"; field public static final String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool"; field public static final String KEY_CARRIER_APP_REQUIRED_DURING_SIM_SETUP_BOOL = "carrier_app_required_during_setup_bool"; field public static final String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app"; + field public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY = "carrier_certificate_string_array"; + field public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool"; field public static final String KEY_CARRIER_CONFIG_VERSION_STRING = "carrier_config_version_string"; field public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS = "carrier_data_call_permanent_failure_strings"; + field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY = "carrier_default_actions_on_dcfailure_string_array"; + field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE = "carrier_default_actions_on_default_network_available_string_array"; + field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY = "carrier_default_actions_on_redirection_string_array"; + field public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET = "carrier_default_actions_on_reset_string_array"; + field public static final String KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY = "carrier_default_redirection_url_string_array"; + field public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL = "carrier_default_wfc_ims_enabled_bool"; field public static final String KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT = "carrier_default_wfc_ims_mode_int"; field public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT = "carrier_default_wfc_ims_roaming_mode_int"; field @Deprecated public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL = "carrier_force_disable_etws_cmas_test_bool"; @@ -44440,11 +44472,13 @@ package android.telephony { field public static final String KEY_CARRIER_NAME_OVERRIDE_BOOL = "carrier_name_override_bool"; field public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string"; field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool"; + field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string"; field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool"; field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool"; field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool"; field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool"; field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool"; + field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool"; field public static final String KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool"; field public static final String KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL = "carrier_volte_provisioning_required_bool"; field public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool"; @@ -44458,6 +44492,7 @@ package android.telephony { field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array"; field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int"; field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array"; + field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool"; field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool"; field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string"; field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string"; @@ -44467,6 +44502,7 @@ package android.telephony { field public static final String KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING = "config_ims_rcs_package_override_string"; field public static final String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string"; field public static final String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL = "config_telephony_use_own_number_for_voicemail_bool"; + field public static final String KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm"; field public static final String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool"; field public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL = "data_limit_notification_bool"; field public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long"; @@ -44478,6 +44514,7 @@ package android.telephony { field public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string"; field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array"; field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; + field public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL = "disable_charge_indication_bool"; field public static final String KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL = "disable_supplementary_services_in_airplane_mode_bool"; field public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array"; field public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL = "display_hd_audio_property_bool"; @@ -44487,9 +44524,13 @@ package android.telephony { field public static final String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool"; field public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool"; field public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool"; + field public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool"; + field public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL = "editable_wfc_roaming_mode_bool"; + field public static final String KEY_EMERGENCY_NOTIFICATION_DELAY_INT = "emergency_notification_delay_int"; field public static final String KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY = "emergency_number_prefix_string_array"; field public static final String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool"; field public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool"; + field public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT = "enhanced_4g_lte_title_variant_int"; field public static final String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool"; field public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int"; field public static final String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array"; @@ -44498,13 +44539,17 @@ package android.telephony { field public static final String KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool"; field public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool"; field public static final String KEY_HIDE_IMS_APN_BOOL = "hide_ims_apn_bool"; + field public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL = "hide_lte_plus_data_icon_bool"; field public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool"; field public static final String KEY_HIDE_PRESET_APN_DETAILS_BOOL = "hide_preset_apn_details_bool"; field public static final String KEY_HIDE_SIM_LOCK_SETTINGS_BOOL = "hide_sim_lock_settings_bool"; + field public static final String KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS = "ignore_data_enabled_changed_for_video_calls"; + field public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL = "ignore_rtt_mode_setting_bool"; field public static final String KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL = "ignore_sim_network_locked_events_bool"; field public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int"; field public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int"; field public static final String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL = "is_ims_conference_size_enforced_bool"; + field public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool"; field public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "lte_rsrq_thresholds_int_array"; field public static final String KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY = "lte_rssnr_thresholds_int_array"; field public static final String KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL = "mdn_is_additional_voicemail_number_bool"; @@ -44540,6 +44585,7 @@ package android.telephony { field public static final String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl"; field public static final String KEY_MMS_USER_AGENT_STRING = "userAgent"; field public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int"; + field public static final String KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network"; field public static final String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array"; field public static final String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool"; field public static final String KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG = "opportunistic_network_data_switch_hysteresis_time_long"; @@ -44553,15 +44599,24 @@ package android.telephony { field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool"; field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array"; field public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string"; + field public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY = "read_only_apn_fields_string_array"; + field public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY = "read_only_apn_types_string_array"; field public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool"; field @Deprecated public static final String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool"; field public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool"; + field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool"; + field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool"; field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool"; + field public static final String KEY_SHOW_BLOCKING_PAY_PHONE_OPTION_BOOL = "show_blocking_pay_phone_option_bool"; field public static final String KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL = "show_call_blocking_disabled_notification_always_bool"; + field public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING = "show_carrier_data_icon_pattern_string"; field public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool"; field public static final String KEY_SHOW_ICCID_IN_SIM_STATUS_BOOL = "show_iccid_in_sim_status_bool"; + field public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL = "show_ims_registration_status_bool"; field public static final String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool"; field public static final String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL = "show_signal_strength_in_sim_status_bool"; + field public static final String KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL = "show_video_call_charges_alert_dialog_bool"; + field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool"; field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool"; field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool"; @@ -44569,14 +44624,20 @@ package android.telephony { field public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool"; field public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"; field public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool"; + field public static final String KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL = "support_enhanced_call_blocking_bool"; + field public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL = "support_ims_conference_event_package_bool"; field public static final String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool"; field public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool"; + field public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool"; + field public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY = "support_tdscdma_roaming_networks_string_array"; field public static final String KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL = "treat_downgraded_video_calls_as_video_calls_bool"; field public static final String KEY_TTY_SUPPORTED_BOOL = "tty_supported_bool"; + field public static final String KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY = "unloggable_numbers_string_array"; field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool"; field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool"; field public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool"; field public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool"; + field public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = "use_wfc_home_network_mode_in_roaming_network_bool"; field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool"; field public static final String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool"; field public static final String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int"; @@ -44589,6 +44650,8 @@ package android.telephony { field public static final String KEY_VVM_PREFETCH_BOOL = "vvm_prefetch_bool"; field public static final String KEY_VVM_SSL_ENABLED_BOOL = "vvm_ssl_enabled_bool"; field public static final String KEY_VVM_TYPE_STRING = "vvm_type_string"; + field public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string"; + field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool"; field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool"; } @@ -45570,6 +45633,7 @@ package android.telephony { field public static final int APPTYPE_USIM = 2; // 0x2 field public static final int AUTHTYPE_EAP_AKA = 129; // 0x81 field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80 + field public static final long CALLBACK_ON_MORE_ERROR_CODE_CHANGE = 130595455L; // 0x7c8ba7fL field public static final int CALL_STATE_IDLE = 0; // 0x0 field public static final int CALL_STATE_OFFHOOK = 2; // 0x2 field public static final int CALL_STATE_RINGING = 1; // 0x1 diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index d802177e249b..c8253a0b9e88 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -1 +1,126 @@ // Signature format: 2.0 +package android.app.timedetector { + + public final class PhoneTimeSuggestion implements android.os.Parcelable { + method public void addDebugInfo(@NonNull String); + method public void addDebugInfo(@NonNull java.util.List<java.lang.String>); + method public int describeContents(); + method @NonNull public java.util.List<java.lang.String> getDebugInfo(); + method public int getPhoneId(); + method @Nullable public android.os.TimestampedValue<java.lang.Long> getUtcTime(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.timedetector.PhoneTimeSuggestion> CREATOR; + } + + public static final class PhoneTimeSuggestion.Builder { + ctor public PhoneTimeSuggestion.Builder(int); + method @NonNull public android.app.timedetector.PhoneTimeSuggestion.Builder addDebugInfo(@NonNull String); + method @NonNull public android.app.timedetector.PhoneTimeSuggestion build(); + method @NonNull public android.app.timedetector.PhoneTimeSuggestion.Builder setUtcTime(@Nullable android.os.TimestampedValue<java.lang.Long>); + } + + public class TimeDetector { + method @RequiresPermission("android.permission.SUGGEST_PHONE_TIME_AND_ZONE") public void suggestPhoneTime(@NonNull android.app.timedetector.PhoneTimeSuggestion); + } + +} + +package android.app.timezonedetector { + + public final class PhoneTimeZoneSuggestion implements android.os.Parcelable { + method public void addDebugInfo(@NonNull String); + method public void addDebugInfo(@NonNull java.util.List<java.lang.String>); + method @NonNull public static android.app.timezonedetector.PhoneTimeZoneSuggestion createEmptySuggestion(int, @NonNull String); + method public int describeContents(); + method @NonNull public java.util.List<java.lang.String> getDebugInfo(); + method public int getMatchType(); + method public int getPhoneId(); + method public int getQuality(); + method @Nullable public String getZoneId(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.timezonedetector.PhoneTimeZoneSuggestion> CREATOR; + field public static final int MATCH_TYPE_EMULATOR_ZONE_ID = 4; // 0x4 + field public static final int MATCH_TYPE_NA = 0; // 0x0 + field public static final int MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET = 3; // 0x3 + field public static final int MATCH_TYPE_NETWORK_COUNTRY_ONLY = 2; // 0x2 + field public static final int MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY = 5; // 0x5 + field public static final int QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS = 3; // 0x3 + field public static final int QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET = 2; // 0x2 + field public static final int QUALITY_NA = 0; // 0x0 + field public static final int QUALITY_SINGLE_ZONE = 1; // 0x1 + } + + public static final class PhoneTimeZoneSuggestion.Builder { + ctor public PhoneTimeZoneSuggestion.Builder(int); + method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder addDebugInfo(@NonNull String); + method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion build(); + method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setMatchType(int); + method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setQuality(int); + method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setZoneId(@Nullable String); + } + + public class TimeZoneDetector { + method @RequiresPermission("android.permission.SUGGEST_PHONE_TIME_AND_ZONE") public void suggestPhoneTimeZone(@NonNull android.app.timezonedetector.PhoneTimeZoneSuggestion); + } + +} + +package android.os { + + public final class TimestampedValue<T> implements android.os.Parcelable { + ctor public TimestampedValue(long, @Nullable T); + method public int describeContents(); + method public long getReferenceTimeMillis(); + method @Nullable public T getValue(); + method public static long referenceTimeDifference(@NonNull android.os.TimestampedValue<?>, @NonNull android.os.TimestampedValue<?>); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.os.TimestampedValue<?>> CREATOR; + } + +} + +package android.timezone { + + public final class CountryTimeZones { + method @Nullable public android.icu.util.TimeZone getDefaultTimeZone(); + method @Nullable public String getDefaultTimeZoneId(); + method @NonNull public java.util.List<android.timezone.CountryTimeZones.TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long); + method public boolean hasUtcZone(long); + method public boolean isDefaultTimeZoneBoosted(); + method public boolean isForCountryCode(@NonNull String); + method @Nullable public android.timezone.CountryTimeZones.OffsetResult lookupByOffsetWithBias(int, @Nullable Boolean, @Nullable Integer, long, @Nullable android.icu.util.TimeZone); + } + + public static final class CountryTimeZones.OffsetResult { + ctor public CountryTimeZones.OffsetResult(@NonNull android.icu.util.TimeZone, boolean); + method @NonNull public android.icu.util.TimeZone getTimeZone(); + method public boolean isOnlyMatch(); + } + + public static final class CountryTimeZones.TimeZoneMapping { + method @Nullable public android.icu.util.TimeZone getTimeZone(); + method @NonNull public String getTimeZoneId(); + } + + public class TelephonyLookup { + method @NonNull public static android.timezone.TelephonyLookup getInstance(); + method @Nullable public android.timezone.TelephonyNetworkFinder getTelephonyNetworkFinder(); + } + + public class TelephonyNetwork { + method @NonNull public String getCountryIsoCode(); + method @NonNull public String getMcc(); + method @NonNull public String getMnc(); + } + + public class TelephonyNetworkFinder { + method @Nullable public android.timezone.TelephonyNetwork findNetworkByMccMnc(@NonNull String, @NonNull String); + } + + public final class TimeZoneFinder { + method @NonNull public static android.timezone.TimeZoneFinder getInstance(); + method @Nullable public android.timezone.CountryTimeZones lookupCountryTimeZones(@NonNull String); + } + +} + diff --git a/api/system-current.txt b/api/system-current.txt index d6a3433f66bd..8eac86f6b028 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -200,6 +200,7 @@ package android { field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES"; field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"; field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON"; + field public static final String SUGGEST_PHONE_TIME_AND_ZONE = "android.permission.SUGGEST_PHONE_TIME_AND_ZONE"; field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS"; field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED"; field public static final String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE"; @@ -683,6 +684,7 @@ package android.app { package android.app.admin { public class DevicePolicyManager { + method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwnerNameOnAnyUser(); @@ -4299,6 +4301,7 @@ package android.net { method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider); method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); + method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, "android.permission.NETWORK_STACK"}) public boolean shouldAvoidBadWifi(); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle); @@ -4316,6 +4319,7 @@ package android.net { field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb field public static final int TYPE_NONE = -1; // 0xffffffff + field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd } public abstract static class ConnectivityManager.OnStartTetheringCallback { @@ -4522,6 +4526,9 @@ package android.net { field @Deprecated public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore"; field public static final String EXTRA_NEW_SCORER = "newScorer"; field @Deprecated public static final String EXTRA_PACKAGE_NAME = "packageName"; + field public static final int SCORE_FILTER_CURRENT_NETWORK = 1; // 0x1 + field public static final int SCORE_FILTER_NONE = 0; // 0x0 + field public static final int SCORE_FILTER_SCAN_RESULTS = 2; // 0x2 } public static interface NetworkScoreManager.NetworkScoreCallback { @@ -4605,6 +4612,10 @@ package android.net { field public final android.net.RssiCurve rssiCurve; } + public abstract class SocketKeepalive implements java.lang.AutoCloseable { + field public static final int SUCCESS = 0; // 0x0 + } + public final class StaticIpConfiguration implements android.os.Parcelable { ctor public StaticIpConfiguration(); ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); @@ -4638,6 +4649,10 @@ package android.net { field @NonNull public final String specifier; } + public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { + method public boolean satisfiedBy(android.net.NetworkSpecifier); + } + public class TrafficStats { method public static void setThreadStatsTagApp(); method public static void setThreadStatsTagBackup(); @@ -6938,6 +6953,9 @@ package android.provider { } public static final class Telephony.CellBroadcasts implements android.provider.BaseColumns { + field @NonNull public static final String AUTHORITY_LEGACY = "cellbroadcast-legacy"; + field @NonNull public static final android.net.Uri AUTHORITY_LEGACY_URI; + field @NonNull public static final String CALL_METHOD_GET_PREFERENCE = "get_preference"; field public static final String CID = "cid"; field public static final String CMAS_CATEGORY = "cmas_category"; field public static final String CMAS_CERTAINTY = "cmas_certainty"; @@ -6968,6 +6986,20 @@ package android.provider { field public static final String SUB_ID = "sub_id"; } + public static final class Telephony.CellBroadcasts.Preference { + field @NonNull public static final String ENABLE_ALERT_VIBRATION_PREF = "enable_alert_vibrate"; + field @NonNull public static final String ENABLE_AREA_UPDATE_INFO_PREF = "enable_area_update_info_alerts"; + field @NonNull public static final String ENABLE_CMAS_AMBER_PREF = "enable_cmas_amber_alerts"; + field @NonNull public static final String ENABLE_CMAS_EXTREME_THREAT_PREF = "enable_cmas_extreme_threat_alerts"; + field @NonNull public static final String ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF = "receive_cmas_in_second_language"; + field @NonNull public static final String ENABLE_CMAS_PRESIDENTIAL_PREF = "enable_cmas_presidential_alerts"; + field @NonNull public static final String ENABLE_CMAS_SEVERE_THREAT_PREF = "enable_cmas_severe_threat_alerts"; + field @NonNull public static final String ENABLE_EMERGENCY_PERF = "enable_emergency_alerts"; + field @NonNull public static final String ENABLE_PUBLIC_SAFETY_PREF = "enable_public_safety_messages"; + field @NonNull public static final String ENABLE_STATE_LOCAL_TEST_PREF = "enable_state_local_test_alerts"; + field @NonNull public static final String ENABLE_TEST_ALERT_PREF = "enable_test_alerts"; + } + public static final class Telephony.SimInfo { field @NonNull public static final android.net.Uri CONTENT_URI; } @@ -8846,6 +8878,7 @@ package android.telephony { } public final class PreciseDataConnectionState implements android.os.Parcelable { + ctor public PreciseDataConnectionState(int, int, int, @NonNull String, @Nullable android.net.LinkProperties, int, @Nullable android.telephony.data.ApnSetting); method @Deprecated @NonNull public String getDataConnectionApn(); method @Deprecated public int getDataConnectionApnTypeBitMask(); method @Deprecated public int getDataConnectionFailCause(); @@ -8954,6 +8987,7 @@ package android.telephony { public class ServiceState implements android.os.Parcelable { method @NonNull public android.telephony.ServiceState createLocationInfoSanitizedCopy(boolean); method public void fillInNotifierBundle(@NonNull android.os.Bundle); + method public int getDataNetworkType(); method public int getDataRegistrationState(); method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int); method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoList(); @@ -9101,6 +9135,11 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>); } + public class SmsMessage { + method @Nullable public static android.telephony.SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[], boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static byte[] getSubmitPduEncodedMessage(boolean, @NonNull String, @NonNull String, int, int, int, int, int, int); + } + public class SubscriptionInfo implements android.os.Parcelable { method public boolean areUiccApplicationsEnabled(); method @Nullable public java.util.List<android.telephony.UiccAccessRule> getAccessRules(); @@ -9179,6 +9218,7 @@ package android.telephony { method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(); @@ -10171,6 +10211,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean); @@ -10435,6 +10476,7 @@ package android.telephony.ims.stub { method public String getConfigString(int); method public final void notifyProvisionedValueChanged(int, int); method public final void notifyProvisionedValueChanged(int, String); + method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); method public int setConfig(int, int); method public int setConfig(int, String); field public static final int CONFIG_RESULT_FAILED = 1; // 0x1 diff --git a/api/test-current.txt b/api/test-current.txt index a20b75c3355b..2a4af615e86c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3018,6 +3018,7 @@ package android.telephony { public class ServiceState implements android.os.Parcelable { method public void addNetworkRegistrationInfo(android.telephony.NetworkRegistrationInfo); + method public int getDataNetworkType(); method public void setCdmaSystemAndNetworkId(int, int); method public void setCellBandwidths(int[]); method public void setChannelNumber(int); @@ -3580,6 +3581,7 @@ package android.telephony.ims { method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public String getProvisioningStringValue(int); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @WorkerThread public boolean getRcsProvisioningStatusForCapability(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean); @@ -3803,6 +3805,7 @@ package android.telephony.ims.stub { method public String getConfigString(int); method public final void notifyProvisionedValueChanged(int, int); method public final void notifyProvisionedValueChanged(int, String); + method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); method public int setConfig(int, int); method public int setConfig(int, String); field public static final int CONFIG_RESULT_FAILED = 1; // 0x1 diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java index 6033655c8513..c2ee6dcd13b2 100644 --- a/cmds/sm/src/com/android/commands/sm/Sm.java +++ b/cmds/sm/src/com/android/commands/sm/Sm.java @@ -103,6 +103,10 @@ public final class Sm { runSetVirtualDisk(); } else if ("set-isolated-storage".equals(op)) { runIsolatedStorage(); + } else if ("start-checkpoint".equals(op)) { + runStartCheckpoint(); + } else if ("supports-checkpoint".equals(op)) { + runSupportsCheckpoint(); } else { throw new IllegalArgumentException(); } @@ -313,6 +317,27 @@ public final class Sm { } } + private void runStartCheckpoint() throws RemoteException { + final String numRetriesString = nextArg(); + if (numRetriesString == null) { + throw new IllegalArgumentException("Expected <num-retries>"); + } + int numRetries; + try { + numRetries = Integer.parseInt(numRetriesString); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("<num-retries> must be a positive integer"); + } + if (numRetries <= 0) { + throw new IllegalArgumentException("<num-retries> must be a positive integer"); + } + mSm.startCheckpoint(numRetries); + } + + private void runSupportsCheckpoint() throws RemoteException { + System.out.println(mSm.supportsCheckpoint()); + } + private String nextArg() { if (mNextArg >= mArgs.length) { return null; @@ -344,6 +369,10 @@ public final class Sm { System.err.println(""); System.err.println(" sm set-isolated-storage [on|off|default]"); System.err.println(""); + System.err.println(" sm start-checkpoint <num-retries>"); + System.err.println(""); + System.err.println(" sm supports-checkpoint"); + System.err.println(""); return 1; } } diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index da1a76f8169d..bc190fd293f1 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -50,6 +50,7 @@ cc_defaults { srcs: [ ":statsd_aidl", + ":ICarStatsService.aidl", "src/active_config_list.proto", "src/statsd_config.proto", "src/uid_data.proto", @@ -69,6 +70,7 @@ cc_defaults { "src/config/ConfigKey.cpp", "src/config/ConfigListener.cpp", "src/config/ConfigManager.cpp", + "src/external/CarStatsPuller.cpp", "src/external/GpuStatsPuller.cpp", "src/external/Perfetto.cpp", "src/external/StatsPuller.cpp", diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 1c7180ffbde1..64b7aae01619 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -266,7 +266,9 @@ status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* rep IResultReceiver::asInterface(data.readStrongBinder()); err = command(in, out, err, args, resultReceiver); - resultReceiver->send(err); + if (resultReceiver != nullptr) { + resultReceiver->send(err); + } return NO_ERROR; } default: { return BnStatsManager::onTransact(code, data, reply, flags); } @@ -411,13 +413,20 @@ status_t StatsService::command(int in, int out, int err, Vector<String8>& args, return cmd_trigger_active_config_broadcast(out, args); } if (!args[0].compare(String8("data-subscribe"))) { - if (mShellSubscriber == nullptr) { - mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager); + { + std::lock_guard<std::mutex> lock(mShellSubscriberMutex); + if (mShellSubscriber == nullptr) { + mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager); + } } int timeoutSec = -1; if (argCount >= 2) { timeoutSec = atoi(args[1].c_str()); } + if (resultReceiver == nullptr) { + ALOGI("Null resultReceiver given, no subscription will be started"); + return UNEXPECTED_NULL; + } mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec); return NO_ERROR; } @@ -1385,7 +1394,10 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackTypeIn, const android::String16& packageNameIn, - const int64_t packageVersionCodeIn) { + const int64_t packageVersionCodeIn, + const int32_t rollbackReasonIn, + const android::String16& + failingPackageNameIn) { // Note: We skip the usage stats op check here since we do not have a package name. // This is ok since we are overloading the usage_stats permission. // This method only sends data, it does not receive it. @@ -1407,7 +1419,8 @@ Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackType } android::util::stats_write(android::util::WATCHDOG_ROLLBACK_OCCURRED, - rollbackTypeIn, String8(packageNameIn).string(), packageVersionCodeIn); + rollbackTypeIn, String8(packageNameIn).string(), packageVersionCodeIn, + rollbackReasonIn, String8(failingPackageNameIn).string()); // Fast return to save disk read. if (rollbackTypeIn != android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 53b6ce989195..5f1335efc2e0 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -200,7 +200,9 @@ public: virtual Status sendWatchdogRollbackOccurredAtom( const int32_t rollbackTypeIn, const android::String16& packageNameIn, - const int64_t packageVersionCodeIn) override; + const int64_t packageVersionCodeIn, + const int32_t rollbackReasonIn, + const android::String16& failingPackageNameIn) override; /** * Binder call to get registered experiment IDs. @@ -432,6 +434,10 @@ private: sp<ShellSubscriber> mShellSubscriber; + /** + * Mutex for setting the shell subscriber + */ + mutable mutex mShellSubscriberMutex; std::shared_ptr<LogEventQueue> mEventQueue; FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index c92315687e71..bf89759385cb 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -50,6 +50,7 @@ import "frameworks/base/core/proto/android/stats/intelligence/enums.proto"; import "frameworks/base/core/proto/android/stats/launcher/launcher.proto"; import "frameworks/base/core/proto/android/stats/location/location_enums.proto"; import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto"; +import "frameworks/base/core/proto/android/stats/otaupdate/updateengine_enums.proto"; import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto"; import "frameworks/base/core/proto/android/stats/style/style_enums.proto"; import "frameworks/base/core/proto/android/telecomm/enums.proto"; @@ -123,10 +124,10 @@ message Atom { AppStartOccurred app_start_occurred = 48; AppStartCanceled app_start_canceled = 49; AppStartFullyDrawn app_start_fully_drawn = 50; - LmkKillOccurred lmk_kill_occurred = 51; + LmkKillOccurred lmk_kill_occurred = 51 [(log_from_module) = "lmkd"]; PictureInPictureStateChanged picture_in_picture_state_changed = 52; WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53; - LmkStateChanged lmk_state_changed = 54; + LmkStateChanged lmk_state_changed = 54 [(log_from_module) = "lmkd"]; AppStartMemoryStateCaptured app_start_memory_state_captured = 55; ShutdownSequenceReported shutdown_sequence_reported = 56; BootSequenceReported boot_sequence_reported = 57; @@ -322,14 +323,17 @@ message Atom { ExclusionRectStateChanged exclusion_rect_state_changed = 223; BackGesture back_gesture_reported_reported = 224; + UpdateEngineUpdateAttemptReported update_engine_update_attempt_reported = 225; + UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226; AppCompatibilityChangeReported app_compatibility_change_reported = 228 [(allow_from_any_uid) = true]; PerfettoUploaded perfetto_uploaded = 229 [(log_from_module) = "perfetto"]; + VmsClientConnectionStateChanged vms_client_connection_state_changed = 230; } // Pulled events will start at field 10000. - // Next: 10062 + // Next: 10067 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001; @@ -393,6 +397,8 @@ message Atom { CoolingDevice cooling_device = 10059; AppOps app_ops = 10060; ProcessSystemIonHeapSize process_system_ion_heap_size = 10061; + VmsClientStats vms_client_stats = 10065; + NotificationRemoteViews notification_remote_views = 10066; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -1689,6 +1695,19 @@ message WatchdogRollbackOccurred { optional string package_name = 2; optional int32 package_version_code = 3; + + enum RollbackReasonType { + REASON_UNKNOWN = 0; + REASON_NATIVE_CRASH = 1; + REASON_EXPLICIT_HEALTH_CHECK = 2; + REASON_APP_CRASH = 3; + REASON_APP_NOT_RESPONDING = 4; + } + optional RollbackReasonType rollback_reason = 4; + + // Set by RollbackPackageHealthObserver to be the package that is failing when a rollback + // is initiated. Empty if the package is unknown. + optional string failing_package_name = 5; } /** @@ -3713,6 +3732,33 @@ message RoleRequestResultReported { optional Result result = 9; } +/** + * Logs when a Vehicle Maps Service client's connection state has changed + * + * Logged from: + * packages/services/Car/service/src/com/android/car/stats/VmsClientLog.java + */ +message VmsClientConnectionStateChanged { + // The UID of the VMS client app + optional int32 uid = 1 [(is_uid) = true]; + + enum State { + UNKNOWN = 0; + // Attempting to connect to the client + CONNECTING = 1; + // Client connection established + CONNECTED = 2; + // Client connection closed unexpectedly + DISCONNECTED = 3; + // Client connection closed by VMS + TERMINATED = 4; + // Error establishing the client connection + CONNECTION_ERROR = 5; + } + + optional State state = 2; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// @@ -4727,6 +4773,24 @@ message ProcStatsPkgProc { optional ProcessStatsSectionProto proc_stats_section = 1; } +// Next Tag: 2 +message PackageRemoteViewInfoProto { + optional string package_name = 1; + // add per-package additional info here (like channels) +} + +// Next Tag: 2 +message NotificationRemoteViewsProto { + repeated PackageRemoteViewInfoProto package_remote_view_info = 1; +} + +/** + * Pulled from NotificationManagerService.java + */ +message NotificationRemoteViews { + optional NotificationRemoteViewsProto notification_remote_views = 1; +} + message PowerProfileProto { optional double cpu_suspend = 1; @@ -6858,7 +6922,6 @@ message AppCompatibilityChangeReported { // Where it was logged from. optional Source source = 4; - } /** @@ -6894,3 +6957,99 @@ message PerfettoUploaded { optional int64 trace_uuid_lsb = 2; optional int64 trace_uuid_msb = 3; } + +/** + * Pulls client metrics on data transferred via Vehicle Maps Service. + * Metrics are keyed by uid + layer. + * + * Pulled from: + * packages/services/Car/service/src/com/android/car/stats/CarStatsService.java + */ +message VmsClientStats { + // UID of the VMS client app + optional int32 uid = 1 [(is_uid) = true]; + + // VMS layer definition + optional int32 layer_type = 2; + optional int32 layer_channel = 3; + optional int32 layer_version = 4; + + // Bytes and packets sent by the client for the layer + optional int64 tx_bytes = 5; + optional int64 tx_packets = 6; + + // Bytes and packets received by the client for the layer + optional int64 rx_bytes = 7; + optional int64 rx_packets = 8; + + // Bytes and packets dropped due to client error + optional int64 dropped_bytes = 9; + optional int64 dropped_packets = 10; +} + +/** + * Information about an OTA update attempt by update_engine. + * Logged from platform/system/update_engine/metrics_reporter_android.cc + */ +message UpdateEngineUpdateAttemptReported { + // The number of attempts for the update engine to apply a given payload. + optional int32 attempt_number = 1; + + optional android.stats.otaupdate.PayloadType payload_type = 2; + + // The total time in minutes for the update engine to apply a given payload. + // The time is calculated by calling clock_gettime() / CLOCK_BOOTTIME; and + // it's increased when the system is sleeping. + optional int32 duration_boottime_in_minutes = 3; + + // The total time in minutes for the update engine to apply a given payload. + // The time is calculated by calling clock_gettime() / CLOCK_MONOTONIC_RAW; + // and it's not increased when the system is sleeping. + optional int32 duration_monotonic_in_minutes = 4; + + // The size of the payload in MiBs. + optional int32 payload_size_mib = 5; + + // The attempt result reported by the update engine for an OTA update. + optional android.stats.otaupdate.AttemptResult attempt_result = 6; + + // The error code reported by the update engine after an OTA update attempt + // on A/B devices. + optional android.stats.otaupdate.ErrorCode error_code = 7; + + // The build fingerprint of the source system. The value is read from a + // system property when the device takes the update. e.g. + // Android/aosp_sailfish/sailfish:10/QP1A.190425.004/5507117:userdebug/test-keys + optional string source_fingerprint = 8; +} + +/** + * Information about all the attempts the device make before finishing the + * successful update. + * Logged from platform/system/update_engine/metrics_reporter_android.cc + */ +message UpdateEngineSuccessfulUpdateReported { + // The number of attempts for the update engine to apply the payload for a + // successful update. + optional int32 attempt_count = 1; + + optional android.stats.otaupdate.PayloadType payload_type = 2; + + optional int32 payload_size_mib = 3; + + // The total number of bytes downloaded by update_engine since the last + // successful update. + optional int32 total_bytes_downloaded_mib = 4; + + // The ratio in percentage of the over-downloaded bytes compared to the + // total bytes needed to successfully install the update. e.g. 200 if we + // download 200MiB in total for a 100MiB package. + optional int32 download_overhead_percentage = 5; + + // The total time in minutes for the update engine to apply the payload for a + // successful update. + optional int32 total_duration_minutes = 6; + + // The number of reboot of the device during a successful update. + optional int32 reboot_count = 7; +} diff --git a/cmds/statsd/src/external/CarStatsPuller.cpp b/cmds/statsd/src/external/CarStatsPuller.cpp new file mode 100644 index 000000000000..70c0456b5eb4 --- /dev/null +++ b/cmds/statsd/src/external/CarStatsPuller.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG false +#include "Log.h" + +#include <binder/IServiceManager.h> +#include <com/android/internal/car/ICarStatsService.h> + +#include "CarStatsPuller.h" +#include "logd/LogEvent.h" +#include "stats_log_util.h" + +using android::binder::Status; +using com::android::internal::car::ICarStatsService; + +namespace android { +namespace os { +namespace statsd { + +static std::mutex gCarStatsMutex; +static sp<ICarStatsService> gCarStats = nullptr; + +class CarStatsDeathRecipient : public android::IBinder::DeathRecipient { + public: + CarStatsDeathRecipient() = default; + ~CarStatsDeathRecipient() override = default; + + // android::IBinder::DeathRecipient override: + void binderDied(const android::wp<android::IBinder>& /* who */) override { + ALOGE("Car service has died"); + std::lock_guard<std::mutex> lock(gCarStatsMutex); + if (gCarStats) { + sp<IBinder> binder = IInterface::asBinder(gCarStats); + binder->unlinkToDeath(this); + gCarStats = nullptr; + } + } +}; + +static sp<CarStatsDeathRecipient> gDeathRecipient = new CarStatsDeathRecipient(); + +static sp<ICarStatsService> getCarService() { + std::lock_guard<std::mutex> lock(gCarStatsMutex); + if (!gCarStats) { + const sp<IBinder> binder = defaultServiceManager()->checkService(String16("car_stats")); + if (!binder) { + ALOGW("Car service is unavailable"); + return nullptr; + } + gCarStats = interface_cast<ICarStatsService>(binder); + binder->linkToDeath(gDeathRecipient); + } + return gCarStats; +} + +CarStatsPuller::CarStatsPuller(const int tagId) : StatsPuller(tagId) { +} + +bool CarStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) { + const sp<ICarStatsService> carService = getCarService(); + if (!carService) { + return false; + } + + vector<StatsLogEventWrapper> returned_value; + Status status = carService->pullData(mTagId, &returned_value); + if (!status.isOk()) { + ALOGW("CarStatsPuller::pull failed for %d", mTagId); + return false; + } + + data->clear(); + for (const StatsLogEventWrapper& it : returned_value) { + LogEvent::createLogEvents(it, *data); + } + VLOG("CarStatsPuller::pull succeeded for %d", mTagId); + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/CarStatsPuller.h b/cmds/statsd/src/external/CarStatsPuller.h new file mode 100644 index 000000000000..ca0f1a9c9a17 --- /dev/null +++ b/cmds/statsd/src/external/CarStatsPuller.h @@ -0,0 +1,36 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +/** + * Pull atoms from CarService. + */ +class CarStatsPuller : public StatsPuller { +public: + explicit CarStatsPuller(const int tagId); + bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override; +}; + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 69e6a11b9ad7..1c9d7763bc83 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -27,6 +27,7 @@ #include "../logd/LogEvent.h" #include "../stats_log_util.h" #include "../statscompanion_util.h" +#include "CarStatsPuller.h" #include "GpuStatsPuller.h" #include "PowerStatsPuller.h" #include "ResourceHealthManagerPuller.h" @@ -267,6 +268,13 @@ std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { // App ops {android::util::APP_OPS, {.puller = new StatsCompanionServicePuller(android::util::APP_OPS)}}, + // VmsClientStats + {android::util::VMS_CLIENT_STATS, + {.additiveFields = {5, 6, 7, 8, 9, 10}, + .puller = new CarStatsPuller(android::util::VMS_CLIENT_STATS)}}, + // NotiifcationRemoteViews. + {android::util::NOTIFICATION_REMOTE_VIEWS, + {.puller = new StatsCompanionServicePuller(android::util::NOTIFICATION_REMOTE_VIEWS)}}, }; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { diff --git a/core/java/Android.bp b/core/java/Android.bp index fb27f74211fb..9a8e130436f8 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -7,3 +7,8 @@ filegroup { name: "IDropBoxManagerService.aidl", srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"], } + +filegroup { + name: "ICarStatsService.aidl", + srcs: ["com/android/internal/car/ICarStatsService.aidl"], +} diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 7ecaacae6b09..6519366cf7c6 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -563,6 +563,21 @@ public class AccountManager { * account, or the AbstractAcccountAuthenticator managing the account did so or because the * client shares a signature with the managing AbstractAccountAuthenticator. * + * <div class="caution"><p><b>Caution: </b>This method returns personal and sensitive user data. + * If your app accesses, collects, uses, or shares personal and sensitive data, you must clearly + * disclose that fact to users. For apps published on Google Play, policies protecting user data + * require that you do the following:</p> + * <ul> + * <li>Disclose to the user how your app accesses, collects, uses, or shares personal and + * sensitive data. Learn more about + * <a href="https://play.google.com/about/privacy-security-deception/user-data/#!#personal-sensitive">acceptable + * disclosure and consent</a>.</li> + * <li>Provide a privacy policy that describes your use of this data on- and off-device.</li> + * </ul> + * <p>To learn more, visit the + * <a href="https://play.google.com/about/privacy-security-deception/user-data">Google Play + * Policy regarding user data</a>.</p></div> + * * <p> * It is safe to call this method from the main thread. * @@ -649,6 +664,22 @@ public class AccountManager { * the account. For example, there are types corresponding to Google and Facebook. The exact * string token to use will be published somewhere associated with the authenticator in * question. + * </p> + * + * <div class="caution"><p><b>Caution: </b>This method returns personal and sensitive user data. + * If your app accesses, collects, uses, or shares personal and sensitive data, you must clearly + * disclose that fact to users. For apps published on Google Play, policies protecting user data + * require that you do the following:</p> + * <ul> + * <li>Disclose to the user how your app accesses, collects, uses, or shares personal and + * sensitive data. Learn more about + * <a href="https://play.google.com/about/privacy-security-deception/user-data/#!#personal-sensitive">acceptable + * disclosure and consent</a>.</li> + * <li>Provide a privacy policy that describes your use of this data on- and off-device.</li> + * </ul> + * <p>To learn more, visit the + * <a href="https://play.google.com/about/privacy-security-deception/user-data">Google Play + * Policy regarding user data</a>.</p></div> * * <p> * It is safe to call this method from the main thread. diff --git a/core/java/android/animation/FloatEvaluator.java b/core/java/android/animation/FloatEvaluator.java index 9463aa12d116..ae90e37d4c71 100644 --- a/core/java/android/animation/FloatEvaluator.java +++ b/core/java/android/animation/FloatEvaluator.java @@ -24,7 +24,7 @@ public class FloatEvaluator implements TypeEvaluator<Number> { /** * This function returns the result of linearly interpolating the start and end values, with * <code>fraction</code> representing the proportion between the start and end values. The - * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>, + * calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>, * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>, * and <code>t</code> is <code>fraction</code>. * diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 726a6195efc5..06e8b8e502d2 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2904,7 +2904,7 @@ public class ActivityManager { public static final int IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170; /** - * Constant for {@link #importance}: This process is contains services + * Constant for {@link #importance}: This process contains services * that should remain running. These are background services apps have * started, not something the user is aware of, so they may be killed by * the system relatively freely (though it is generally desired that they diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java index 08cad04401e9..996939eb9ee1 100644 --- a/core/java/android/app/AppOpsManagerInternal.java +++ b/core/java/android/app/AppOpsManagerInternal.java @@ -16,7 +16,6 @@ package android.app; -import android.annotation.NonNull; import android.util.SparseIntArray; import com.android.internal.util.function.QuadFunction; @@ -77,38 +76,10 @@ public abstract class AppOpsManagerInternal { public abstract void setDeviceAndProfileOwners(SparseIntArray owners); /** - * Sets the app-ops mode for a certain app-op and uid. - * - * <p>Similar as {@link AppOpsManager#setUidMode} but does not require the package manager to be - * working. Hence this can be used very early during boot. - * - * <p>Only for internal callers. Does <u>not</u> verify that package name belongs to uid. - * - * @param code The op code to set. - * @param uid The UID for which to set. - * @param mode The new mode to set. - */ - public abstract void setUidMode(int code, int uid, int mode); - - /** * Set all {@link #setMode (package) modes} for this uid to the default value. * * @param code The app-op * @param uid The uid */ public abstract void setAllPkgModesToDefault(int code, int uid); - - /** - * Get the (raw) mode of an app-op. - * - * <p>Does <u>not</u> verify that package belongs to uid. The caller needs to do that. - * - * @param code The code of the op - * @param uid The uid of the package the op belongs to - * @param packageName The package the op belongs to - * - * @return The mode of the op - */ - public abstract @AppOpsManager.Mode int checkOperationUnchecked(int code, int uid, - @NonNull String packageName); } diff --git a/core/java/android/app/DisabledWallpaperManager.java b/core/java/android/app/DisabledWallpaperManager.java new file mode 100644 index 000000000000..518594191e6c --- /dev/null +++ b/core/java/android/app/DisabledWallpaperManager.java @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A no-op implementation of {@link WallpaperManager}. + */ +final class DisabledWallpaperManager extends WallpaperManager { + + private static final String TAG = DisabledWallpaperManager.class.getSimpleName(); + + // Don't need to worry about synchronization + private static DisabledWallpaperManager sInstance; + + // TODO(b/138939803): STOPSHIP changed to false and/or remove it + private static final boolean DEBUG = true; + + @NonNull + static DisabledWallpaperManager getInstance() { + if (sInstance == null) { + sInstance = new DisabledWallpaperManager(); + } + return sInstance; + } + + private DisabledWallpaperManager() { + super(null, null, null); + } + + @Override + public boolean isWallpaperSupported() { + return false; + } + + @Override + public boolean isSetWallpaperAllowed() { + return false; + } + + // TODO(b/138939803): STOPSHIP methods below should not be necessary, + // callers should check if isWallpaperSupported(), consider removing them to keep this class + // simpler + + private static <T> T unsupported() { + if (DEBUG) Log.w(TAG, "unsupported method called; returning null", new Exception()); + return null; + } + + private static boolean unsupportedBoolean() { + if (DEBUG) Log.w(TAG, "unsupported method called; returning false", new Exception()); + return false; + } + + @Override + public Drawable getDrawable() { + return unsupported(); + } + + @Override + public Drawable getBuiltInDrawable() { + return unsupported(); + } + + @Override + public Drawable getBuiltInDrawable(int which) { + return unsupported(); + } + + @Override + public Drawable getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit, + float horizontalAlignment, float verticalAlignment) { + return unsupported(); + } + + @Override + public Drawable getBuiltInDrawable(int outWidth, int outHeight, boolean scaleToFit, + float horizontalAlignment, float verticalAlignment, int which) { + return unsupported(); + } + + @Override + public Drawable peekDrawable() { + return unsupported(); + } + + @Override + public Drawable getFastDrawable() { + return unsupported(); + } + + @Override + public Drawable peekFastDrawable() { + return unsupported(); + } + + @Override + public Bitmap getBitmap() { + return unsupported(); + } + + @Override + public Bitmap getBitmap(boolean hardware) { + return unsupported(); + } + + @Override + public Bitmap getBitmapAsUser(int userId, boolean hardware) { + return unsupported(); + } + + @Override + public ParcelFileDescriptor getWallpaperFile(int which) { + return unsupported(); + } + + @Override + public void addOnColorsChangedListener(OnColorsChangedListener listener, Handler handler) { + unsupported(); + } + + @Override + public void addOnColorsChangedListener(OnColorsChangedListener listener, Handler handler, + int userId) { + unsupported(); + } + + @Override + public void removeOnColorsChangedListener(OnColorsChangedListener callback) { + unsupported(); + } + + @Override + public void removeOnColorsChangedListener(OnColorsChangedListener callback, int userId) { + unsupported(); + } + + @Override + public WallpaperColors getWallpaperColors(int which) { + return unsupported(); + } + + @Override + public WallpaperColors getWallpaperColors(int which, int userId) { + return unsupported(); + } + + @Override + public ParcelFileDescriptor getWallpaperFile(int which, int userId) { + return unsupported(); + } + + @Override + public void forgetLoadedWallpaper() { + unsupported(); + } + + @Override + public WallpaperInfo getWallpaperInfo() { + return unsupported(); + } + + @Override + public WallpaperInfo getWallpaperInfo(int userId) { + return unsupported(); + } + + @Override + public int getWallpaperId(int which) { + return unsupported(); + } + + @Override + public int getWallpaperIdForUser(int which, int userId) { + return unsupported(); + } + + @Override + public Intent getCropAndSetWallpaperIntent(Uri imageUri) { + return unsupported(); + } + + @Override + public void setResource(int resid) throws IOException { + unsupported(); + } + + @Override + public int setResource(int resid, int which) throws IOException { + return unsupported(); + } + + @Override + public void setBitmap(Bitmap bitmap) throws IOException { + unsupported(); + } + + @Override + public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup) + throws IOException { + return unsupported(); + } + + @Override + public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which) + throws IOException { + return unsupported(); + } + + @Override + public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which, + int userId) throws IOException { + return unsupported(); + } + + @Override + public void setStream(InputStream bitmapData) throws IOException { + unsupported(); + } + + @Override + public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup) + throws IOException { + return unsupported(); + } + + @Override + public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup, + int which) throws IOException { + return unsupported(); + } + + @Override + public boolean hasResourceWallpaper(int resid) { + return unsupportedBoolean(); + } + + @Override + public int getDesiredMinimumWidth() { + return unsupported(); + } + + @Override + public int getDesiredMinimumHeight() { + return unsupported(); + } + + @Override + public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) { + unsupported(); + } + + @Override + public void setDisplayPadding(Rect padding) { + unsupported(); + } + + @Override + public void setDisplayOffset(IBinder windowToken, int x, int y) { + unsupported(); + } + + @Override + public void clearWallpaper() { + unsupported(); + } + + @Override + public void clearWallpaper(int which, int userId) { + unsupported(); + } + + @Override + public boolean setWallpaperComponent(ComponentName name) { + return unsupportedBoolean(); + } + + @Override + public boolean setWallpaperComponent(ComponentName name, int userId) { + return unsupportedBoolean(); + } + + @Override + public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) { + unsupported(); + } + + @Override + public void setWallpaperOffsetSteps(float xStep, float yStep) { + unsupported(); + } + + @Override + public void sendWallpaperCommand(IBinder windowToken, String action, int x, int y, int z, + Bundle extras) { + unsupported(); + } + + @Override + public void clearWallpaperOffsets(IBinder windowToken) { + unsupported(); + } + + @Override + public void clear() throws IOException { + unsupported(); + } + + @Override + public void clear(int which) throws IOException { + unsupported(); + } + + @Override + public boolean isWallpaperBackupEligible(int which) { + return unsupportedBoolean(); + } +} diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 9f51db88e7dc..32668980d131 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -202,4 +202,6 @@ interface INotificationManager void setPrivateNotificationsAllowed(boolean allow); boolean getPrivateNotificationsAllowed(); + + long pullStats(long startNs, int report, boolean doAgg, out List<ParcelFileDescriptor> stats); } diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl index a3e0845af0ce..f5809ba627ff 100644 --- a/core/java/android/app/IUiModeManager.aidl +++ b/core/java/android/app/IUiModeManager.aidl @@ -68,4 +68,9 @@ interface IUiModeManager { * Tells if Night mode is locked or not. */ boolean isNightModeLocked(); + + /** + * @hide + */ + boolean setNightModeActivated(boolean active); } diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 6acbf21e5602..bef8b04992f1 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -1257,7 +1257,12 @@ public final class PendingIntent implements Parcelable { return b != null ? new PendingIntent(b, in.getClassCookie(PendingIntent.class)) : null; } - /*package*/ PendingIntent(IIntentSender target) { + /** + * Creates a PendingIntent with the given target. + * @param target the backing IIntentSender + * @hide + */ + public PendingIntent(IIntentSender target) { mTarget = target; } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 88976e182e6d..e41510da479a 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -714,11 +714,22 @@ final class SystemServiceRegistry { @Override public WallpaperManager createService(ContextImpl ctx) throws ServiceNotFoundException { - final IBinder b; - if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) { - b = ServiceManager.getServiceOrThrow(Context.WALLPAPER_SERVICE); - } else { - b = ServiceManager.getService(Context.WALLPAPER_SERVICE); + final IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE); + if (b == null) { + // There are 2 reason service can be null: + // 1.Device doesn't support it - that's fine + // 2.App is running on instant mode - should fail + final boolean enabled = Resources.getSystem() + .getBoolean(com.android.internal.R.bool.config_enableWallpaperService); + if (!enabled) { + // Life moves on... + return DisabledWallpaperManager.getInstance(); + } + if (ctx.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) { + // Instant app + throw new ServiceNotFoundException(Context.WALLPAPER_SERVICE); + } + // Bad state - WallpaperManager methods will throw exception } IWallpaperManager service = IWallpaperManager.Stub.asInterface(b); return new WallpaperManager(service, ctx.getOuterContext(), diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 6582d240554b..363306483409 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -473,4 +473,18 @@ public class UiModeManager { } return true; } + + /** + * @hide* + */ + public boolean setNightModeActivated(boolean active) { + if (mService != null) { + try { + return mService.setNightModeActivated(active); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 102de950a129..7a6e314cf779 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -510,7 +510,9 @@ public class WallpaperManager { /*package*/ WallpaperManager(IWallpaperManager service, Context context, Handler handler) { mContext = context; - initGlobals(service, context.getMainLooper()); + if (service != null) { + initGlobals(service, context.getMainLooper()); + } } /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 12d3c59c2bbb..5afd82f198a7 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4032,9 +4032,17 @@ public class DevicePolicyManager { * Make the device lock immediately, as if the lock screen timeout has expired at the point of * this call. * <p> + * This method secures the device in response to an urgent situation, such as a lost or stolen + * device. After this method is called, the device must be unlocked using strong authentication + * (PIN, pattern, or password). This API is intended for use only by device admins. + * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} * to be able to call this method; if it has not, a security exception will be thrown. * <p> + * If there's no lock type set, this method forces the device to go to sleep but doesn't lock + * the device. Device admins who find the device in this state can lock an otherwise-insecure + * device by first calling {@link #resetPassword} to set the password and then lock the device. + * <p> * This method can be called on the {@link DevicePolicyManager} instance returned by * {@link #getParentProfileInstance(ComponentName)} in order to lock the parent profile. * <p> @@ -4051,9 +4059,17 @@ public class DevicePolicyManager { * Make the device lock immediately, as if the lock screen timeout has expired at the point of * this call. * <p> + * This method secures the device in response to an urgent situation, such as a lost or stolen + * device. After this method is called, the device must be unlocked using strong authentication + * (PIN, pattern, or password). This API is intended for use only by device admins. + * <p> * The calling device admin must have requested {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} * to be able to call this method; if it has not, a security exception will be thrown. * <p> + * If there's no lock type set, this method forces the device to go to sleep but doesn't lock + * the device. Device admins who find the device in this state can lock an otherwise-insecure + * device by first calling {@link #resetPassword} to set the password and then lock the device. + * <p> * This method can be called on the {@link DevicePolicyManager} instance returned by * {@link #getParentProfileInstance(ComponentName)} in order to lock the parent profile. * @@ -6993,7 +7009,9 @@ public class DevicePolicyManager { * @param userHandle The user for whom to check the caller-id permission * @hide */ - public boolean getBluetoothContactSharingDisabled(UserHandle userHandle) { + @SystemApi + @RequiresPermission(permission.INTERACT_ACROSS_USERS) + public boolean getBluetoothContactSharingDisabled(@NonNull UserHandle userHandle) { if (mService != null) { try { return mService.getBluetoothContactSharingDisabledForUser(userHandle @@ -8080,7 +8098,7 @@ public class DevicePolicyManager { * Sets which system features are enabled when the device runs in lock task mode. This method * doesn't affect the features when lock task mode is inactive. Any system features not included * in {@code flags} are implicitly disabled when calling this method. By default, only - * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS} is enabled—all the other features are disabled. To + * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS} is enabled; all the other features are disabled. To * disable the global actions dialog, call this method omitting * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}. * diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java index 36f5f9673236..5c0ddc1859db 100644 --- a/core/java/android/app/backup/WallpaperBackupHelper.java +++ b/core/java/android/app/backup/WallpaperBackupHelper.java @@ -85,6 +85,10 @@ public class WallpaperBackupHelper extends FileBackupHelperBase implements Backu */ @Override public void restoreEntity(BackupDataInputStream data) { + if (mWpm == null) { + Slog.w(TAG, "restoreEntity(): no wallpaper service"); + return; + } final String key = data.getKey(); if (isKeyInList(key, mKeys)) { if (key.equals(WALLPAPER_IMAGE_KEY)) { diff --git a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java index 1bb81b1487af..1e6ab4136187 100644 --- a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java +++ b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java @@ -45,6 +45,17 @@ import java.util.concurrent.Executor; */ @SystemApi public final class ContentSuggestionsManager { + /** + * Key into the extras Bundle passed to {@link #provideContextImage(int, Bundle)}. + * This can be used to provide the bitmap to + * {@link android.service.contentsuggestions.ContentSuggestionsService}. + * The value must be a {@link android.graphics.Bitmap} with the + * config {@link android.graphics.Bitmap.Config.HARDWARE}. + * + * @hide + */ + public static final String EXTRA_BITMAP = "android.contentsuggestions.extra.BITMAP"; + private static final String TAG = ContentSuggestionsManager.class.getSimpleName(); /** @@ -70,7 +81,7 @@ public final class ContentSuggestionsManager { * system content suggestions service. * * @param taskId of the task to snapshot. - * @param imageContextRequestExtras sent with with request to provide implementation specific + * @param imageContextRequestExtras sent with request to provide implementation specific * extra information. */ public void provideContextImage( diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java index 955093d3380e..90ecce2a2170 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -390,6 +390,8 @@ public class SliceManager { } Bundle extras = new Bundle(); extras.putParcelable(SliceProvider.EXTRA_INTENT, intent); + extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS, + new ArrayList<>(supportedSpecs)); final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras); if (res == null) { return null; diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java index 479e4b4efb4c..bd649f88f40a 100644 --- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java +++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java @@ -18,6 +18,7 @@ package android.app.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.os.TimestampedValue; @@ -28,17 +29,23 @@ import java.util.List; import java.util.Objects; /** - * A time signal from a telephony source. The value can be {@code null} to indicate that the - * telephony source has entered an "un-opinionated" state and any previously sent suggestions are - * being withdrawn. When not {@code null}, the value consists of the number of milliseconds elapsed - * since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime clock when that number - * was established. The elapsed realtime clock is considered accurate but volatile, so time signals - * must not be persisted across device resets. + * A time suggestion from an identified telephony source. e.g. from NITZ information from a specific + * radio. + * + * <p>The time value can be {@code null} to indicate that the telephony source has entered an + * "un-opinionated" state and any previous suggestions from the source are being withdrawn. When not + * {@code null}, the value consists of the number of milliseconds elapsed since 1/1/1970 00:00:00 + * UTC and the time according to the elapsed realtime clock when that number was established. The + * elapsed realtime clock is considered accurate but volatile, so time suggestions must not be + * persisted across device resets. * * @hide */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public final class PhoneTimeSuggestion implements Parcelable { + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final @NonNull Parcelable.Creator<PhoneTimeSuggestion> CREATOR = new Parcelable.Creator<PhoneTimeSuggestion>() { public PhoneTimeSuggestion createFromParcel(Parcel in) { @@ -85,15 +92,27 @@ public final class PhoneTimeSuggestion implements Parcelable { dest.writeList(mDebugInfo); } + /** + * Returns an identifier for the source of this suggestion. When a device has several "phones", + * i.e. sim slots or equivalent, it is used to identify which one. + */ public int getPhoneId() { return mPhoneId; } + /** + * Returns the suggestion. {@code null} means that the caller is no longer sure what time it + * is. + */ @Nullable public TimestampedValue<Long> getUtcTime() { return mUtcTime; } + /** + * Returns debug metadata for the suggestion. The information is present in {@link #toString()} + * but is not considered for {@link #equals(Object)} and {@link #hashCode()}. + */ @NonNull public List<String> getDebugInfo() { return mDebugInfo == null @@ -105,7 +124,7 @@ public final class PhoneTimeSuggestion implements Parcelable { * information is present in {@link #toString()} but is not considered for * {@link #equals(Object)} and {@link #hashCode()}. */ - public void addDebugInfo(String debugInfo) { + public void addDebugInfo(@NonNull String debugInfo) { if (mDebugInfo == null) { mDebugInfo = new ArrayList<>(); } @@ -156,16 +175,19 @@ public final class PhoneTimeSuggestion implements Parcelable { * * @hide */ - public static class Builder { + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final class Builder { private final int mPhoneId; - private TimestampedValue<Long> mUtcTime; - private List<String> mDebugInfo; + @Nullable private TimestampedValue<Long> mUtcTime; + @Nullable private List<String> mDebugInfo; + /** Creates a builder with the specified {@code phoneId}. */ public Builder(int phoneId) { mPhoneId = phoneId; } /** Returns the builder for call chaining. */ + @NonNull public Builder setUtcTime(@Nullable TimestampedValue<Long> utcTime) { if (utcTime != null) { // utcTime can be null, but the value it holds cannot. @@ -177,6 +199,7 @@ public final class PhoneTimeSuggestion implements Parcelable { } /** Returns the builder for call chaining. */ + @NonNull public Builder addDebugInfo(@NonNull String debugInfo) { if (mDebugInfo == null) { mDebugInfo = new ArrayList<>(); @@ -186,6 +209,7 @@ public final class PhoneTimeSuggestion implements Parcelable { } /** Returns the {@link PhoneTimeSuggestion}. */ + @NonNull public PhoneTimeSuggestion build() { return new PhoneTimeSuggestion(this); } diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java index 54dd1bed5361..7c29f017c02b 100644 --- a/core/java/android/app/timedetector/TimeDetector.java +++ b/core/java/android/app/timedetector/TimeDetector.java @@ -18,6 +18,7 @@ package android.app.timedetector; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; @@ -29,8 +30,11 @@ import android.util.Log; /** * The interface through which system components can send signals to the TimeDetectorService. + * + * <p>This class is marked non-final for mockito. * @hide */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @SystemService(Context.TIME_DETECTOR_SERVICE) public class TimeDetector { private static final String TAG = "timedetector.TimeDetector"; @@ -38,6 +42,7 @@ public class TimeDetector { private final ITimeDetectorService mITimeDetectorService; + /** @hide */ public TimeDetector() throws ServiceNotFoundException { mITimeDetectorService = ITimeDetectorService.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.TIME_DETECTOR_SERVICE)); @@ -62,6 +67,8 @@ public class TimeDetector { /** * Suggests the user's manually entered current time to the detector. + * + * @hide */ @RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE) public void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion) { @@ -77,6 +84,8 @@ public class TimeDetector { /** * A shared utility method to create a {@link ManualTimeSuggestion}. + * + * @hide */ public static ManualTimeSuggestion createManualTimeSuggestion(long when, String why) { TimestampedValue<Long> utcTime = @@ -88,6 +97,8 @@ public class TimeDetector { /** * Suggests the time according to a network time source like NTP. + * + * @hide */ @RequiresPermission(android.Manifest.permission.SET_TIME) public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) { diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java index e8162488394c..d71ffcb9f772 100644 --- a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java +++ b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java @@ -19,6 +19,7 @@ package android.app.timezonedetector; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -30,12 +31,27 @@ import java.util.List; import java.util.Objects; /** - * A suggested time zone from a Phone-based signal, e.g. from MCC and NITZ information. + * A time zone suggestion from an identified telephony source, e.g. from MCC and NITZ information + * associated with a specific radio. + * + * <p>The time zone ID can be {@code null} to indicate that the telephony source has entered an + * "un-opinionated" state and any previous suggestions from that source are being withdrawn. + * When not {@code null}, the value consists of a suggested time zone ID and metadata that can be + * used to judge quality / certainty of the suggestion. + * + * <p>{@code matchType} must be set to {@link #MATCH_TYPE_NA} when {@code zoneId} is {@code null}, + * and one of the other {@code MATCH_TYPE_} values when it is not {@code null}. + * + * <p>{@code quality} must be set to {@link #QUALITY_NA} when {@code zoneId} is {@code null}, + * and one of the other {@code QUALITY_} values when it is not {@code null}. * * @hide */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public final class PhoneTimeZoneSuggestion implements Parcelable { + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @NonNull public static final Creator<PhoneTimeZoneSuggestion> CREATOR = new Creator<PhoneTimeZoneSuggestion>() { @@ -58,6 +74,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { return new Builder(phoneId).addDebugInfo(debugInfo).build(); } + /** @hide */ @IntDef({ MATCH_TYPE_NA, MATCH_TYPE_NETWORK_COUNTRY_ONLY, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, MATCH_TYPE_EMULATOR_ZONE_ID, MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY }) @Retention(RetentionPolicy.SOURCE) @@ -90,6 +107,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { */ public static final int MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY = 5; + /** @hide */ @IntDef({ QUALITY_NA, QUALITY_SINGLE_ZONE, QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS }) @Retention(RetentionPolicy.SOURCE) @@ -115,7 +133,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { /** * The ID of the phone this suggestion is associated with. For multiple-sim devices this - * helps to establish origin so filtering / stickiness can be implemented. + * helps to establish source so filtering / stickiness can be implemented. */ private final int mPhoneId; @@ -123,6 +141,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { * The suggestion. {@code null} means there is no current suggestion and any previous suggestion * should be forgotten. */ + @Nullable private final String mZoneId; /** @@ -139,9 +158,10 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { private final int mQuality; /** - * Free-form debug information about how the signal was derived. Used for debug only, + * Free-form debug information about how the suggestion was derived. Used for debug only, * intentionally not used in equals(), etc. */ + @Nullable private List<String> mDebugInfo; private PhoneTimeZoneSuggestion(Builder builder) { @@ -182,25 +202,47 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { return 0; } + /** + * Returns an identifier for the source of this suggestion. When a device has several "phones", + * i.e. sim slots or equivalent, it is used to identify which one. + */ public int getPhoneId() { return mPhoneId; } + /** + * Returns the suggested time zone Olson ID, e.g. "America/Los_Angeles". {@code null} means that + * the caller is no longer sure what the current time zone is. See + * {@link PhoneTimeZoneSuggestion} for the associated {@code matchType} / {@code quality} rules. + */ @Nullable public String getZoneId() { return mZoneId; } + /** + * Returns information about how the suggestion was determined which could be used to rank + * suggestions when several are available from different sources. See + * {@link PhoneTimeZoneSuggestion} for the associated rules. + */ @MatchType public int getMatchType() { return mMatchType; } + /** + * Returns information about the likelihood of the suggested zone being correct. See + * {@link PhoneTimeZoneSuggestion} for the associated rules. + */ @Quality public int getQuality() { return mQuality; } + /** + * Returns debug metadata for the suggestion. The information is present in {@link #toString()} + * but is not considered for {@link #equals(Object)} and {@link #hashCode()}. + */ @NonNull public List<String> getDebugInfo() { return mDebugInfo == null @@ -267,36 +309,43 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { * * @hide */ - public static class Builder { + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final class Builder { private final int mPhoneId; - private String mZoneId; + @Nullable private String mZoneId; @MatchType private int mMatchType; @Quality private int mQuality; - private List<String> mDebugInfo; + @Nullable private List<String> mDebugInfo; public Builder(int phoneId) { mPhoneId = phoneId; } - /** Returns the builder for call chaining. */ - public Builder setZoneId(String zoneId) { + /** + * Returns the builder for call chaining. + */ + @NonNull + public Builder setZoneId(@Nullable String zoneId) { mZoneId = zoneId; return this; } /** Returns the builder for call chaining. */ + @NonNull public Builder setMatchType(@MatchType int matchType) { mMatchType = matchType; return this; } /** Returns the builder for call chaining. */ + @NonNull public Builder setQuality(@Quality int quality) { mQuality = quality; return this; } /** Returns the builder for call chaining. */ + @NonNull public Builder addDebugInfo(@NonNull String debugInfo) { if (mDebugInfo == null) { mDebugInfo = new ArrayList<>(); @@ -333,6 +382,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { } /** Returns the {@link PhoneTimeZoneSuggestion}. */ + @NonNull public PhoneTimeZoneSuggestion build() { validate(); return new PhoneTimeZoneSuggestion(this); diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java index e165d8a76caa..5b5f311264e3 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetector.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java @@ -18,6 +18,7 @@ package android.app.timezonedetector; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; @@ -28,8 +29,10 @@ import android.util.Log; /** * The interface through which system components can send signals to the TimeZoneDetectorService. * + * <p>This class is non-final for mockito. * @hide */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @SystemService(Context.TIME_ZONE_DETECTOR_SERVICE) public class TimeZoneDetector { private static final String TAG = "timezonedetector.TimeZoneDetector"; @@ -37,6 +40,7 @@ public class TimeZoneDetector { private final ITimeZoneDetectorService mITimeZoneDetectorService; + /** @hide */ public TimeZoneDetector() throws ServiceNotFoundException { mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE)); @@ -46,7 +50,10 @@ public class TimeZoneDetector { * Suggests the current time zone, determined using telephony signals, to the detector. The * detector may ignore the signal based on system settings, whether better information is * available, and so on. + * + * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE) public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) { if (DEBUG) { @@ -62,6 +69,8 @@ public class TimeZoneDetector { /** * Suggests the current time zone, determined for the user's manually information, to the * detector. The detector may ignore the signal based on system settings. + * + * @hide */ @RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE) public void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) { @@ -77,6 +86,8 @@ public class TimeZoneDetector { /** * A shared utility method to create a {@link ManualTimeZoneSuggestion}. + * + * @hide */ public static ManualTimeZoneSuggestion createManualTimeZoneSuggestion(String tzId, String why) { ManualTimeZoneSuggestion suggestion = new ManualTimeZoneSuggestion(tzId); diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 9fe4dd66b874..61b23b675190 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1107,6 +1107,24 @@ public final class BluetoothDevice implements Parcelable { } /** + * Get the Bluetooth alias of the remote device. + * If Alias is null, get the Bluetooth name instead. + * + * @return the Bluetooth alias, or null if no alias or there was a problem + * @hide + * @see #getAlias() + * @see #getName() + */ + @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.") + public String getAliasName() { + String name = getAlias(); + if (name == null) { + name = getName(); + } + return name; + } + + /** * Get the most recent identified battery level of this Bluetooth device * * @return Battery level in percents from 0 to 100, or {@link #BATTERY_LEVEL_UNKNOWN} if diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java index 038994fb5535..7511fd051e41 100644 --- a/core/java/android/bluetooth/le/ScanFilter.java +++ b/core/java/android/bluetooth/le/ScanFilter.java @@ -671,8 +671,6 @@ public final class ScanFilter implements Parcelable { /** * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id. - * <p> - * Note the first two bytes of the {@code manufacturerData} is the manufacturerId. * * @throws IllegalArgumentException If the {@code manufacturerId} is invalid. */ diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java index e955c2df2881..f465395849f3 100644 --- a/core/java/android/content/ComponentName.java +++ b/core/java/android/content/ComponentName.java @@ -305,6 +305,12 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co proto.end(token); } + /** + * {@inheritDoc} + * + * <p>Two components are considered to be equal if the packages in which they reside have the + * same name, and if the classes that implement each component also have the same name. + */ @Override public boolean equals(Object obj) { try { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 9c7bf1f7c996..281732cdd0ad 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2689,6 +2689,9 @@ public class Intent implements Parcelable, Cloneable { * that application is first launched (that is the first time it is moved * out of the stopped state). The data contains the name of the package. * + * <p>When the application is first launched, the application itself doesn't receive this + * broadcast.</p> + * * <p class="note">This is a protected intent that can only be sent * by the system. */ diff --git a/core/java/android/content/SyncStats.java b/core/java/android/content/SyncStats.java index 03b2250edee1..9596a6016c44 100644 --- a/core/java/android/content/SyncStats.java +++ b/core/java/android/content/SyncStats.java @@ -58,7 +58,7 @@ public class SyncStats implements Parcelable { * attempted to update or delete a version of a resource on the server. This is expected * to clear itself automatically once the new state is retrieved from the server, * though it may remain until the user intervenes manually, perhaps by clearing the - * local storage and starting over frmo scratch. This is considered a hard error. + * local storage and starting over from scratch. This is considered a hard error. */ public long numConflictDetectedExceptions; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 78db3d82ce93..2217807254e9 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -101,19 +101,6 @@ import java.util.concurrent.Executor; * <p> * The ApiDemos project contains examples of using this API: * <code>ApiDemos/src/com/example/android/apis/content/InstallApk*.java</code>. - * <p> - * On Android Q or above, an app installed notification will be posted - * by system after a new app is installed. - * To customize installer's notification icon, you should declare the following in the manifest - * <application> as follows: </p> - * <pre> - * <meta-data android:name="com.android.packageinstaller.notification.smallIcon" - * android:resource="@drawable/installer_notification_icon"/> - * </pre> - * <pre> - * <meta-data android:name="com.android.packageinstaller.notification.color" - * android:resource="@color/installer_notification_color"/> - * </pre> */ public class PackageInstaller { private static final String TAG = "PackageInstaller"; diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index caf095884db2..0a390fed2046 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -16,11 +16,16 @@ package android.content.pm; +import android.annotation.IntDef; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; import android.os.UserManager; +import android.util.DebugUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Per-user information. @@ -93,6 +98,25 @@ public class UserInfo implements Parcelable { */ public static final int FLAG_DEMO = 0x00000200; + /** + * @hide + */ + @IntDef(flag = true, prefix = "FLAG_", value = { + FLAG_PRIMARY, + FLAG_ADMIN, + FLAG_GUEST, + FLAG_RESTRICTED, + FLAG_INITIALIZED, + FLAG_MANAGED_PROFILE, + FLAG_DISABLED, + FLAG_QUIET_MODE, + FLAG_EPHEMERAL, + FLAG_DEMO + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UserInfoFlag { + } + public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL; @UnsupportedAppUsage @@ -127,6 +151,18 @@ public class UserInfo implements Parcelable { @UnsupportedAppUsage public boolean guestToRemove; + /** + * This is used to optimize the creation of an user, i.e. OEMs might choose to pre-create a + * number of users at the first boot, so the actual creation later is faster. + * + * <p>A {@code preCreated} user is not a real user yet, so it should not show up on regular + * user operations (other than user creation per se). + * + * <p>Once the pre-created is used to create a "real" user later on, {@code preCreate} is set to + * {@code false}. + */ + public boolean preCreated; + @UnsupportedAppUsage public UserInfo(int id, String name, int flags) { this(id, name, null, flags); @@ -154,6 +190,13 @@ public class UserInfo implements Parcelable { @UnsupportedAppUsage public boolean isGuest() { + return isGuest(flags); + } + + /** + * Checks if the flag denotes a guest user. + */ + public static boolean isGuest(@UserInfoFlag int flags) { return (flags & FLAG_GUEST) == FLAG_GUEST; } @@ -164,6 +207,13 @@ public class UserInfo implements Parcelable { @UnsupportedAppUsage public boolean isManagedProfile() { + return isManagedProfile(flags); + } + + /** + * Checks if the flag denotes a managed profile. + */ + public static boolean isManagedProfile(@UserInfoFlag int flags) { return (flags & FLAG_MANAGED_PROFILE) == FLAG_MANAGED_PROFILE; } @@ -251,6 +301,7 @@ public class UserInfo implements Parcelable { lastLoggedInTime = orig.lastLoggedInTime; lastLoggedInFingerprint = orig.lastLoggedInFingerprint; partial = orig.partial; + preCreated = orig.preCreated; profileGroupId = orig.profileGroupId; restrictedProfileParentId = orig.restrictedProfileParentId; guestToRemove = orig.guestToRemove; @@ -267,6 +318,22 @@ public class UserInfo implements Parcelable { return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}"; } + /** @hide */ + public String toFullString() { + return "UserInfo[id=" + id + + ", name=" + name + + ", flags=" + flagsToString(flags) + + (preCreated ? " (pre-created)" : "") + + (partial ? " (partial)" : "") + + "]"; + } + + /** @hide */ + public static String flagsToString(int flags) { + return DebugUtils.flagsToString(UserInfo.class, "FLAG_", flags); + } + + @Override public int describeContents() { return 0; } @@ -280,9 +347,10 @@ public class UserInfo implements Parcelable { dest.writeLong(creationTime); dest.writeLong(lastLoggedInTime); dest.writeString(lastLoggedInFingerprint); - dest.writeInt(partial ? 1 : 0); + dest.writeBoolean(partial); + dest.writeBoolean(preCreated); dest.writeInt(profileGroupId); - dest.writeInt(guestToRemove ? 1 : 0); + dest.writeBoolean(guestToRemove); dest.writeInt(restrictedProfileParentId); dest.writeInt(profileBadge); } @@ -307,9 +375,10 @@ public class UserInfo implements Parcelable { creationTime = source.readLong(); lastLoggedInTime = source.readLong(); lastLoggedInFingerprint = source.readString(); - partial = source.readInt() != 0; + partial = source.readBoolean(); + preCreated = source.readBoolean(); profileGroupId = source.readInt(); - guestToRemove = source.readInt() != 0; + guestToRemove = source.readBoolean(); restrictedProfileParentId = source.readInt(); profileBadge = source.readInt(); } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index 90affdfed39f..29c5c935c1bf 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -360,8 +360,9 @@ public class TypedArray { /** * Retrieve the boolean value for the attribute at <var>index</var>. * <p> - * If the attribute is an integer value, this method will return whether - * it is equal to zero. If the attribute is not a boolean or integer value, + * If the attribute is an integer value, this method returns false if the + * attribute is equal to zero, and true otherwise. + * If the attribute is not a boolean or integer value, * this method will attempt to coerce it to an integer using * {@link Integer#decode(String)} and return whether it is equal to zero. * diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java index 6eaf54bd0d4d..23f18a80caf8 100644 --- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java +++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java @@ -1163,7 +1163,7 @@ public final class MandatoryStreamCombination { if (orderedPreviewSizes != null) { for (Size size : orderedPreviewSizes) { if ((mDisplaySize.getWidth() >= size.getWidth()) && - (mDisplaySize.getWidth() >= size.getHeight())) { + (mDisplaySize.getHeight() >= size.getHeight())) { return size; } } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 929edf0835bb..674c58d00dcd 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -662,7 +662,7 @@ public class ConnectivityManager { * {@hide} */ @Deprecated - @UnsupportedAppUsage + @SystemApi public static final int TYPE_WIFI_P2P = 13; /** @@ -3622,14 +3622,26 @@ public class ConnectivityManager { /** * Helper function to request a network with a particular legacy type. * - * This is temporarily public @hide so it can be called by system code that uses the - * NetworkRequest API to request networks but relies on CONNECTIVITY_ACTION broadcasts for - * instead network notifications. + * @deprecated This is temporarily public for tethering to backwards compatibility that uses + * the NetworkRequest API to request networks with legacy type and relies on + * CONNECTIVITY_ACTION broadcasts instead of NetworkCallbacks. New caller should use + * {@link #requestNetwork(NetworkRequest, NetworkCallback, Handler)} instead. * * TODO: update said system code to rely on NetworkCallbacks and make this method private. + + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * @param timeoutMs The time in milliseconds to attempt looking for a suitable network + * before {@link NetworkCallback#onUnavailable()} is called. The timeout must + * be a positive value (i.e. >0). + * @param legacyType to specify the network type(#TYPE_*). + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. * * @hide */ + @SystemApi + @Deprecated public void requestNetwork(@NonNull NetworkRequest request, @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType, @NonNull Handler handler) { diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 385cb1d68b57..72a6b397a30c 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -57,9 +57,6 @@ interface INetworkPolicyManager { @UnsupportedAppUsage boolean getRestrictBackground(); - /** Callback used to change internal state on tethering */ - void onTetheringChanged(String iface, boolean tethering); - /** Gets the restrict background status based on the caller's UID: 1 - disabled 2 - whitelisted diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index c7b30096db29..3be49d530c64 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -300,22 +300,34 @@ public class NetworkRequest implements Parcelable { * this without a single transport set will generate an exception, as will * subsequently adding or removing transports after this is set. * </p> - * The interpretation of this {@code String} is bearer specific and bearers that use - * it should document their particulars. For example, Bluetooth may use some sort of - * device id while WiFi could used ssid and/or bssid. Cellular may use carrier spn. + * If the {@code networkSpecifier} is provided, it shall be interpreted as follows: + * <ul> + * <li>If the specifier can be parsed as an integer, it will be treated as a + * {@link android.net TelephonyNetworkSpecifier}, and the provided integer will be + * interpreted as a SubscriptionId. + * <li>If the value is an ethernet interface name, it will be treated as such. + * <li>For all other cases, the behavior is undefined. + * </ul> * - * @param networkSpecifier An {@code String} of opaque format used to specify the bearer - * specific network specifier where the bearer has a choice of - * networks. + * @param networkSpecifier A {@code String} of either a SubscriptionId in cellular + * network request or an ethernet interface name in ethernet + * network request. + * + * @deprecated Use {@link #setNetworkSpecifier(NetworkSpecifier)} instead. */ + @Deprecated public Builder setNetworkSpecifier(String networkSpecifier) { - /* - * A StringNetworkSpecifier does not accept null or empty ("") strings. When network - * specifiers were strings a null string and an empty string were considered equivalent. - * Hence no meaning is attached to a null or empty ("") string. - */ - return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null - : new StringNetworkSpecifier(networkSpecifier)); + try { + int subId = Integer.parseInt(networkSpecifier); + return setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(subId).build()); + } catch (NumberFormatException nfe) { + // A StringNetworkSpecifier does not accept null or empty ("") strings. When network + // specifiers were strings a null string and an empty string were considered + // equivalent. Hence no meaning is attached to a null or empty ("") string. + return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null + : new StringNetworkSpecifier(networkSpecifier)); + } } /** diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index f6dc52522cb2..c233ec0e52cf 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -163,27 +163,26 @@ public class NetworkScoreManager { public static final String EXTRA_NEW_SCORER = "newScorer"; /** @hide */ - @IntDef({CACHE_FILTER_NONE, CACHE_FILTER_CURRENT_NETWORK, CACHE_FILTER_SCAN_RESULTS}) + @IntDef({SCORE_FILTER_NONE, SCORE_FILTER_CURRENT_NETWORK, SCORE_FILTER_SCAN_RESULTS}) @Retention(RetentionPolicy.SOURCE) - public @interface CacheUpdateFilter {} + public @interface ScoreUpdateFilter {} /** - * Do not filter updates sent to the cache. - * @hide + * Do not filter updates sent to the {@link NetworkScoreCallback}]. */ - public static final int CACHE_FILTER_NONE = 0; + public static final int SCORE_FILTER_NONE = 0; /** - * Only send cache updates when the network matches the connected network. - * @hide + * Only send updates to the {@link NetworkScoreCallback} when the network matches the connected + * network. */ - public static final int CACHE_FILTER_CURRENT_NETWORK = 1; + public static final int SCORE_FILTER_CURRENT_NETWORK = 1; /** - * Only send cache updates when the network is part of the current scan result set. - * @hide + * Only send updates to the {@link NetworkScoreCallback} when the network is part of the + * current scan result set. */ - public static final int CACHE_FILTER_SCAN_RESULTS = 2; + public static final int SCORE_FILTER_SCAN_RESULTS = 2; /** @hide */ @IntDef({RECOMMENDATIONS_ENABLED_FORCED_OFF, RECOMMENDATIONS_ENABLED_OFF, @@ -404,13 +403,13 @@ public class NetworkScoreManager { * @throws SecurityException if the caller does not hold the * {@link permission#REQUEST_NETWORK_SCORES} permission. * @throws IllegalArgumentException if a score cache is already registered for this type. - * @deprecated equivalent to registering for cache updates with CACHE_FILTER_NONE. + * @deprecated equivalent to registering for cache updates with {@link #SCORE_FILTER_NONE}. * @hide */ @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) @Deprecated // migrate to registerNetworkScoreCache(int, INetworkScoreCache, int) public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) { - registerNetworkScoreCache(networkType, scoreCache, CACHE_FILTER_NONE); + registerNetworkScoreCache(networkType, scoreCache, SCORE_FILTER_NONE); } /** @@ -418,7 +417,7 @@ public class NetworkScoreManager { * * @param networkType the type of network this cache can handle. See {@link NetworkKey#type} * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores - * @param filterType the {@link CacheUpdateFilter} to apply + * @param filterType the {@link ScoreUpdateFilter} to apply * @throws SecurityException if the caller does not hold the * {@link permission#REQUEST_NETWORK_SCORES} permission. * @throws IllegalArgumentException if a score cache is already registered for this type. @@ -426,7 +425,7 @@ public class NetworkScoreManager { */ @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache, - @CacheUpdateFilter int filterType) { + @ScoreUpdateFilter int filterType) { try { mService.registerNetworkScoreCache(networkType, scoreCache, filterType); } catch (RemoteException e) { @@ -510,7 +509,7 @@ public class NetworkScoreManager { * Register a network score callback. * * @param networkType the type of network this cache can handle. See {@link NetworkKey#type} - * @param filterType the {@link CacheUpdateFilter} to apply + * @param filterType the {@link ScoreUpdateFilter} to apply * @param callback implementation of {@link NetworkScoreCallback} that will be invoked when the * scores change. * @param executor The executor on which to execute the callbacks. @@ -522,7 +521,7 @@ public class NetworkScoreManager { @SystemApi @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES) public void registerNetworkScoreCallback(@NetworkKey.NetworkType int networkType, - @CacheUpdateFilter int filterType, + @ScoreUpdateFilter int filterType, @NonNull @CallbackExecutor Executor executor, @NonNull NetworkScoreCallback callback) throws SecurityException { if (callback == null || executor == null) { diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java index fb224fbe1318..fc9a8f63c131 100644 --- a/core/java/android/net/SocketKeepalive.java +++ b/core/java/android/net/SocketKeepalive.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -53,7 +54,11 @@ import java.util.concurrent.Executor; public abstract class SocketKeepalive implements AutoCloseable { static final String TAG = "SocketKeepalive"; - /** @hide */ + /** + * No errors. + * @hide + */ + @SystemApi public static final int SUCCESS = 0; /** @hide */ diff --git a/core/java/android/net/TelephonyNetworkSpecifier.java b/core/java/android/net/TelephonyNetworkSpecifier.java new file mode 100644 index 000000000000..726f77059707 --- /dev/null +++ b/core/java/android/net/TelephonyNetworkSpecifier.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.net; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * NetworkSpecifier object for cellular network request. Apps should use the + * {@link TelephonyNetworkSpecifier.Builder} class to create an instance. + */ +public final class TelephonyNetworkSpecifier extends NetworkSpecifier implements Parcelable { + + private final int mSubId; + + /** + * Return the subscription Id of current TelephonyNetworkSpecifier object. + * + * @return The subscription id. + */ + public int getSubscriptionId() { + return mSubId; + } + + /** + * @hide + */ + public TelephonyNetworkSpecifier(int subId) { + this.mSubId = subId; + } + + public static final @NonNull Creator<TelephonyNetworkSpecifier> CREATOR = + new Creator<TelephonyNetworkSpecifier>() { + @Override + public TelephonyNetworkSpecifier createFromParcel(Parcel in) { + int subId = in.readInt(); + return new TelephonyNetworkSpecifier(subId); + } + + @Override + public TelephonyNetworkSpecifier[] newArray(int size) { + return new TelephonyNetworkSpecifier[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mSubId); + } + + @Override + public int hashCode() { + return mSubId; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof TelephonyNetworkSpecifier)) { + return false; + } + + TelephonyNetworkSpecifier lhs = (TelephonyNetworkSpecifier) obj; + return mSubId == lhs.mSubId; + } + + @Override + public String toString() { + return new StringBuilder() + .append("TelephonyNetworkSpecifier [") + .append("mSubId = ").append(mSubId) + .append("]") + .toString(); + } + + /** @hide */ + @Override + public boolean satisfiedBy(NetworkSpecifier other) { + // Any generic requests should be satisfied by a specific telephony network. + // For simplicity, we treat null same as MatchAllNetworkSpecifier + return equals(other) || other == null || other instanceof MatchAllNetworkSpecifier; + } + + + /** + * Builder to create {@link TelephonyNetworkSpecifier} object. + */ + public static final class Builder { + // Integer.MIN_VALUE which is not a valid subId, services as the sentinel to check if + // subId was set + private static final int SENTINEL_SUB_ID = Integer.MIN_VALUE; + + private int mSubId; + + public Builder() { + mSubId = SENTINEL_SUB_ID; + } + + /** + * Set the subscription id. + * + * @param subId The subscription Id. + * @return Instance of {@link Builder} to enable the chaining of the builder method. + */ + public @NonNull Builder setSubscriptionId(int subId) { + mSubId = subId; + return this; + } + + /** + * Create a NetworkSpecifier for the cellular network request. + * + * @return TelephonyNetworkSpecifier object. + * @throws IllegalArgumentException when subscription Id is not provided through + * {@link #setSubscriptionId(int)}. + */ + public @NonNull TelephonyNetworkSpecifier build() { + if (mSubId == SENTINEL_SUB_ID) { + throw new IllegalArgumentException("Subscription Id is not provided."); + } + return new TelephonyNetworkSpecifier(mSubId); + } + } +} diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index 535bf675cd0e..64f20b839a63 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -49,8 +49,8 @@ import java.util.concurrent.CountDownLatch; * limited to a local network over Multicast DNS. DNS service discovery is described at * http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt * - * <p> The API is asynchronous and responses to requests from an application are on listener - * callbacks on a seperate internal thread. + * <p> The API is asynchronous, and responses to requests from an application are on listener + * callbacks on a separate internal thread. * * <p> There are three main operations the API supports - registration, discovery and resolution. * <pre> diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 6750fc77e705..eb67492483f1 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -245,7 +245,8 @@ public class Build { public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", ""); /** - * The user-visible security patch level. + * The user-visible security patch level. This value represents the date when the device + * most recently applied a security patch. */ public static final String SECURITY_PATCH = SystemProperties.get( "ro.build.version.security_patch", ""); diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java index 4628910bfd52..ca303d973235 100644 --- a/core/java/android/os/FileObserver.java +++ b/core/java/android/os/FileObserver.java @@ -32,7 +32,7 @@ import java.util.List; /** * Monitors files (using <a href="http://en.wikipedia.org/wiki/Inotify">inotify</a>) - * to fire an event after files are accessed or changed by by any process on + * to fire an event after files are accessed or changed by any process on * the device (including this one). FileObserver is an abstract class; * subclasses must implement the event handler {@link #onEvent(int, String)}. * diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index e3f9326048d1..36ea1bcc91d9 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -224,7 +224,7 @@ interface IStatsManager { * Logs an event for watchdog rollbacks. */ oneway void sendWatchdogRollbackOccurredAtom(in int rollbackType, in String packageName, - in long packageVersionCode); + in long packageVersionCode, in int rollbackReason, in String failingPackageName); /** * Returns the most recently registered experiment IDs. diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 63641e538b8e..c30491a3965c 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -41,6 +41,7 @@ interface IUserManager { */ UserInfo createUser(in String name, int flags); + UserInfo preCreateUser(int flags); UserInfo createProfileForUser(in String name, int flags, int userHandle, in String[] disallowedPackages); UserInfo createRestrictedProfile(String name, int parentUserHandle); @@ -53,7 +54,7 @@ interface IUserManager { void setUserIcon(int userHandle, in Bitmap icon); ParcelFileDescriptor getUserIcon(int userHandle); UserInfo getPrimaryUser(); - List<UserInfo> getUsers(boolean excludeDying); + List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated); List<UserInfo> getProfiles(int userHandle, boolean enabledOnly); int[] getProfileIds(int userId, boolean enabledOnly); boolean canAddMoreManagedProfiles(int userHandle, boolean allowedToRemoveOne); @@ -92,6 +93,7 @@ interface IUserManager { boolean someUserHasSeedAccount(in String accountName, in String accountType); boolean isManagedProfile(int userId); boolean isDemoUser(int userId); + boolean isPreCreated(int userId); UserInfo createProfileForUserEvenWhenDisallowed(in String name, int flags, int userHandle, in String[] disallowedPackages); boolean isUserUnlockingOrUnlocked(int userId); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index febc36cbe47b..6b56401884f0 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -45,70 +45,10 @@ import java.util.concurrent.Executor; * <p> * <b>Device battery life will be significantly affected by the use of this API.</b> * Do not acquire {@link WakeLock}s unless you really need them, use the minimum levels - * possible, and be sure to release them as soon as possible. - * </p><p> - * The primary API you'll use is {@link #newWakeLock(int, String) newWakeLock()}. - * This will create a {@link PowerManager.WakeLock} object. You can then use methods - * on the wake lock object to control the power state of the device. - * </p><p> - * In practice it's quite simple: - * {@samplecode - * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - * PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag"); - * wl.acquire(); - * ..screen will stay on during this section.. - * wl.release(); - * } - * </p><p> - * The following wake lock levels are defined, with varying effects on system power. - * <i>These levels are mutually exclusive - you may only specify one of them.</i> + * possible, and be sure to release them as soon as possible. In most cases, + * you'll want to use + * {@link android.view.WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON} instead. * - * <table> - * <tr><th>Flag Value</th> - * <th>CPU</th> <th>Screen</th> <th>Keyboard</th></tr> - * - * <tr><td>{@link #PARTIAL_WAKE_LOCK}</td> - * <td>On*</td> <td>Off</td> <td>Off</td> - * </tr> - * - * <tr><td>{@link #SCREEN_DIM_WAKE_LOCK}</td> - * <td>On</td> <td>Dim</td> <td>Off</td> - * </tr> - * - * <tr><td>{@link #SCREEN_BRIGHT_WAKE_LOCK}</td> - * <td>On</td> <td>Bright</td> <td>Off</td> - * </tr> - * - * <tr><td>{@link #FULL_WAKE_LOCK}</td> - * <td>On</td> <td>Bright</td> <td>Bright</td> - * </tr> - * </table> - * </p><p> - * *<i>If you hold a partial wake lock, the CPU will continue to run, regardless of any - * display timeouts or the state of the screen and even after the user presses the power button. - * In all other wake locks, the CPU will run, but the user can still put the device to sleep - * using the power button.</i> - * </p><p> - * In addition, you can add two more flags, which affect behavior of the screen only. - * <i>These flags have no effect when combined with a {@link #PARTIAL_WAKE_LOCK}.</i></p> - * - * <table> - * <tr><th>Flag Value</th> <th>Description</th></tr> - * - * <tr><td>{@link #ACQUIRE_CAUSES_WAKEUP}</td> - * <td>Normal wake locks don't actually turn on the illumination. Instead, they cause - * the illumination to remain on once it turns on (e.g. from user activity). This flag - * will force the screen and/or keyboard to turn on immediately, when the WakeLock is - * acquired. A typical use would be for notifications which are important for the user to - * see immediately.</td> - * </tr> - * - * <tr><td>{@link #ON_AFTER_RELEASE}</td> - * <td>If this flag is set, the user activity timer will be reset when the WakeLock is - * released, causing the illumination to remain on a bit longer. This can be used to - * reduce flicker if you are cycling between wake lock conditions.</td> - * </tr> - * </table> * <p> * Any application using a WakeLock must request the {@code android.permission.WAKE_LOCK} * permission in an {@code <uses-permission>} element of the application's manifest. @@ -923,7 +863,8 @@ public final class PowerManager { * {@link #FULL_WAKE_LOCK}, {@link #SCREEN_DIM_WAKE_LOCK} * and {@link #SCREEN_BRIGHT_WAKE_LOCK}. Exactly one wake lock level must be * specified as part of the {@code levelAndFlags} parameter. - * </p><p> + * </p> + * <p> * The wake lock flags are: {@link #ACQUIRE_CAUSES_WAKEUP} * and {@link #ON_AFTER_RELEASE}. Multiple flags can be combined as part of the * {@code levelAndFlags} parameters. diff --git a/core/java/android/os/TimestampedValue.java b/core/java/android/os/TimestampedValue.java index 348574ed43c7..f4c87ac9dfc9 100644 --- a/core/java/android/os/TimestampedValue.java +++ b/core/java/android/os/TimestampedValue.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import java.util.Objects; @@ -35,19 +36,27 @@ import java.util.Objects; * @param <T> the type of the value with an associated timestamp * @hide */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public final class TimestampedValue<T> implements Parcelable { private final long mReferenceTimeMillis; + @Nullable private final T mValue; - public TimestampedValue(long referenceTimeMillis, T value) { + public TimestampedValue(long referenceTimeMillis, @Nullable T value) { mReferenceTimeMillis = referenceTimeMillis; mValue = value; } + /** Returns the reference time value. See {@link TimestampedValue} for more information. */ public long getReferenceTimeMillis() { return mReferenceTimeMillis; } + /** + * Returns the value associated with the timestamp. See {@link TimestampedValue} for more + * information. + */ + @Nullable public T getValue() { return mValue; } @@ -86,6 +95,8 @@ public final class TimestampedValue<T> implements Parcelable { return one.mReferenceTimeMillis - two.mReferenceTimeMillis; } + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final @NonNull Parcelable.Creator<TimestampedValue<?>> CREATOR = new Parcelable.ClassLoaderCreator<TimestampedValue<?>>() { diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index a507e2576df9..0dfb7c380b04 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -37,6 +37,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.UserInfo; +import android.content.pm.UserInfo.UserInfoFlag; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; @@ -2006,18 +2007,20 @@ public class UserManager { /** * Creates a user with the specified name and options. For non-admin users, default user - * restrictions are going to be applied. - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. + * restrictions will be applied. + * + * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * * @param name the user's name - * @param flags flags that identify the type of user and other properties. + * @param flags UserInfo flags that identify the type of user and other properties. * @see UserInfo * - * @return the UserInfo object for the created user, or null if the user could not be created. + * @return the UserInfo object for the created user, or {@code null} if the user could not be + * created. * @hide */ @UnsupportedAppUsage - public UserInfo createUser(String name, int flags) { + public @Nullable UserInfo createUser(@Nullable String name, @UserInfoFlag int flags) { UserInfo user = null; try { user = mService.createUser(name, flags); @@ -2034,6 +2037,44 @@ public class UserManager { } /** + * Pre-creates a user with the specified name and options. For non-admin users, default user + * restrictions will be applied. + * + * <p>This method can be used by OEMs to "warm" up the user creation by pre-creating some users + * at the first boot, so they when the "real" user is created (for example, + * by {@link #createUser(String, int)} or {@link #createGuest(Context, String)}), it takes + * less time. + * + * <p>This method completes the majority of work necessary for user creation: it + * creates user data, CE and DE encryption keys, app data directories, initializes the user and + * grants default permissions. When pre-created users become "real" users, only then are + * components notified of new user creation by firing user creation broadcasts. + * + * <p>All pre-created users are removed during system upgrade. + * + * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} permission. + * + * @param flags UserInfo flags that identify the type of user and other properties. + * @see UserInfo + * + * @return the UserInfo object for the created user, or {@code null} if the user could not be + * created. + * + * @throw {@link IllegalArgumentException} if {@code flags} contains + * {@link UserInfo#FLAG_MANAGED_PROFILE}. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + public @Nullable UserInfo preCreateUser(@UserInfoFlag int flags) { + try { + return mService.preCreateUser(flags); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Creates a guest user and configures it. * @param context an application context * @param name the name to set for the user @@ -2340,6 +2381,8 @@ public class UserManager { /** * Return the number of users currently created on the device. + * <p>This API is not for use by third-party apps. It requires the {@code MANAGE_USERS} + * permission.</p> */ public int getUserCount() { List<UserInfo> users = getUsers(); @@ -2349,15 +2392,26 @@ public class UserManager { /** * Returns information for all users on this device, including ones marked for deletion. * To retrieve only users that are alive, use {@link #getUsers(boolean)}. - * <p> - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. + * * @return the list of users that exist on the device. * @hide */ @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public List<UserInfo> getUsers() { + return getUsers(/* excludeDying= */ false); + } + + /** + * Returns information for all users on this device, based on the filtering parameters. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + public List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, + boolean excludePreCreated) { try { - return mService.getUsers(false); + return mService.getUsers(excludePartial, excludeDying, excludePreCreated); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2373,16 +2427,12 @@ public class UserManager { @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean excludeDying) { - try { - List<UserInfo> users = mService.getUsers(excludeDying); - long[] result = new long[users.size()]; - for (int i = 0; i < result.length; i++) { - result[i] = users.get(i).serialNumber; - } - return result; - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); + List<UserInfo> users = getUsers(excludeDying); + long[] result = new long[users.size()]; + for (int i = 0; i < result.length; i++) { + result[i] = users.get(i).serialNumber; } + return result; } /** @@ -2768,11 +2818,8 @@ public class UserManager { */ @UnsupportedAppUsage public @NonNull List<UserInfo> getUsers(boolean excludeDying) { - try { - return mService.getUsers(excludeDying); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } + return getUsers(/*excludePartial= */ true, excludeDying, + /* excludePreCreated= */ true); } /** diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java index 921f0f2ab1e2..5cb33615fe22 100644 --- a/core/java/android/os/image/DynamicSystemClient.java +++ b/core/java/android/os/image/DynamicSystemClient.java @@ -256,9 +256,13 @@ public class DynamicSystemClient { mService.send(msg); } catch (RemoteException e) { Slog.e(TAG, "Unable to get status from installation service"); - mExecutor.execute(() -> { + if (mExecutor != null) { + mExecutor.execute(() -> { + mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e); + }); + } else { mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e); - }); + } } } diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java index 4c92c28c1bfa..cbf531c5730a 100644 --- a/core/java/android/os/image/DynamicSystemManager.java +++ b/core/java/android/os/image/DynamicSystemManager.java @@ -106,9 +106,9 @@ public class DynamicSystemManager { * @return true if the call succeeds */ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM) - public boolean startInstallation() { + public boolean startInstallation(String dsuSlot) { try { - return mService.startInstallation(); + return mService.startInstallation(dsuSlot); } catch (RemoteException e) { throw new RuntimeException(e.toString()); } diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl index 69cbab2c68ad..cc32f998d0c2 100644 --- a/core/java/android/os/image/IDynamicSystemService.aidl +++ b/core/java/android/os/image/IDynamicSystemService.aidl @@ -22,9 +22,10 @@ interface IDynamicSystemService { /** * Start DynamicSystem installation. + * @param dsuSlot Name used to identify this installation * @return true if the call succeeds */ - boolean startInstallation(); + boolean startInstallation(@utf8InCpp String dsuSlot); /** * Create a DSU partition. This call may take 60~90 seconds. The caller diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1f3ae09e9216..cf697f7dbdc6 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -662,6 +662,22 @@ public final class Settings { "android.settings.NIGHT_DISPLAY_SETTINGS"; /** + * Activity Action: Show settings to allow configuration of Dark theme. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_DARK_THEME_SETTINGS = + "android.settings.DARK_THEME_SETTINGS"; + + /** * Activity Action: Show settings to allow configuration of locale. * <p> * In some cases, a matching Activity may not exist, so ensure you @@ -7859,6 +7875,19 @@ public final class Settings { NON_NEGATIVE_INTEGER_VALIDATOR; /** + * Number of successful "Motion Sense" tap gestures to pause media. + * @hide + */ + public static final String AWARE_TAP_PAUSE_GESTURE_COUNT = "aware_tap_pause_gesture_count"; + + /** + * Number of touch interactions to pause media when a "Motion Sense" gesture could + * have been used. + * @hide + */ + public static final String AWARE_TAP_PAUSE_TOUCH_COUNT = "aware_tap_pause_touch_count"; + + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per * UiModeManager. @@ -8939,6 +8968,14 @@ public final class Settings { new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"}); /** + * Current provider of proximity-based sharing services. + * Default value in @string/config_defaultNearbySharingComponent. + * No VALIDATOR as this setting will not be backed up. + * @hide + */ + public static final String NEARBY_SHARING_COMPONENT = "nearby_sharing_component"; + + /** * Controls whether aware is enabled. * @hide */ @@ -8955,6 +8992,14 @@ public final class Settings { private static final Validator AWARE_LOCK_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR; /** + * Controls whether tap gesture is enabled. + * @hide + */ + public static final String TAP_GESTURE = "tap_gesture"; + + private static final Validator TAP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; + + /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear @@ -9036,8 +9081,6 @@ public final class Settings { DOZE_PICK_UP_GESTURE, DOZE_DOUBLE_TAP_GESTURE, DOZE_TAP_SCREEN_GESTURE, - DOZE_WAKE_LOCK_SCREEN_GESTURE, - DOZE_WAKE_DISPLAY_GESTURE, NFC_PAYMENT_DEFAULT_COMPONENT, AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, FACE_UNLOCK_KEYGUARD_ENABLED, @@ -9045,9 +9088,6 @@ public final class Settings { FACE_UNLOCK_DISMISSES_KEYGUARD, FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, - ASSIST_GESTURE_ENABLED, - ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, - ASSIST_GESTURE_WAKE_ENABLED, VR_DISPLAY_MODE, NOTIFICATION_BADGING, NOTIFICATION_DISMISS_RTL, @@ -9080,12 +9120,9 @@ public final class Settings { TRUST_AGENTS_EXTEND_UNLOCK, UI_NIGHT_MODE, LOCK_SCREEN_WHEN_TRUST_LOST, - SKIP_GESTURE, SKIP_DIRECTION, - SILENCE_GESTURE, THEME_CUSTOMIZATION_OVERLAY_PACKAGES, NAVIGATION_MODE, - AWARE_ENABLED, SKIP_GESTURE_COUNT, SKIP_TOUCH_COUNT, SILENCE_ALARMS_GESTURE_COUNT, @@ -9096,7 +9133,9 @@ public final class Settings { SILENCE_TIMER_TOUCH_COUNT, DARK_MODE_DIALOG_SEEN, GLOBAL_ACTIONS_PANEL_ENABLED, - AWARE_LOCK_ENABLED + AWARE_LOCK_ENABLED, + AWARE_TAP_PAUSE_GESTURE_COUNT, + AWARE_TAP_PAUSE_TOUCH_COUNT }; /** @@ -9291,6 +9330,9 @@ public final class Settings { VALIDATORS.put(UI_NIGHT_MODE, UI_NIGHT_MODE_VALIDATOR); VALIDATORS.put(GLOBAL_ACTIONS_PANEL_ENABLED, GLOBAL_ACTIONS_PANEL_ENABLED_VALIDATOR); VALIDATORS.put(AWARE_LOCK_ENABLED, AWARE_LOCK_ENABLED_VALIDATOR); + VALIDATORS.put(AWARE_TAP_PAUSE_GESTURE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(AWARE_TAP_PAUSE_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(TAP_GESTURE, TAP_GESTURE_VALIDATOR); } /** @@ -9864,14 +9906,35 @@ public final class Settings { * List of ISO country codes in which eUICC UI is shown. Country codes should be separated * by comma. * - * <p>Used to hide eUICC UI from users who are currently in countries no carriers support - * eUICC. + * Note: if {@link #EUICC_SUPPORTED_COUNTRIES} is empty, then {@link + * #EUICC_UNSUPPORTED_COUNTRIES} is used. + * + * <p>Used to hide eUICC UI from users who are currently in countries where no carriers + * support eUICC. + * * @hide */ //TODO(b/77914569) Changes this to System Api. public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries"; /** + * List of ISO country codes in which eUICC UI is not shown. Country codes should be + * separated by comma. + * + * Note: if {@link #EUICC_SUPPORTED_COUNTRIES} is empty, then {@link + * #EUICC_UNSUPPORTED_COUNTRIES} is used. + * + * <p>Used to hide eUICC UI from users who are currently in countries where no carriers + * support eUICC. + * + * @hide + */ + //TODO(b/77914569) Changes this to System Api. + public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries"; + private static final Validator EUICC_UNSUPPORTED_COUNTRIES_VALIDATOR = + new SettingsValidators.ComponentNameListValidator(","); + + /** * Whether any activity can be resized. When this is true, any * activity, regardless of manifest values, can be resized for multi-window. * (0 = false, 1 = true) @@ -13840,6 +13903,7 @@ public final class Settings { VALIDATORS.put(DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, DYNAMIC_POWER_SAVINGS_VALIDATOR); VALIDATORS.put(BLUETOOTH_ON, BLUETOOTH_ON_VALIDATOR); + VALIDATORS.put(EUICC_UNSUPPORTED_COUNTRIES, EUICC_UNSUPPORTED_COUNTRIES_VALIDATOR); VALIDATORS.put(PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_VALIDATOR); VALIDATORS.put(PRIVATE_DNS_SPECIFIER, PRIVATE_DNS_SPECIFIER_VALIDATOR); VALIDATORS.put(SOFT_AP_TIMEOUT_ENABLED, SOFT_AP_TIMEOUT_ENABLED_VALIDATOR); diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 28d31e1bf103..90ac821363b2 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -35,6 +35,7 @@ import android.database.Cursor; import android.database.sqlite.SqliteWrapper; import android.net.Uri; import android.os.Build; +import android.os.Bundle; import android.os.Parcel; import android.telephony.Rlog; import android.telephony.ServiceState; @@ -4058,6 +4059,108 @@ public final class Telephony { public static final Uri MESSAGE_HISTORY_URI = Uri.parse("content://cellbroadcasts/history"); /** + * The authority for the legacy cellbroadcast provider. + * This is used for OEM data migration. OEMs want to migrate message history or + * sharepreference data to mainlined cellbroadcastreceiver app, should have a + * contentprovider with authority: cellbroadcast-legacy. Mainlined cellbroadcastreceiver + * will interact with this URI to retrieve data and persists to mainlined cellbroadcast app. + * + * @hide + */ + @SystemApi + public static final @NonNull String AUTHORITY_LEGACY = "cellbroadcast-legacy"; + + /** + * A content:// style uri to the authority for the legacy cellbroadcast provider. + * @hide + */ + @SystemApi + public static final @NonNull Uri AUTHORITY_LEGACY_URI = + Uri.parse("content://cellbroadcast-legacy"); + + /** + * Method name to {@link android.content.ContentProvider#call(String, String, Bundle) + * for {@link #AUTHORITY_LEGACY}. Used to query cellbroadcast {@link Preference}, + * containing following supported entries + * <ul> + * <li>{@link #ENABLE_AREA_UPDATE_INFO_PREF}</li> + * <li>{@link #ENABLE_TEST_ALERT_PREF}</li> + * <li>{@link #ENABLE_STATE_LOCAL_TEST_PREF}</li> + * <li>{@link #ENABLE_PUBLIC_SAFETY_PREF}</li> + * <li>{@link #ENABLE_CMAS_AMBER_PREF}</li> + * <li>{@link #ENABLE_CMAS_SEVERE_THREAT_PREF}</li> + * <li>{@link #ENABLE_CMAS_EXTREME_THREAT_PREF}</li> + * <li>{@link #ENABLE_CMAS_PRESIDENTIAL_PREF}</li> + * <li>{@link #ENABLE_ALERT_VIBRATION_PREF}</li> + * <li>{@link #ENABLE_EMERGENCY_PERF}</li> + * <li>{@link #ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF}</li> + * </ul> + * @hide + */ + @SystemApi + public static final @NonNull String CALL_METHOD_GET_PREFERENCE = "get_preference"; + + /** + * Arg name to {@link android.content.ContentProvider#call(String, String, Bundle)} + * for {@link #AUTHORITY_LEGACY}. + * Contains all supported shared preferences for cellbroadcast. + * + * @hide + */ + @SystemApi + public static final class Preference { + /** + * Not Instantiatable. + * @hide + */ + private Preference() {} + + /** Preference to enable area update info alert */ + public static final @NonNull String ENABLE_AREA_UPDATE_INFO_PREF = + "enable_area_update_info_alerts"; + + /** Preference to enable test alert */ + public static final @NonNull String ENABLE_TEST_ALERT_PREF = + "enable_test_alerts"; + + /** Preference to enable state local test alert */ + public static final @NonNull String ENABLE_STATE_LOCAL_TEST_PREF + = "enable_state_local_test_alerts"; + + /** Preference to enable public safety alert */ + public static final @NonNull String ENABLE_PUBLIC_SAFETY_PREF + = "enable_public_safety_messages"; + + /** Preference to enable amber alert */ + public static final @NonNull String ENABLE_CMAS_AMBER_PREF + = "enable_cmas_amber_alerts"; + + /** Preference to enable severe threat alert */ + public static final @NonNull String ENABLE_CMAS_SEVERE_THREAT_PREF + = "enable_cmas_severe_threat_alerts"; + + /** Preference to enable extreme threat alert */ + public static final @NonNull String ENABLE_CMAS_EXTREME_THREAT_PREF = + "enable_cmas_extreme_threat_alerts"; + + /** Preference to enable presidential alert */ + public static final @NonNull String ENABLE_CMAS_PRESIDENTIAL_PREF = + "enable_cmas_presidential_alerts"; + + /** Preference to enable alert vibration */ + public static final @NonNull String ENABLE_ALERT_VIBRATION_PREF = + "enable_alert_vibrate"; + + /** Preference to enable emergency alert */ + public static final @NonNull String ENABLE_EMERGENCY_PERF = + "enable_emergency_alerts"; + + /** Preference to enable receive alerts in second language */ + public static final @NonNull String ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF = + "receive_cmas_in_second_language"; + } + + /** * The subscription which received this cell broadcast message. * <P>Type: INTEGER</P> */ diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index 91f77ea0462d..63a38ddc2b1d 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -71,12 +71,21 @@ public final class FillRequest implements Parcelable { */ public static final int FLAG_COMPATIBILITY_MODE_REQUEST = 0x2; + /** + * Indicates the request came from a password field. + * + * (TODO: b/141703197) Temporary fix for augmented autofill showing passwords. + * + * @hide + */ + public static final @RequestFlags int FLAG_PASSWORD_INPUT_TYPE = 0x4; + /** @hide */ public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE; /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = { - FLAG_MANUAL_REQUEST, FLAG_COMPATIBILITY_MODE_REQUEST + FLAG_MANUAL_REQUEST, FLAG_COMPATIBILITY_MODE_REQUEST, FLAG_PASSWORD_INPUT_TYPE }) @Retention(RetentionPolicy.SOURCE) @interface RequestFlags{} @@ -100,7 +109,7 @@ public final class FillRequest implements Parcelable { @Nullable Bundle clientState, @RequestFlags int flags) { mId = id; mFlags = Preconditions.checkFlagsArgument(flags, - FLAG_MANUAL_REQUEST | FLAG_COMPATIBILITY_MODE_REQUEST); + FLAG_MANUAL_REQUEST | FLAG_COMPATIBILITY_MODE_REQUEST | FLAG_PASSWORD_INPUT_TYPE); mContexts = Preconditions.checkCollectionElementsNotNull(contexts, "contexts"); mClientState = clientState; } diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java index 6a29d485b997..5d003706ac83 100644 --- a/core/java/android/service/autofill/augmented/FillWindow.java +++ b/core/java/android/service/autofill/augmented/FillWindow.java @@ -242,6 +242,7 @@ public final class FillWindow implements AutoCloseable { synchronized (mLock) { if (mDestroyed) return; if (mUpdateCalled) { + mFillView.setOnClickListener(null); hide(); mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED); } diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java index efc8e877f3c3..306b4830932e 100644 --- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java +++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java @@ -64,14 +64,23 @@ public abstract class ContentSuggestionsService extends Service { @Override public void provideContextImage(int taskId, GraphicBuffer contextImage, int colorSpaceId, Bundle imageContextRequestExtras) { + if (imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP) + && contextImage != null) { + throw new IllegalArgumentException("Two bitmaps provided; expected one."); + } Bitmap wrappedBuffer = null; - if (contextImage != null) { - ColorSpace colorSpace = null; - if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) { - colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]); + if (imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) { + wrappedBuffer = imageContextRequestExtras.getParcelable( + ContentSuggestionsManager.EXTRA_BITMAP); + } else { + if (contextImage != null) { + ColorSpace colorSpace = null; + if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) { + colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]); + } + wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace); } - wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace); } mHandler.sendMessage( diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 5da34a343513..377f29acd179 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1514,6 +1514,7 @@ public abstract class NotificationListenerService extends Service { private ArrayList<Notification.Action> mSmartActions; private ArrayList<CharSequence> mSmartReplies; private boolean mCanBubble; + private boolean mVisuallyInterruptive; private static final int PARCEL_VERSION = 2; @@ -1545,6 +1546,7 @@ public abstract class NotificationListenerService extends Service { out.writeTypedList(mSmartActions, flags); out.writeCharSequenceList(mSmartReplies); out.writeBoolean(mCanBubble); + out.writeBoolean(mVisuallyInterruptive); } /** @hide */ @@ -1577,6 +1579,7 @@ public abstract class NotificationListenerService extends Service { mSmartActions = in.createTypedArrayList(Notification.Action.CREATOR); mSmartReplies = in.readCharSequenceList(); mCanBubble = in.readBoolean(); + mVisuallyInterruptive = in.readBoolean(); } @@ -1764,6 +1767,11 @@ public abstract class NotificationListenerService extends Service { } /** @hide */ + public boolean visuallyInterruptive() { + return mVisuallyInterruptive; + } + + /** @hide */ public boolean isNoisy() { return mNoisy; } @@ -1779,7 +1787,8 @@ public abstract class NotificationListenerService extends Service { ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge, int userSentiment, boolean hidden, long lastAudiblyAlertedMs, boolean noisy, ArrayList<Notification.Action> smartActions, - ArrayList<CharSequence> smartReplies, boolean canBubble) { + ArrayList<CharSequence> smartReplies, boolean canBubble, + boolean visuallyInterruptive) { mKey = key; mRank = rank; mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW; @@ -1800,6 +1809,7 @@ public abstract class NotificationListenerService extends Service { mSmartActions = smartActions; mSmartReplies = smartReplies; mCanBubble = canBubble; + mVisuallyInterruptive = visuallyInterruptive; } /** @@ -1824,7 +1834,8 @@ public abstract class NotificationListenerService extends Service { other.mNoisy, other.mSmartActions, other.mSmartReplies, - other.mCanBubble); + other.mCanBubble, + other.mVisuallyInterruptive); } /** @@ -1876,7 +1887,8 @@ public abstract class NotificationListenerService extends Service { && ((mSmartActions == null ? 0 : mSmartActions.size()) == (other.mSmartActions == null ? 0 : other.mSmartActions.size())) && Objects.equals(mSmartReplies, other.mSmartReplies) - && Objects.equals(mCanBubble, other.mCanBubble); + && Objects.equals(mCanBubble, other.mCanBubble) + && Objects.equals(mVisuallyInterruptive, other.mVisuallyInterruptive); } } diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java new file mode 100644 index 000000000000..ada59d6d7d55 --- /dev/null +++ b/core/java/android/timezone/CountryTimeZones.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.timezone; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.icu.util.TimeZone; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Information about a country's time zones. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class CountryTimeZones { + + /** + * A mapping to a time zone ID with some associated metadata. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final class TimeZoneMapping { + + private libcore.timezone.CountryTimeZones.TimeZoneMapping mDelegate; + + TimeZoneMapping(libcore.timezone.CountryTimeZones.TimeZoneMapping delegate) { + this.mDelegate = Objects.requireNonNull(delegate); + } + + /** + * Returns the ID for this mapping. See also {@link #getTimeZone()} which handles when the + * ID is unrecognized. + */ + @NonNull + public String getTimeZoneId() { + return mDelegate.timeZoneId; + } + + /** + * Returns a {@link TimeZone} object for this mapping, or {@code null} if the ID is + * unrecognized. + */ + @Nullable + public TimeZone getTimeZone() { + return mDelegate.getTimeZone(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TimeZoneMapping that = (TimeZoneMapping) o; + return this.mDelegate.equals(that.mDelegate); + } + + @Override + public int hashCode() { + return this.mDelegate.hashCode(); + } + + @Override + public String toString() { + return mDelegate.toString(); + } + } + + /** + * The result of lookup up a time zone using offset information (and possibly more). + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static final class OffsetResult { + + private final TimeZone mTimeZone; + private final boolean mIsOnlyMatch; + + /** Creates an instance with the supplied information. */ + public OffsetResult(@NonNull TimeZone timeZone, boolean isOnlyMatch) { + mTimeZone = Objects.requireNonNull(timeZone); + mIsOnlyMatch = isOnlyMatch; + } + + /** + * Returns a time zone that matches the supplied criteria. + */ + @NonNull + public TimeZone getTimeZone() { + return mTimeZone; + } + + /** + * Returns {@code true} if there is only one matching time zone for the supplied criteria. + */ + public boolean isOnlyMatch() { + return mIsOnlyMatch; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OffsetResult that = (OffsetResult) o; + return mIsOnlyMatch == that.mIsOnlyMatch + && mTimeZone.getID().equals(that.mTimeZone.getID()); + } + + @Override + public int hashCode() { + return Objects.hash(mTimeZone, mIsOnlyMatch); + } + + @Override + public String toString() { + return "OffsetResult{" + + "mTimeZone=" + mTimeZone + + ", mIsOnlyMatch=" + mIsOnlyMatch + + '}'; + } + } + + @NonNull + private final libcore.timezone.CountryTimeZones mDelegate; + + CountryTimeZones(libcore.timezone.CountryTimeZones delegate) { + mDelegate = delegate; + } + + /** + * Returns true if the ISO code for the country is a match for the one specified. + */ + public boolean isForCountryCode(@NonNull String countryIso) { + return mDelegate.isForCountryCode(countryIso); + } + + /** + * Returns the default time zone ID for the country. Can return {@code null} in cases when no + * data is available or the time zone ID was not recognized. + */ + @Nullable + public String getDefaultTimeZoneId() { + return mDelegate.getDefaultTimeZoneId(); + } + + /** + * Returns the default time zone for the country. Can return {@code null} in cases when no data + * is available or the time zone ID was not recognized. + */ + @Nullable + public TimeZone getDefaultTimeZone() { + return mDelegate.getDefaultTimeZone(); + } + + /** + * Qualifier for a country's default time zone. {@code true} indicates whether the default + * would be a good choice <em>generally</em> when there's no other information available. + */ + public boolean isDefaultTimeZoneBoosted() { + return mDelegate.getDefaultTimeZoneBoost(); + } + + /** + * Returns true if the country has at least one zone that is the same as UTC at the given time. + */ + public boolean hasUtcZone(long whenMillis) { + return mDelegate.hasUtcZone(whenMillis); + } + + /** + * Returns a time zone for the country, if there is one, that matches the desired properties. If + * there are multiple matches and the {@code bias} is one of them then it is returned, otherwise + * an arbitrary match is returned based on the {@link #getEffectiveTimeZoneMappingsAt(long)} + * ordering. + * + * @param totalOffsetMillis the offset from UTC at {@code whenMillis} + * @param isDst the Daylight Savings Time state at {@code whenMillis}. {@code true} means DST, + * {@code false} means not DST, {@code null} means unknown + * @param dstOffsetMillis the part of {@code totalOffsetMillis} contributed by DST, only used if + * {@code isDst} is {@code true}. The value can be {@code null} if the DST offset is + * unknown + * @param whenMillis the UTC time to match against + * @param bias the time zone to prefer, can be {@code null} + */ + @Nullable + public OffsetResult lookupByOffsetWithBias(int totalOffsetMillis, @Nullable Boolean isDst, + @SuppressLint("AutoBoxing") @Nullable Integer dstOffsetMillis, long whenMillis, + @Nullable TimeZone bias) { + libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = + mDelegate.lookupByOffsetWithBias( + totalOffsetMillis, isDst, dstOffsetMillis, whenMillis, bias); + return delegateOffsetResult == null ? null : + new OffsetResult(delegateOffsetResult.mTimeZone, delegateOffsetResult.mOneMatch); + } + + /** + * Returns an immutable, ordered list of time zone mappings for the country in an undefined but + * "priority" order, filtered so that only "effective" time zone IDs are returned. An + * "effective" time zone is one that differs from another time zone used in the country after + * {@code whenMillis}. The list can be empty if there were no zones configured or the configured + * zone IDs were not recognized. + */ + @NonNull + public List<TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long whenMillis) { + List<libcore.timezone.CountryTimeZones.TimeZoneMapping> delegateList = + mDelegate.getEffectiveTimeZoneMappingsAt(whenMillis); + + List<TimeZoneMapping> toReturn = new ArrayList<>(delegateList.size()); + for (libcore.timezone.CountryTimeZones.TimeZoneMapping delegateMapping : delegateList) { + toReturn.add(new TimeZoneMapping(delegateMapping)); + } + return Collections.unmodifiableList(toReturn); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CountryTimeZones that = (CountryTimeZones) o; + return mDelegate.equals(that.mDelegate); + } + + @Override + public int hashCode() { + return Objects.hash(mDelegate); + } + + @Override + public String toString() { + return mDelegate.toString(); + } +} diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java new file mode 100644 index 000000000000..39dbe85cb485 --- /dev/null +++ b/core/java/android/timezone/TelephonyLookup.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.timezone; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +import com.android.internal.annotations.GuardedBy; + +import java.util.Objects; + +/** + * A class that can find time zone-related information about telephony networks. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public class TelephonyLookup { + + private static Object sLock = new Object(); + @GuardedBy("sLock") + private static TelephonyLookup sInstance; + + @NonNull + private final libcore.timezone.TelephonyLookup mDelegate; + + /** + * Obtains an instance for use when resolving telephony time zone information. This method never + * returns {@code null}. + */ + @NonNull + public static TelephonyLookup getInstance() { + synchronized (sLock) { + if (sInstance == null) { + sInstance = new TelephonyLookup(libcore.timezone.TelephonyLookup.getInstance()); + } + return sInstance; + } + } + + private TelephonyLookup(@NonNull libcore.timezone.TelephonyLookup delegate) { + mDelegate = Objects.requireNonNull(delegate); + } + + /** + * Returns an object capable of querying telephony network information. This method can return + * {@code null} in the event of an error while reading the underlying data files. + */ + @Nullable + public TelephonyNetworkFinder getTelephonyNetworkFinder() { + libcore.timezone.TelephonyNetworkFinder telephonyNetworkFinderDelegate = + mDelegate.getTelephonyNetworkFinder(); + return telephonyNetworkFinderDelegate != null + ? new TelephonyNetworkFinder(telephonyNetworkFinderDelegate) : null; + } +} diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java new file mode 100644 index 000000000000..ae39fbddfa1c --- /dev/null +++ b/core/java/android/timezone/TelephonyNetwork.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.timezone; + +import android.annotation.NonNull; +import android.annotation.SystemApi; + +import java.util.Objects; + +/** + * Information about a telephony network. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public class TelephonyNetwork { + + @NonNull + private final libcore.timezone.TelephonyNetwork mDelegate; + + TelephonyNetwork(@NonNull libcore.timezone.TelephonyNetwork delegate) { + mDelegate = Objects.requireNonNull(delegate); + } + + /** + * Returns the Mobile Country Code of the network. + */ + @NonNull + public String getMcc() { + return mDelegate.getMcc(); + } + + /** + * Returns the Mobile Network Code of the network. + */ + @NonNull + public String getMnc() { + return mDelegate.getMnc(); + } + + /** + * Returns the country in which the network operates as an ISO 3166 alpha-2 (lower case). + */ + @NonNull + public String getCountryIsoCode() { + return mDelegate.getCountryIsoCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TelephonyNetwork that = (TelephonyNetwork) o; + return mDelegate.equals(that.mDelegate); + } + + @Override + public int hashCode() { + return Objects.hash(mDelegate); + } + + @Override + public String toString() { + return "TelephonyNetwork{" + + "mDelegate=" + mDelegate + + '}'; + } +} diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java new file mode 100644 index 000000000000..a81a516c4b33 --- /dev/null +++ b/core/java/android/timezone/TelephonyNetworkFinder.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.timezone; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +import java.util.Objects; + +/** + * A class that can find telephony networks loaded via {@link TelephonyLookup}. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public class TelephonyNetworkFinder { + + @NonNull + private final libcore.timezone.TelephonyNetworkFinder mDelegate; + + TelephonyNetworkFinder(libcore.timezone.TelephonyNetworkFinder delegate) { + mDelegate = Objects.requireNonNull(delegate); + } + + /** + * Returns information held about a specific MCC + MNC combination. It is expected for this + * method to return {@code null}. Only known, unusual networks will typically have information + * returned, e.g. if they operate in countries other than the one suggested by their MCC. + */ + @Nullable + public TelephonyNetwork findNetworkByMccMnc(@NonNull String mcc, @NonNull String mnc) { + Objects.requireNonNull(mcc); + Objects.requireNonNull(mnc); + + libcore.timezone.TelephonyNetwork telephonyNetworkDelegate = + mDelegate.findNetworkByMccMnc(mcc, mnc); + return telephonyNetworkDelegate != null + ? new TelephonyNetwork(telephonyNetworkDelegate) : null; + } +} diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java new file mode 100644 index 000000000000..15dfe62bb789 --- /dev/null +++ b/core/java/android/timezone/TimeZoneFinder.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.timezone; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +import com.android.internal.annotations.GuardedBy; + +/** + * A class that can be used to find time zones. + * + * @hide + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class TimeZoneFinder { + + private static Object sLock = new Object(); + @GuardedBy("sLock") + private static TimeZoneFinder sInstance; + + private final libcore.timezone.TimeZoneFinder mDelegate; + + private TimeZoneFinder(libcore.timezone.TimeZoneFinder delegate) { + mDelegate = delegate; + } + + /** + * Obtains an instance for use when resolving telephony time zone information. This method never + * returns {@code null}. + */ + @NonNull + public static TimeZoneFinder getInstance() { + synchronized (sLock) { + if (sInstance == null) { + sInstance = new TimeZoneFinder(libcore.timezone.TimeZoneFinder.getInstance()); + } + } + return sInstance; + } + + /** + * Returns a {@link CountryTimeZones} object associated with the specified country code. + * Caching is handled as needed. If the country code is not recognized or there is an error + * during lookup this method can return null. + */ + @Nullable + public CountryTimeZones lookupCountryTimeZones(@NonNull String countryIso) { + libcore.timezone.CountryTimeZones delegate = mDelegate.lookupCountryTimeZones(countryIso); + return delegate == null ? null : new CountryTimeZones(delegate); + } +} diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index 4c08b596ee1f..9f6065e695c5 100755 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -23,9 +23,8 @@ import android.os.SystemProperties; /** * A structure describing general information about a display, such as its * size, density, and font scaling. - * <p>To access the DisplayMetrics members, initialize an object like this:</p> - * <pre> DisplayMetrics metrics = new DisplayMetrics(); - * getWindowManager().getDefaultDisplay().getMetrics(metrics);</pre> + * <p>To access the DisplayMetrics members, retrieve display metrics like this:</p> + * <pre>context.getResources().getDisplayMetrics();</pre> */ public class DisplayMetrics { /** @@ -245,7 +244,7 @@ public class DisplayMetrics { * this density value will be 1; on a 120 dpi screen it would be .75; etc. * * <p>This value does not exactly follow the real screen size (as given by - * {@link #xdpi} and {@link #ydpi}, but rather is used to scale the size of + * {@link #xdpi} and {@link #ydpi}), but rather is used to scale the size of * the overall UI in steps based on gross changes in the display dpi. For * example, a 240x320 screen will have a density of 1 even if its width is * 1.8", 1.3", etc. However, if the screen resolution is increased to diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java index 23fd4f2f61d3..9ac4cf267b47 100644 --- a/core/java/android/util/StatsLog.java +++ b/core/java/android/util/StatsLog.java @@ -179,6 +179,8 @@ public final class StatsLog extends StatsLogInternal { * @param rollbackType state of the rollback. * @param packageName package name being rolled back. * @param packageVersionCode version of the package being rolled back. + * @param rollbackReason reason the package is being rolled back. + * @param failingPackageName the package name causing the failure. * * @return True if the log request was sent to statsd. * @@ -186,7 +188,7 @@ public final class StatsLog extends StatsLogInternal { */ @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) public static boolean logWatchdogRollbackOccurred(int rollbackType, String packageName, - long packageVersionCode) { + long packageVersionCode, int rollbackReason, String failingPackageName) { synchronized (sLogLock) { try { IStatsManager service = getIStatsManagerLocked(); @@ -198,7 +200,7 @@ public final class StatsLog extends StatsLogInternal { } service.sendWatchdogRollbackOccurredAtom(rollbackType, packageName, - packageVersionCode); + packageVersionCode, rollbackReason, failingPackageName); return true; } catch (RemoteException e) { sService = null; diff --git a/core/java/android/view/CompositionSamplingListener.java b/core/java/android/view/CompositionSamplingListener.java index e4309a6d9aa4..f23bb40278ac 100644 --- a/core/java/android/view/CompositionSamplingListener.java +++ b/core/java/android/view/CompositionSamplingListener.java @@ -29,7 +29,7 @@ import java.util.concurrent.Executor; */ public abstract class CompositionSamplingListener { - private final long mNativeListener; + private long mNativeListener; private final Executor mExecutor; public CompositionSamplingListener(Executor executor) { @@ -37,13 +37,19 @@ public abstract class CompositionSamplingListener { mNativeListener = nativeCreate(this); } + public void destroy() { + if (mNativeListener == 0) { + return; + } + unregister(this); + nativeDestroy(mNativeListener); + mNativeListener = 0; + } + @Override protected void finalize() throws Throwable { try { - if (mNativeListener != 0) { - unregister(this); - nativeDestroy(mNativeListener); - } + destroy(); } finally { super.finalize(); } @@ -59,6 +65,9 @@ public abstract class CompositionSamplingListener { */ public static void register(CompositionSamplingListener listener, int displayId, IBinder stopLayer, Rect samplingArea) { + if (listener.mNativeListener == 0) { + return; + } Preconditions.checkArgument(displayId == Display.DEFAULT_DISPLAY, "default display only for now"); nativeRegister(listener.mNativeListener, stopLayer, samplingArea.left, samplingArea.top, @@ -69,6 +78,9 @@ public abstract class CompositionSamplingListener { * Unregisters a sampling listener. */ public static void unregister(CompositionSamplingListener listener) { + if (listener.mNativeListener == 0) { + return; + } nativeUnregister(listener.mNativeListener); } diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index 715181f28076..e77d7191d5b8 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -66,6 +66,7 @@ public final class DisplayCutout { private static final String BOTTOM_MARKER = "@bottom"; private static final String DP_MARKER = "@dp"; private static final String RIGHT_MARKER = "@right"; + private static final String LEFT_MARKER = "@left"; /** * Category for overlays that allow emulating a display cutout on devices that don't have @@ -642,6 +643,9 @@ public final class DisplayCutout { if (spec.endsWith(RIGHT_MARKER)) { offsetX = displayWidth; spec = spec.substring(0, spec.length() - RIGHT_MARKER.length()).trim(); + } else if (spec.endsWith(LEFT_MARKER)) { + offsetX = 0; + spec = spec.substring(0, spec.length() - LEFT_MARKER.length()).trim(); } else { offsetX = displayWidth / 2f; } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 2c48af04aa18..fcd8127bbaa4 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -190,8 +190,7 @@ public final class SurfaceControl implements Parcelable { private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject, InputWindowHandle handle); - private static native void nativeTransferTouchFocus(long transactionObj, IBinder fromToken, - IBinder toToken); + private static native boolean nativeGetProtectedContentSupport(); private static native void nativeSetMetadata(long transactionObj, long nativeObject, int key, Parcel data); @@ -2249,22 +2248,6 @@ public final class SurfaceControl implements Parcelable { } /** - * Transfers touch focus from one window to another. It is possible for multiple windows to - * have touch focus if they support split touch dispatch - * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this - * method only transfers touch focus of the specified window without affecting - * other windows that may also have touch focus at the same time. - * @param fromToken The token of a window that currently has touch focus. - * @param toToken The token of the window that should receive touch focus in - * place of the first. - * @hide - */ - public Transaction transferTouchFocus(IBinder fromToken, IBinder toToken) { - nativeTransferTouchFocus(mNativeObject, fromToken, toToken); - return this; - } - - /** * Waits until any changes to input windows have been sent from SurfaceFlinger to * InputFlinger before returning. * diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 3313537965c4..fe60bba92739 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -492,7 +492,7 @@ import java.util.function.Predicate; * * <p> * To initiate a layout, call {@link #requestLayout}. This method is typically - * called by a view on itself when it believes that is can no longer fit within + * called by a view on itself when it believes that it can no longer fit within * its current bounds. * </p> * @@ -2850,7 +2850,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Default for the root view. The gravity determines the text alignment, ALIGN_NORMAL, - * ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s text direction. + * ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph's text direction. * * Use with {@link #setTextAlignment(int)} */ @@ -2878,7 +2878,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public static final int TEXT_ALIGNMENT_CENTER = 4; /** - * Align to the start of the view, which is ALIGN_LEFT if the view’s resolved + * Align to the start of the view, which is ALIGN_LEFT if the view's resolved * layoutDirection is LTR, and ALIGN_RIGHT otherwise. * * Use with {@link #setTextAlignment(int)} @@ -2886,7 +2886,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public static final int TEXT_ALIGNMENT_VIEW_START = 5; /** - * Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved + * Align to the end of the view, which is ALIGN_RIGHT if the view's resolved * layoutDirection is LTR, and ALIGN_LEFT otherwise. * * Use with {@link #setTextAlignment(int)} @@ -3689,7 +3689,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * if the user swipes from the top of the screen. * <p>When system bars are hidden in immersive mode, they can be revealed temporarily with * system gestures, such as swiping from the top of the screen. These transient system bars - * will overlay app’s content, may have some degree of transparency, and will automatically + * will overlay app's content, may have some degree of transparency, and will automatically * hide after a short timeout. * </p><p>Since this flag is a modifier for {@link #SYSTEM_UI_FLAG_FULLSCREEN} and * {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, it only has an effect when used in combination @@ -10298,7 +10298,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Gets the unique identifier of the window in which this View reseides. + * Gets the unique identifier of the window in which this View resides. * * @return The window accessibility id. * @@ -16136,7 +16136,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * by the most recent call to {@link #measure(int, int)}. This result is a bit mask * as defined by {@link #MEASURED_SIZE_MASK} and {@link #MEASURED_STATE_TOO_SMALL}. * This should be used during measurement and layout calculations only. Use - * {@link #getHeight()} to see how wide a view is after layout. + * {@link #getHeight()} to see how high a view is after layout. * * @return The measured height of this view as a bit mask. */ @@ -26397,7 +26397,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Returns the over-scroll mode for this view. The result will be - * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} + * one of {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} * (allow over-scrolling only if the view content is larger than the container), * or {@link #OVER_SCROLL_NEVER}. * @@ -26414,7 +26414,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Set the over-scroll mode for this view. Valid over-scroll modes are - * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} + * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} * (allow over-scrolling only if the view content is larger than the container), * or {@link #OVER_SCROLL_NEVER}. * diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index 075b0ab728fa..7ce0f4571f76 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -127,7 +127,7 @@ public class AnimationUtils { * * @param context Application context used to access resources * @param id The resource id of the animation to load - * @return The animation object reference by the specified id + * @return The animation object referenced by the specified id * @throws NotFoundException when the animation cannot be loaded */ public static Animation loadAnimation(Context context, @AnimRes int id) @@ -208,7 +208,7 @@ public class AnimationUtils { * * @param context Application context used to access resources * @param id The resource id of the animation to load - * @return The animation object reference by the specified id + * @return The animation controller object referenced by the specified id * @throws NotFoundException when the layout animation controller cannot be loaded */ public static LayoutAnimationController loadLayoutAnimation(Context context, @AnimRes int id) @@ -331,7 +331,7 @@ public class AnimationUtils { * * @param context Application context used to access resources * @param id The resource id of the animation to load - * @return The animation object reference by the specified id + * @return The interpolator object referenced by the specified id * @throws NotFoundException */ public static Interpolator loadInterpolator(Context context, @AnimRes @InterpolatorRes int id) @@ -361,7 +361,7 @@ public class AnimationUtils { * * @param res The resources * @param id The resource id of the animation to load - * @return The interpolator object reference by the specified id + * @return The interpolator object referenced by the specified id * @throws NotFoundException * @hide */ diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 1f7ae0e7e245..bfa34ed76f07 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -17,6 +17,7 @@ package android.view.autofill; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; +import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.view.autofill.Helper.sDebug; import static android.view.autofill.Helper.sVerbose; import static android.view.autofill.Helper.toList; @@ -60,6 +61,7 @@ import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityWindowInfo; +import android.widget.TextView; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; @@ -966,6 +968,10 @@ public final class AutofillManager { if (!isClientDisablingEnterExitEvent()) { final AutofillValue value = view.getAutofillValue(); + if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) { + flags |= FLAG_PASSWORD_INPUT_TYPE; + } + if (!isActiveLocked()) { // Starts new session. startSessionLocked(id, null, value, flags); @@ -1130,6 +1136,10 @@ public final class AutofillManager { } else { // don't notify entered when Activity is already in background if (!isClientDisablingEnterExitEvent()) { + if (view instanceof TextView && ((TextView) view).isAnyPasswordInputType()) { + flags |= FLAG_PASSWORD_INPUT_TYPE; + } + if (!isActiveLocked()) { // Starts new session. startSessionLocked(id, bounds, null, flags); diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index d395f5294d6c..e671708a1106 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -92,7 +92,10 @@ import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; /** @@ -422,6 +425,13 @@ public final class InputMethodManager { int mCursorCandEnd; /** + * Initial startInput with {@link StartInputReason.WINDOW_FOCUS_GAIN} is executed + * in a background thread. Later, if there is an actual startInput it will wait on + * main thread till the background thread completes. + */ + private CompletableFuture<Void> mWindowFocusGainFuture; + + /** * The instance that has previously been sent to the input method. */ private CursorAnchorInfo mCursorAnchorInfo = null; @@ -645,14 +655,14 @@ public final class InputMethodManager { } catch (RemoteException e) { } } - // Check focus again in case that "onWindowFocus" is called before - // handling this message. - if (mServedView != null && canStartInput(mServedView)) { - if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) { - final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS - : StartInputReason.DEACTIVATED_BY_IMMS; - startInputInner(reason, null, 0, 0, 0); - } + } + // Check focus again in case that "onWindowFocus" is called before + // handling this message. + if (mServedView != null && canStartInput(mServedView)) { + if (checkFocusNoStartInput(mRestartOnNextWindowFocus)) { + final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS + : StartInputReason.DEACTIVATED_BY_IMMS; + startInputInner(reason, null, 0, 0, 0); } } return; @@ -1215,6 +1225,10 @@ public final class InputMethodManager { */ void clearBindingLocked() { if (DEBUG) Log.v(TAG, "Clearing binding!"); + if (mWindowFocusGainFuture != null) { + mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */); + mWindowFocusGainFuture = null; + } clearConnectionLocked(); setInputChannelLocked(null); mBindSequence = -1; @@ -1598,6 +1612,18 @@ public final class InputMethodManager { boolean startInputInner(@StartInputReason int startInputReason, @Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags) { + if (startInputReason != StartInputReason.WINDOW_FOCUS_GAIN + && mWindowFocusGainFuture != null) { + try { + mWindowFocusGainFuture.get(); + } catch (ExecutionException | InterruptedException e) { + // do nothing + } catch (CancellationException e) { + // window no longer has focus. + return true; + } + } + final View view; synchronized (mH) { view = mServedView; @@ -1951,31 +1977,38 @@ public final class InputMethodManager { startInputFlags |= StartInputFlags.FIRST_WINDOW_FOCUS_GAIN; } - if (checkFocusNoStartInput(forceNewFocus)) { - // We need to restart input on the current focus view. This - // should be done in conjunction with telling the system service - // about the window gaining focus, to help make the transition - // smooth. - if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(), - startInputFlags, softInputMode, windowFlags)) { - return; - } + final boolean forceNewFocus1 = forceNewFocus; + final int startInputFlags1 = startInputFlags; + if (mWindowFocusGainFuture != null) { + mWindowFocusGainFuture.cancel(false/* mayInterruptIfRunning */); } + mWindowFocusGainFuture = CompletableFuture.runAsync(() -> { + if (checkFocusNoStartInput(forceNewFocus1)) { + // We need to restart input on the current focus view. This + // should be done in conjunction with telling the system service + // about the window gaining focus, to help make the transition + // smooth. + if (startInputInner(StartInputReason.WINDOW_FOCUS_GAIN, rootView.getWindowToken(), + startInputFlags1, softInputMode, windowFlags)) { + return; + } + } - // For some reason we didn't do a startInput + windowFocusGain, so - // we'll just do a window focus gain and call it a day. - synchronized (mH) { - try { - if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput"); - mService.startInputOrWindowGainedFocus( - StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient, - rootView.getWindowToken(), startInputFlags, softInputMode, windowFlags, - null, null, 0 /* missingMethodFlags */, - rootView.getContext().getApplicationInfo().targetSdkVersion); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + // For some reason we didn't do a startInput + windowFocusGain, so + // we'll just do a window focus gain and call it a day. + synchronized (mH) { + try { + if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput"); + mService.startInputOrWindowGainedFocus( + StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient, + rootView.getWindowToken(), startInputFlags1, softInputMode, windowFlags, + null, null, 0 /* missingMethodFlags */, + rootView.getContext().getApplicationInfo().targetSdkVersion); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } - } + }); } /** @hide */ @@ -1990,6 +2023,10 @@ public final class InputMethodManager { // If the mCurRootView is losing window focus, release the strong reference to it // so as not to prevent it from being garbage-collected. mCurRootView = null; + if (mWindowFocusGainFuture != null) { + mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */); + mWindowFocusGainFuture = null; + } } else { if (DEBUG) { Log.v(TAG, "Ignoring onPreWindowFocus()." diff --git a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java b/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java index 6b90588f8d25..31645672f049 100644 --- a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java +++ b/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java @@ -60,7 +60,9 @@ public final class ActionsModelParamsSupplier implements private boolean mParsed = true; public ActionsModelParamsSupplier(Context context, @Nullable Runnable onChangedListener) { - mAppContext = Preconditions.checkNotNull(context).getApplicationContext(); + final Context appContext = Preconditions.checkNotNull(context).getApplicationContext(); + // Some contexts don't have an app context. + mAppContext = appContext != null ? appContext : context; mOnChangedListener = onChangedListener == null ? () -> {} : onChangedListener; mSettingsObserver = new SettingsObserver(mAppContext, () -> { synchronized (mLock) { diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java index aeb99b896b11..7c527bae475d 100644 --- a/core/java/android/view/textclassifier/ConversationActions.java +++ b/core/java/android/view/textclassifier/ConversationActions.java @@ -21,10 +21,12 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.UserIdInt; import android.app.Person; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.text.SpannedString; import com.android.internal.annotations.VisibleForTesting; @@ -316,6 +318,8 @@ public final class ConversationActions implements Parcelable { private final List<String> mHints; @Nullable private String mCallingPackageName; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; @NonNull private Bundle mExtras; @@ -340,6 +344,7 @@ public final class ConversationActions implements Parcelable { List<String> hints = new ArrayList<>(); in.readStringList(hints); String callingPackageName = in.readString(); + int userId = in.readInt(); Bundle extras = in.readBundle(); Request request = new Request( conversation, @@ -348,6 +353,7 @@ public final class ConversationActions implements Parcelable { hints, extras); request.setCallingPackageName(callingPackageName); + request.setUserId(userId); return request; } @@ -358,6 +364,7 @@ public final class ConversationActions implements Parcelable { parcel.writeInt(mMaxSuggestions); parcel.writeStringList(mHints); parcel.writeString(mCallingPackageName); + parcel.writeInt(mUserId); parcel.writeBundle(mExtras); } @@ -428,6 +435,24 @@ public final class ConversationActions implements Parcelable { } /** + * Sets the id of the user that sent this request. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of the user that sent this request. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the extended data related to this request. * * <p><b>NOTE: </b>Do not modify this bundle. diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java index 9ae0c65e0cff..ae9d5c6520f7 100644 --- a/core/java/android/view/textclassifier/SelectionEvent.java +++ b/core/java/android/view/textclassifier/SelectionEvent.java @@ -19,8 +19,10 @@ package android.view.textclassifier; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.view.textclassifier.TextClassifier.EntityType; import android.view.textclassifier.TextClassifier.WidgetType; @@ -127,6 +129,7 @@ public final class SelectionEvent implements Parcelable { private String mWidgetType = TextClassifier.WIDGET_TYPE_UNKNOWN; private @InvocationMethod int mInvocationMethod; @Nullable private String mWidgetVersion; + private @UserIdInt int mUserId = UserHandle.USER_NULL; @Nullable private String mResultId; private long mEventTime; private long mDurationSinceSessionStart; @@ -171,6 +174,7 @@ public final class SelectionEvent implements Parcelable { mEnd = in.readInt(); mSmartStart = in.readInt(); mSmartEnd = in.readInt(); + mUserId = in.readInt(); } @Override @@ -199,6 +203,7 @@ public final class SelectionEvent implements Parcelable { dest.writeInt(mEnd); dest.writeInt(mSmartStart); dest.writeInt(mSmartEnd); + dest.writeInt(mUserId); } @Override @@ -401,6 +406,24 @@ public final class SelectionEvent implements Parcelable { } /** + * Sets the id of this event's user. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of this event's user. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the type of widget that was involved in triggering this event. */ @WidgetType @@ -426,6 +449,7 @@ public final class SelectionEvent implements Parcelable { mPackageName = context.getPackageName(); mWidgetType = context.getWidgetType(); mWidgetVersion = context.getWidgetVersion(); + mUserId = context.getUserId(); } /** @@ -612,7 +636,7 @@ public final class SelectionEvent implements Parcelable { @Override public int hashCode() { return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType, - mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mResultId, + mWidgetVersion, mPackageName, mUserId, mWidgetType, mInvocationMethod, mResultId, mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent, mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd); } @@ -633,6 +657,7 @@ public final class SelectionEvent implements Parcelable { && Objects.equals(mEntityType, other.mEntityType) && Objects.equals(mWidgetVersion, other.mWidgetVersion) && Objects.equals(mPackageName, other.mPackageName) + && mUserId == other.mUserId && Objects.equals(mWidgetType, other.mWidgetType) && mInvocationMethod == other.mInvocationMethod && Objects.equals(mResultId, other.mResultId) @@ -652,12 +677,12 @@ public final class SelectionEvent implements Parcelable { return String.format(Locale.US, "SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, " + "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, " - + "resultId=%s, eventTime=%d, durationSinceSessionStart=%d, " + + "userId=%d, resultId=%s, eventTime=%d, durationSinceSessionStart=%d, " + "durationSincePreviousEvent=%d, eventIndex=%d," + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}", mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType, mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, - mResultId, mEventTime, mDurationSinceSessionStart, + mUserId, mResultId, mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent, mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd); } diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java index 8f8766e3f783..a97c3305208a 100644 --- a/core/java/android/view/textclassifier/SystemTextClassifier.java +++ b/core/java/android/view/textclassifier/SystemTextClassifier.java @@ -18,6 +18,7 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.content.Context; import android.os.Bundle; @@ -50,6 +51,10 @@ public final class SystemTextClassifier implements TextClassifier { private final TextClassificationConstants mSettings; private final TextClassifier mFallback; private final String mPackageName; + // NOTE: Always set this before sending a request to the manager service otherwise the manager + // service will throw a remote exception. + @UserIdInt + private final int mUserId; private TextClassificationSessionId mSessionId; public SystemTextClassifier(Context context, TextClassificationConstants settings) @@ -60,6 +65,7 @@ public final class SystemTextClassifier implements TextClassifier { mFallback = context.getSystemService(TextClassificationManager.class) .getTextClassifier(TextClassifier.LOCAL); mPackageName = Preconditions.checkNotNull(context.getOpPackageName()); + mUserId = context.getUserId(); } /** @@ -72,6 +78,7 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { request.setCallingPackageName(mPackageName); + request.setUserId(mUserId); final BlockingCallback<TextSelection> callback = new BlockingCallback<>("textselection"); mManagerService.onSuggestSelection(mSessionId, request, callback); @@ -95,6 +102,7 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { request.setCallingPackageName(mPackageName); + request.setUserId(mUserId); final BlockingCallback<TextClassification> callback = new BlockingCallback<>("textclassification"); mManagerService.onClassifyText(mSessionId, request, callback); @@ -123,6 +131,7 @@ public final class SystemTextClassifier implements TextClassifier { try { request.setCallingPackageName(mPackageName); + request.setUserId(mUserId); final BlockingCallback<TextLinks> callback = new BlockingCallback<>("textlinks"); mManagerService.onGenerateLinks(mSessionId, request, callback); @@ -142,6 +151,7 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { + event.setUserId(mUserId); mManagerService.onSelectionEvent(mSessionId, event); } catch (RemoteException e) { Log.e(LOG_TAG, "Error reporting selection event.", e); @@ -154,6 +164,12 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { + final TextClassificationContext tcContext = event.getEventContext() == null + ? new TextClassificationContext.Builder(mPackageName, WIDGET_TYPE_UNKNOWN) + .build() + : event.getEventContext(); + tcContext.setUserId(mUserId); + event.setEventContext(tcContext); mManagerService.onTextClassifierEvent(mSessionId, event); } catch (RemoteException e) { Log.e(LOG_TAG, "Error reporting textclassifier event.", e); @@ -167,6 +183,7 @@ public final class SystemTextClassifier implements TextClassifier { try { request.setCallingPackageName(mPackageName); + request.setUserId(mUserId); final BlockingCallback<TextLanguage> callback = new BlockingCallback<>("textlanguage"); mManagerService.onDetectLanguage(mSessionId, request, callback); @@ -187,6 +204,7 @@ public final class SystemTextClassifier implements TextClassifier { try { request.setCallingPackageName(mPackageName); + request.setUserId(mUserId); final BlockingCallback<ConversationActions> callback = new BlockingCallback<>("conversation-actions"); mManagerService.onSuggestConversationActions(mSessionId, request, callback); @@ -228,6 +246,7 @@ public final class SystemTextClassifier implements TextClassifier { printWriter.printPair("mFallback", mFallback); printWriter.printPair("mPackageName", mPackageName); printWriter.printPair("mSessionId", mSessionId); + printWriter.printPair("mUserId", mUserId); printWriter.decreaseIndent(); printWriter.println(); } @@ -243,6 +262,7 @@ public final class SystemTextClassifier implements TextClassifier { @NonNull TextClassificationSessionId sessionId) { mSessionId = Preconditions.checkNotNull(sessionId); try { + classificationContext.setUserId(mUserId); mManagerService.onCreateTextClassificationSession(classificationContext, mSessionId); } catch (RemoteException e) { Log.e(LOG_TAG, "Error starting a new classification session.", e); diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 63210516b3e1..975f3ba0763a 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.PendingIntent; import android.app.RemoteAction; import android.content.Context; @@ -35,6 +36,7 @@ import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.text.SpannedString; import android.util.ArrayMap; import android.view.View.OnClickListener; @@ -551,6 +553,8 @@ public final class TextClassification implements Parcelable { @Nullable private final ZonedDateTime mReferenceTime; @NonNull private final Bundle mExtras; @Nullable private String mCallingPackageName; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; private Request( CharSequence text, @@ -631,6 +635,24 @@ public final class TextClassification implements Parcelable { } /** + * Sets the id of the user that sent this request. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of the user that sent this request. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the extended data. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -730,6 +752,7 @@ public final class TextClassification implements Parcelable { dest.writeParcelable(mDefaultLocales, flags); dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString()); dest.writeString(mCallingPackageName); + dest.writeInt(mUserId); dest.writeBundle(mExtras); } @@ -742,11 +765,13 @@ public final class TextClassification implements Parcelable { final ZonedDateTime referenceTime = referenceTimeString == null ? null : ZonedDateTime.parse(referenceTimeString); final String callingPackageName = in.readString(); + final int userId = in.readInt(); final Bundle extras = in.readBundle(); final Request request = new Request(text, startIndex, endIndex, defaultLocales, referenceTime, extras); request.setCallingPackageName(callingPackageName); + request.setUserId(userId); return request; } diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java index 3bf8e9bd2bf0..db07685fe3ae 100644 --- a/core/java/android/view/textclassifier/TextClassificationContext.java +++ b/core/java/android/view/textclassifier/TextClassificationContext.java @@ -18,8 +18,10 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.view.textclassifier.TextClassifier.WidgetType; import com.android.internal.util.Preconditions; @@ -35,6 +37,8 @@ public final class TextClassificationContext implements Parcelable { private final String mPackageName; private final String mWidgetType; @Nullable private final String mWidgetVersion; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; private TextClassificationContext( String packageName, @@ -54,6 +58,24 @@ public final class TextClassificationContext implements Parcelable { } /** + * Sets the id of this context's user. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of this context's user. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the widget type for this classification context. */ @NonNull @@ -75,8 +97,8 @@ public final class TextClassificationContext implements Parcelable { @Override public String toString() { return String.format(Locale.US, "TextClassificationContext{" - + "packageName=%s, widgetType=%s, widgetVersion=%s}", - mPackageName, mWidgetType, mWidgetVersion); + + "packageName=%s, widgetType=%s, widgetVersion=%s, userId=%d}", + mPackageName, mWidgetType, mWidgetVersion, mUserId); } /** @@ -133,12 +155,14 @@ public final class TextClassificationContext implements Parcelable { parcel.writeString(mPackageName); parcel.writeString(mWidgetType); parcel.writeString(mWidgetVersion); + parcel.writeInt(mUserId); } private TextClassificationContext(Parcel in) { mPackageName = in.readString(); mWidgetType = in.readString(); mWidgetVersion = in.readString(); + mUserId = in.readInt(); } public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR = diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java index 57da829b3f44..a041296f97db 100644 --- a/core/java/android/view/textclassifier/TextClassifierEvent.java +++ b/core/java/android/view/textclassifier/TextClassifierEvent.java @@ -139,7 +139,7 @@ public abstract class TextClassifierEvent implements Parcelable { @Nullable private final String[] mEntityTypes; @Nullable - private final TextClassificationContext mEventContext; + private TextClassificationContext mEventContext; @Nullable private final String mResultId; private final int mEventIndex; @@ -289,6 +289,15 @@ public abstract class TextClassifierEvent implements Parcelable { } /** + * Sets the event context. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setEventContext(@Nullable TextClassificationContext eventContext) { + mEventContext = eventContext; + } + + /** * Returns the id of the text classifier result related to this event. */ @Nullable diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java index 6c75ffbea0cd..3e3dc72bd677 100644 --- a/core/java/android/view/textclassifier/TextLanguage.java +++ b/core/java/android/view/textclassifier/TextLanguage.java @@ -20,10 +20,12 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.icu.util.ULocale; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; @@ -226,6 +228,8 @@ public final class TextLanguage implements Parcelable { private final CharSequence mText; private final Bundle mExtra; @Nullable private String mCallingPackageName; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; private Request(CharSequence text, Bundle bundle) { mText = text; @@ -260,6 +264,24 @@ public final class TextLanguage implements Parcelable { } /** + * Sets the id of the user that sent this request. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of the user that sent this request. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns a bundle containing non-structured extra information about this request. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -278,16 +300,19 @@ public final class TextLanguage implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeCharSequence(mText); dest.writeString(mCallingPackageName); + dest.writeInt(mUserId); dest.writeBundle(mExtra); } private static Request readFromParcel(Parcel in) { final CharSequence text = in.readCharSequence(); final String callingPackageName = in.readString(); + final int userId = in.readInt(); final Bundle extra = in.readBundle(); final Request request = new Request(text, extra); request.setCallingPackageName(callingPackageName); + request.setUserId(userId); return request; } diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index f3e0dc1ed843..d7ac52414923 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -20,11 +20,13 @@ import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.Context; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.text.Spannable; import android.text.method.MovementMethod; import android.text.style.ClickableSpan; @@ -339,6 +341,8 @@ public final class TextLinks implements Parcelable { private final boolean mLegacyFallback; @Nullable private String mCallingPackageName; private final Bundle mExtras; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; private Request( CharSequence text, @@ -410,6 +414,24 @@ public final class TextLinks implements Parcelable { } /** + * Sets the id of the user that sent this request. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of the user that sent this request. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the extended data. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -509,6 +531,7 @@ public final class TextLinks implements Parcelable { dest.writeParcelable(mDefaultLocales, flags); dest.writeParcelable(mEntityConfig, flags); dest.writeString(mCallingPackageName); + dest.writeInt(mUserId); dest.writeBundle(mExtras); } @@ -517,11 +540,13 @@ public final class TextLinks implements Parcelable { final LocaleList defaultLocales = in.readParcelable(null); final EntityConfig entityConfig = in.readParcelable(null); final String callingPackageName = in.readString(); + final int userId = in.readInt(); final Bundle extras = in.readBundle(); final Request request = new Request(text, defaultLocales, entityConfig, /* legacyFallback= */ true, extras); request.setCallingPackageName(callingPackageName); + request.setUserId(userId); return request; } diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index 75c27bdbc1d5..94e0bc3dcd67 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -20,10 +20,12 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.text.SpannedString; import android.util.ArrayMap; import android.view.textclassifier.TextClassifier.EntityType; @@ -211,6 +213,8 @@ public final class TextSelection implements Parcelable { private final boolean mDarkLaunchAllowed; private final Bundle mExtras; @Nullable private String mCallingPackageName; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; private Request( CharSequence text, @@ -292,6 +296,24 @@ public final class TextSelection implements Parcelable { } /** + * Sets the id of the user that sent this request. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of the user that sent this request. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the extended data. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -394,6 +416,7 @@ public final class TextSelection implements Parcelable { dest.writeInt(mEndIndex); dest.writeParcelable(mDefaultLocales, flags); dest.writeString(mCallingPackageName); + dest.writeInt(mUserId); dest.writeBundle(mExtras); } @@ -403,11 +426,13 @@ public final class TextSelection implements Parcelable { final int endIndex = in.readInt(); final LocaleList defaultLocales = in.readParcelable(null); final String callingPackageName = in.readString(); + final int userId = in.readInt(); final Bundle extras = in.readBundle(); final Request request = new Request(text, startIndex, endIndex, defaultLocales, /* darkLaunchAllowed= */ false, extras); request.setCallingPackageName(callingPackageName); + request.setUserId(userId); return request; } diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index 23d12374453f..3824c22a40a7 100644 --- a/core/java/android/webkit/CookieManager.java +++ b/core/java/android/webkit/CookieManager.java @@ -22,7 +22,10 @@ import android.net.WebAddress; /** * Manages the cookies used by an application's {@link WebView} instances. - * Cookies are manipulated according to RFC2109. + * <p> + * CookieManager represents cookies as strings in the same format as the + * HTTP {@code Cookie} and {@code Set-Cookie} header fields (defined in + * <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03">RFC6265bis</a>). */ public abstract class CookieManager { /** diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 4db630808ef1..f8522edb7dcd 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -71,11 +71,24 @@ public class WebChromeClient { } /** - * Notify the host application that the current page has entered full - * screen mode. The host application must show the custom View which - * contains the web contents — video or other HTML content — - * in full screen mode. Also see "Full screen support" documentation on - * {@link WebView}. + * Notify the host application that the current page has entered full screen mode. After this + * call, web content will no longer be rendered in the WebView, but will instead be rendered + * in {@code view}. The host application should add this View to a Window which is configured + * with {@link android.view.WindowManager.LayoutParams#FLAG_FULLSCREEN} flag in order to + * actually display this web content full screen. + * + * <p>The application may explicitly exit fullscreen mode by invoking {@code callback} (ex. when + * the user presses the back button). However, this is generally not necessary as the web page + * will often show its own UI to close out of fullscreen. Regardless of how the WebView exits + * fullscreen mode, WebView will invoke {@link #onHideCustomView()}, signaling for the + * application to remove the custom View. + * + * <p>If this method is not overridden, WebView will report to the web page it does not support + * fullscreen mode and will not honor the web page's request to run in fullscreen mode. + * + * <p class="note"><b>Note:</b> if overriding this method, the application must also override + * {@link #onHideCustomView()}. + * * @param view is the View object to be shown. * @param callback invoke this callback to request the page to exit * full screen mode. @@ -98,10 +111,13 @@ public class WebChromeClient { CustomViewCallback callback) {}; /** - * Notify the host application that the current page has exited full - * screen mode. The host application must hide the custom View, ie. the - * View passed to {@link #onShowCustomView} when the content entered fullscreen. - * Also see "Full screen support" documentation on {@link WebView}. + * Notify the host application that the current page has exited full screen mode. The host + * application must hide the custom View (the View which was previously passed to {@link + * #onShowCustomView(View, CustomViewCallback) onShowCustomView()}). After this call, web + * content will render in the original WebView again. + * + * <p class="note"><b>Note:</b> if overriding this method, the application must also override + * {@link #onShowCustomView(View, CustomViewCallback) onShowCustomView()}. */ public void onHideCustomView() {} diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 8a5b64d95f19..2d27a789ebcb 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -328,6 +328,9 @@ public abstract class WebSettings { * <p> * The built-in mechanisms are the only currently supported zoom * mechanisms, so it is recommended that this setting is always enabled. + * However, on-screen zoom controls are deprecated in Android (see + * {@link android.widget.ZoomButtonsController}) so it's recommended to + * disable {@link #setDisplayZoomControls}. * * @param enabled whether the WebView should use its built-in zoom mechanisms */ @@ -347,7 +350,9 @@ public abstract class WebSettings { /** * Sets whether the WebView should display on-screen zoom controls when * using the built-in zoom mechanisms. See {@link #setBuiltInZoomControls}. - * The default is {@code true}. + * The default is {@code true}. However, on-screen zoom controls are deprecated + * in Android (see {@link android.widget.ZoomButtonsController}) so it's + * recommended to set this to {@code false}. * * @param enabled whether the WebView should display on-screen zoom controls */ diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index 6c38c8b9a0f5..de9f76d6eea1 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -49,9 +49,6 @@ import java.util.List; * To customize what type of view is used for the data object, * override {@link #getView(int, View, ViewGroup)} * and inflate a view resource. - * For a code example, see - * the <a href="https://github.com/googlesamples/android-CustomChoiceList/#readme"> - * CustomChoiceList</a> sample. * </p> * <p> * For an example of using an array adapter with a ListView, see the diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 72603444f5ff..53ea793071ab 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -120,10 +120,6 @@ import java.util.function.Predicate; * <a href="{@docRoot}training/improving-layouts/smooth-scrolling.html"> * Making ListView Scrolling Smooth</a> for more ways to ensure a smooth user experience.</p> * - * <p>For a more complete example of creating a custom adapter, see the - * <a href="{@docRoot}samples/CustomChoiceList/index.html"> - * Custom Choice List</a> sample app.</p> - * * <p>To specify an action when a user clicks or taps on a single list item, see * <a href="{@docRoot}guide/topics/ui/declaring-layout.html#HandlingUserSelections"> * Handling click events</a>.</p> diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index b2fedf7a3a7f..e4a34b943657 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -1268,12 +1268,12 @@ public class NumberPicker extends LinearLayout { * current value is set to the {@link NumberPicker#getMaxValue()} value. * </p> * <p> - * If the argument is less than the {@link NumberPicker#getMaxValue()} and + * If the argument is more than the {@link NumberPicker#getMaxValue()} and * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the * current value is set to the {@link NumberPicker#getMaxValue()} value. * </p> * <p> - * If the argument is less than the {@link NumberPicker#getMaxValue()} and + * If the argument is more than the {@link NumberPicker#getMaxValue()} and * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the * current value is set to the {@link NumberPicker#getMinValue()} value. * </p> diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 521922df97e0..d8624f9d3e9e 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -72,7 +72,7 @@ import java.util.TreeSet; * * <p>This behavior has been preserved for apps that set <code>android:targetSdkVersion="17"</code> * or older in their manifest's <code>uses-sdk</code> tag for compatibility. Apps targeting SDK - * version 18 or newer will receive the correct behavior</p> + * version 18 or newer will receive the correct behavior.</p> * * <p>See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative * Layout</a> guide.</p> diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 6f697cd65686..75d83438efda 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -6582,6 +6582,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mTransformation instanceof PasswordTransformationMethod; } + /** + * Returns true if the current inputType is any type of password. + * + * @hide + */ + public boolean isAnyPasswordInputType() { + final int inputType = getInputType(); + return isPasswordInputType(inputType) || isVisiblePasswordInputType(inputType); + } + static boolean isPasswordInputType(int inputType) { final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION); @@ -11283,6 +11293,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Nullable + final TextClassificationManager getTextClassificationManagerForUser() { + return getServiceManagerForUser( + getContext().getPackageName(), TextClassificationManager.class); + } + + @Nullable final <T> T getServiceManagerForUser(String packageName, Class<T> managerClazz) { if (mTextOperationUser == null) { return getContext().getSystemService(managerClazz); @@ -12383,8 +12399,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @NonNull public TextClassifier getTextClassifier() { if (mTextClassifier == null) { - final TextClassificationManager tcm = - mContext.getSystemService(TextClassificationManager.class); + final TextClassificationManager tcm = getTextClassificationManagerForUser(); if (tcm != null) { return tcm.getTextClassifier(); } @@ -12400,8 +12415,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @NonNull TextClassifier getTextClassificationSession() { if (mTextClassificationSession == null || mTextClassificationSession.isDestroyed()) { - final TextClassificationManager tcm = - mContext.getSystemService(TextClassificationManager.class); + final TextClassificationManager tcm = getTextClassificationManagerForUser(); if (tcm != null) { final String widgetType; if (isTextEditable()) { diff --git a/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java b/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java index 4b0b098d4e5b..9aee879f21da 100644 --- a/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java +++ b/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java @@ -18,16 +18,9 @@ package com.android.ims.internal.uce.uceservice; import android.content.Context; import android.content.Intent; - -import android.os.Handler; -import android.os.HandlerThread; import android.os.IBinder; -import android.os.Message; -import android.os.ServiceManager; import android.os.RemoteException; - -import java.util.HashMap; -import android.util.Log; +import android.os.ServiceManager; /** * ImsUceManager Declaration @@ -49,55 +42,25 @@ public class ImsUceManager { private IUceService mUceService = null; private UceServiceDeathRecipient mDeathReceipient = new UceServiceDeathRecipient(); private Context mContext; - private int mPhoneId; - /** - * Stores the UceManager instaces of Clients identified by - * phoneId - * @hide - */ - private static HashMap<Integer, ImsUceManager> sUceManagerInstances = - new HashMap<Integer, ImsUceManager>(); + private static final Object sLock = new Object(); + private static ImsUceManager sUceManager; public static final String ACTION_UCE_SERVICE_UP = "com.android.ims.internal.uce.UCE_SERVICE_UP"; public static final String ACTION_UCE_SERVICE_DOWN = "com.android.ims.internal.uce.UCE_SERVICE_DOWN"; - /** Uce Service status received in IUceListener.setStatus() - * callback - * @hide - */ - public static final int UCE_SERVICE_STATUS_FAILURE = 0; - /** indicate UI to call Presence/Options API. */ - public static final int UCE_SERVICE_STATUS_ON = 1; - /** Indicate UI destroy Presence/Options */ - public static final int UCE_SERVICE_STATUS_CLOSED = 2; - /** Service up and trying to register for network events */ - public static final int UCE_SERVICE_STATUS_READY = 3; - - /** - * Part of the ACTION_UCE_SERVICE_UP or _DOWN intents. A long - * value; the phone ID corresponding to the IMS service coming up or down. - * Internal use only. - * @hide - */ - public static final String EXTRA_PHONE_ID = "android:phone_id"; - /** * Gets the instance of UCE Manager * @hide */ - public static ImsUceManager getInstance(Context context, int phoneId) { - //if (DBG) Log.d (LOG_TAG, "GetInstance Called"); - synchronized (sUceManagerInstances) { - if (sUceManagerInstances.containsKey(phoneId)) { - return sUceManagerInstances.get(phoneId); - } else { - ImsUceManager uceMgr = new ImsUceManager(context, phoneId); - sUceManagerInstances.put(phoneId, uceMgr); - return uceMgr; + public static ImsUceManager getInstance(Context context) { + synchronized (sLock) { + if (sUceManager == null && context != null) { + sUceManager = new ImsUceManager(context); } + return sUceManager; } } @@ -105,10 +68,9 @@ public class ImsUceManager { * Constructor * @hide */ - private ImsUceManager(Context context, int phoneId) { + private ImsUceManager(Context context) { //if (DBG) Log.d (LOG_TAG, "Constructor"); mContext = context; - mPhoneId = phoneId; createUceService(true); } @@ -129,7 +91,7 @@ public class ImsUceManager { * Gets the UCE service name * @hide */ - private String getUceServiceName(int phoneId) { + private String getUceServiceName() { return UCE_SERVICE; } @@ -143,14 +105,14 @@ public class ImsUceManager { public void createUceService(boolean checkService) { //if (DBG) Log.d (LOG_TAG, "CreateUceService Called"); if (checkService) { - IBinder binder = ServiceManager.checkService(getUceServiceName(mPhoneId)); + IBinder binder = ServiceManager.checkService(getUceServiceName()); if (binder == null) { //if (DBG)Log.d (LOG_TAG, "Unable to find IBinder"); return; } } - IBinder b = ServiceManager.getService(getUceServiceName(mPhoneId)); + IBinder b = ServiceManager.getService(getUceServiceName()); if (b != null) { try { @@ -174,12 +136,10 @@ public class ImsUceManager { private class UceServiceDeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { - //if (DBG) Log.d (LOG_TAG, "found IBinder/IUceService Service Died"); mUceService = null; if (mContext != null) { Intent intent = new Intent(ACTION_UCE_SERVICE_DOWN); - intent.putExtra(EXTRA_PHONE_ID, mPhoneId); mContext.sendBroadcast(new Intent(intent)); } } diff --git a/core/java/com/android/internal/app/AbstractResolverComparator.java b/core/java/com/android/internal/app/AbstractResolverComparator.java index 9ac979b54716..3a6a71d8eab6 100644 --- a/core/java/com/android/internal/app/AbstractResolverComparator.java +++ b/core/java/com/android/internal/app/AbstractResolverComparator.java @@ -30,6 +30,7 @@ import android.util.Log; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; +import java.text.Collator; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -37,7 +38,7 @@ import java.util.List; /** * Used to sort resolved activities in {@link ResolverListController}. */ -abstract class AbstractResolverComparator implements Comparator<ResolvedComponentInfo> { +public abstract class AbstractResolverComparator implements Comparator<ResolvedComponentInfo> { private static final int NUM_OF_TOP_ANNOTATIONS_TO_USE = 3; private static final boolean DEBUG = false; @@ -62,6 +63,8 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen // predicting ranking scores. private static final int WATCHDOG_TIMEOUT_MILLIS = 500; + private final Comparator<ResolveInfo> mAzComparator; + protected final Handler mHandler = new Handler(Looper.getMainLooper()) { public void handleMessage(Message msg) { switch (msg.what) { @@ -90,7 +93,7 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen } }; - AbstractResolverComparator(Context context, Intent intent) { + public AbstractResolverComparator(Context context, Intent intent) { String scheme = intent.getScheme(); mHttp = "http".equals(scheme) || "https".equals(scheme); mContentType = intent.getType(); @@ -100,6 +103,7 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen mDefaultBrowserPackageName = mHttp ? mPm.getDefaultBrowserPackageNameAsUser(UserHandle.myUserId()) : null; + mAzComparator = new AzInfoComparator(context); } // get annotations of content from intent. @@ -168,6 +172,20 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen return lhsSpecific ? -1 : 1; } } + + final boolean lPinned = lhsp.isPinned(); + final boolean rPinned = rhsp.isPinned(); + + // Pinned items always receive priority. + if (lPinned && !rPinned) { + return -1; + } else if (!lPinned && rPinned) { + return 1; + } else if (lPinned && rPinned) { + // If both items are pinned, resolve the tie alphabetically. + return mAzComparator.compare(lhsp.getResolveInfoAt(0), rhsp.getResolveInfoAt(0)); + } + return compare(lhs, rhs); } @@ -258,4 +276,25 @@ abstract class AbstractResolverComparator implements Comparator<ResolvedComponen } return false; } + + /** + * Sort intents alphabetically based on package name. + */ + class AzInfoComparator implements Comparator<ResolveInfo> { + Collator mCollator; + AzInfoComparator(Context context) { + mCollator = Collator.getInstance(context.getResources().getConfiguration().locale); + } + + @Override + public int compare(ResolveInfo lhsp, ResolveInfo rhsp) { + if (lhsp == null) { + return -1; + } else if (rhsp == null) { + return 1; + } + return mCollator.compare(lhsp.activityInfo.packageName, rhsp.activityInfo.packageName); + } + } + } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index c0c9dd32a52a..e130eb22fc2a 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -44,6 +44,7 @@ import android.content.IntentFilter; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; +import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.LabeledIntent; @@ -54,6 +55,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.content.res.Configuration; +import android.content.res.Resources; import android.database.Cursor; import android.database.DataSetObserver; import android.graphics.Bitmap; @@ -69,6 +71,7 @@ import android.metrics.LogMaker; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -78,10 +81,12 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; import android.os.UserManager; +import android.os.storage.StorageManager; import android.provider.DeviceConfig; import android.provider.DocumentsContract; import android.provider.Downloads; import android.provider.OpenableColumns; +import android.provider.Settings; import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; @@ -104,6 +109,7 @@ import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.AbsListView; import android.widget.BaseAdapter; +import android.widget.Button; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; @@ -120,9 +126,11 @@ import com.android.internal.widget.ResolverDrawerLayout; import com.google.android.collect.Lists; +import java.io.File; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.net.URISyntaxException; import java.text.Collator; import java.util.ArrayList; import java.util.Arrays; @@ -156,6 +164,9 @@ public class ChooserActivity extends ResolverActivity { private static final String PREF_NUM_SHEET_EXPANSIONS = "pref_num_sheet_expansions"; + private static final String CHIP_LABEL_METADATA_KEY = "android.service.chooser.chip_label"; + private static final String CHIP_ICON_METADATA_KEY = "android.service.chooser.chip_icon"; + private static final boolean DEBUG = false; /** @@ -256,6 +267,9 @@ public class ChooserActivity extends ResolverActivity { private static final int MAX_EXTRA_INITIAL_INTENTS = 2; private static final int MAX_EXTRA_CHOOSER_TARGETS = 2; + private SharedPreferences mPinnedSharedPrefs; + private static final String PINNED_SHARED_PREFS_NAME = "chooser_pin_settings"; + private boolean mListViewDataChanged = false; @Retention(SOURCE) @@ -519,6 +533,15 @@ public class ChooserActivity extends ResolverActivity { mIsSuccessfullySelected = false; Intent intent = getIntent(); Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT); + if (targetParcelable instanceof Uri) { + try { + targetParcelable = Intent.parseUri(targetParcelable.toString(), + Intent.URI_INTENT_SCHEME); + } catch (URISyntaxException ex) { + // doesn't parse as an intent; let the next test fail and error out + } + } + if (!(targetParcelable instanceof Intent)) { Log.w("ChooserActivity", "Target is not an intent: " + targetParcelable); finish(); @@ -600,6 +623,8 @@ public class ChooserActivity extends ResolverActivity { Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER); setSafeForwardingMode(true); + mPinnedSharedPrefs = getPinnedSharedPrefs(this); + pa = intent.getParcelableArrayExtra(Intent.EXTRA_EXCLUDE_COMPONENTS); if (pa != null) { ComponentName[] names = new ComponentName[pa.length]; @@ -731,6 +756,23 @@ public class ChooserActivity extends ResolverActivity { } } + + static SharedPreferences getPinnedSharedPrefs(Context context) { + // The code below is because in the android:ui process, no one can hear you scream. + // The package info in the context isn't initialized in the way it is for normal apps, + // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we + // build the path manually below using the same policy that appears in ContextImpl. + // This fails silently under the hood if there's a problem, so if we find ourselves in + // the case where we don't have access to credential encrypted storage we just won't + // have our pinned target info. + final File prefsFile = new File(new File( + Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL, + context.getUserId(), context.getPackageName()), + "shared_prefs"), + PINNED_SHARED_PREFS_NAME + ".xml"); + return context.getSharedPreferences(prefsFile, MODE_PRIVATE); + } + /** * Returns true if app prediction service is defined and the component exists on device. */ @@ -778,12 +820,19 @@ public class ChooserActivity extends ResolverActivity { return new PackageMonitor() { @Override public void onSomePackagesChanged() { - mAdapter.handlePackagesChanged(); - bindProfileView(); + handlePackagesChanged(); } }; } + /** + * Update UI to reflect changes in data. + */ + public void handlePackagesChanged() { + mAdapter.handlePackagesChanged(); + bindProfileView(); + } + private void onCopyButtonClicked(View v) { Intent targetIntent = getTargetIntent(); if (targetIntent == null) { @@ -868,6 +917,108 @@ public class ChooserActivity extends ResolverActivity { } } + private ComponentName getNearbySharingComponent() { + String nearbyComponent = Settings.Secure.getString( + getContentResolver(), + Settings.Secure.NEARBY_SHARING_COMPONENT); + if (TextUtils.isEmpty(nearbyComponent)) { + nearbyComponent = getString(R.string.config_defaultNearbySharingComponent); + } + if (TextUtils.isEmpty(nearbyComponent)) { + return null; + } + return ComponentName.unflattenFromString(nearbyComponent); + } + + private TargetInfo getNearbySharingTarget(Intent originalIntent) { + final ComponentName cn = getNearbySharingComponent(); + if (cn == null) return null; + + final Intent resolveIntent = new Intent(); + resolveIntent.setComponent(cn); + final ResolveInfo ri = getPackageManager().resolveActivity( + resolveIntent, PackageManager.GET_META_DATA); + if (ri == null || ri.activityInfo == null) { + Log.e(TAG, "Device-specified nearby sharing component (" + cn + + ") not available"); + return null; + } + + // Allow the nearby sharing component to provide a more appropriate icon and label + // for the chip. + CharSequence name = null; + Drawable icon = null; + final Bundle metaData = ri.activityInfo.metaData; + if (metaData != null) { + try { + final Resources pkgRes = getPackageManager().getResourcesForActivity(cn); + final int nameResId = metaData.getInt(CHIP_LABEL_METADATA_KEY); + name = pkgRes.getString(nameResId); + final int resId = metaData.getInt(CHIP_ICON_METADATA_KEY); + icon = pkgRes.getDrawable(resId); + } catch (Resources.NotFoundException ex) { + } catch (NameNotFoundException ex) { + } + } + if (TextUtils.isEmpty(name)) { + name = ri.loadLabel(getPackageManager()); + } + if (icon == null) { + icon = ri.loadIcon(getPackageManager()); + } + + final DisplayResolveInfo dri = new DisplayResolveInfo( + originalIntent, ri, name, "", null); + dri.setDisplayIcon(icon); + return dri; + } + + private Button createActionButton(Drawable icon, CharSequence title, View.OnClickListener r) { + Button b = (Button) LayoutInflater.from(this).inflate(R.layout.chooser_action_button, null); + if (icon != null) { + final int size = getResources() + .getDimensionPixelSize(R.dimen.chooser_action_button_icon_size); + icon.setBounds(0, 0, size, size); + b.setCompoundDrawablesRelative(icon, null, null, null); + } + b.setText(title); + b.setOnClickListener(r); + return b; + } + + private Button createCopyButton() { + final Button b = createActionButton( + getDrawable(R.drawable.ic_menu_copy_material), + getString(R.string.copy), this::onCopyButtonClicked); + b.setId(R.id.chooser_copy_button); + return b; + } + + private @Nullable Button createNearbyButton(Intent originalIntent) { + final TargetInfo ti = getNearbySharingTarget(originalIntent); + if (ti == null) return null; + + return createActionButton( + ti.getDisplayIcon(), + ti.getDisplayLabel(), + (View unused) -> { + safelyStartActivity(ti); + finish(); + } + ); + } + + private void addActionButton(ViewGroup parent, Button b) { + if (b == null) return; + final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams( + LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT + ); + final int gap = getResources().getDimensionPixelSize(R.dimen.resolver_icon_margin) / 2; + lp.setMarginsRelative(gap, 0, gap, 0); + parent.addView(b, lp); + } + private ViewGroup displayContentPreview(@ContentPreviewType int previewType, Intent targetIntent, LayoutInflater layoutInflater, ViewGroup convertView, ViewGroup parent) { @@ -901,8 +1052,10 @@ public class ChooserActivity extends ResolverActivity { ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate( R.layout.chooser_grid_preview_text, parent, false); - contentPreviewLayout.findViewById(R.id.copy_button).setOnClickListener( - this::onCopyButtonClicked); + final ViewGroup actionRow = + (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row); + addActionButton(actionRow, createCopyButton()); + addActionButton(actionRow, createNearbyButton(targetIntent)); CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT); if (sharingText == null) { @@ -1060,7 +1213,8 @@ public class ChooserActivity extends ResolverActivity { // TODO(b/120417119): Disable file copy until after moving to sysui, // due to permissions issues - contentPreviewLayout.findViewById(R.id.file_copy_button).setVisibility(View.GONE); + //((ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row)) + // .addView(createCopyButton()); String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { @@ -1277,9 +1431,10 @@ public class ChooserActivity extends ResolverActivity { } ComponentName name = ri.activityInfo.getComponentName(); + boolean pinned = mPinnedSharedPrefs.getBoolean(name.flattenToString(), false); ResolverTargetActionsDialogFragment f = new ResolverTargetActionsDialogFragment(ri.loadLabel(getPackageManager()), - name); + name, pinned); f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG); } @@ -1511,7 +1666,7 @@ public class ChooserActivity extends ResolverActivity { } return new IntentFilter(intent.getAction(), dataString); } catch (Exception e) { - Log.e(TAG, "failed to get target intent filter " + e); + Log.e(TAG, "failed to get target intent filter", e); return null; } } @@ -1956,6 +2111,12 @@ public class ChooserActivity extends ResolverActivity { } return false; } + + @Override + public boolean isComponentPinned(ComponentName name) { + return mPinnedSharedPrefs.getBoolean(name.flattenToString(), false); + } + } @Override @@ -2089,6 +2250,10 @@ public class ChooserActivity extends ResolverActivity { public boolean isSuspended() { return false; } + + public boolean isPinned() { + return false; + } } final class PlaceHolderTargetInfo extends NotSelectableTargetInfo { @@ -2177,6 +2342,10 @@ public class ChooserActivity extends ResolverActivity { return mIsSuspended; } + public boolean isPinned() { + return mSourceInfo != null && mSourceInfo.isPinned(); + } + /** * Since ShortcutInfos are returned by ShortcutManager, we can cache the shortcuts and skip * the call to LauncherApps#getShortcuts(ShortcutQuery). @@ -2428,6 +2597,12 @@ public class ChooserActivity extends ResolverActivity { } } + @Override + protected boolean shouldAddFooterView() { + // To accommodate for window insets + return true; + } + public class ChooserListAdapter extends ResolveListAdapter { public static final int TARGET_BAD = -1; public static final int TARGET_CALLER = 0; diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 871e4f84f3b6..d42c0bc5661a 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -386,30 +386,47 @@ public class ResolverActivity extends Activity { finish(); } + /** + * Numerous layouts are supported, each with optional ViewGroups. + * Make sure the inset gets added to the correct View, using + * a footer for Lists so it can properly scroll under the navbar. + */ + protected boolean shouldAddFooterView() { + if (useLayoutWithDefault()) return true; + + View buttonBar = findViewById(R.id.button_bar); + if (buttonBar == null || buttonBar.getVisibility() == View.GONE) return true; + + return false; + } + protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { mSystemWindowInsets = insets.getSystemWindowInsets(); mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top, mSystemWindowInsets.right, 0); + resetButtonBar(); + + // Need extra padding so the list can fully scroll up + if (shouldAddFooterView()) { + if (mFooterSpacer == null) { + mFooterSpacer = new Space(getApplicationContext()); + } else { + ((ListView) mAdapterView).removeFooterView(mFooterSpacer); + } + mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, + mSystemWindowInsets.bottom)); + ((ListView) mAdapterView).addFooterView(mFooterSpacer); + } + View emptyView = findViewById(R.id.empty); if (emptyView != null) { emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom - + getResources().getDimensionPixelSize( - R.dimen.chooser_edge_margin_normal) * 2); + + getResources().getDimensionPixelSize( + R.dimen.chooser_edge_margin_normal) * 2); } - if (mFooterSpacer == null) { - mFooterSpacer = new Space(getApplicationContext()); - } else { - ((ListView) mAdapterView).removeFooterView(mFooterSpacer); - } - mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, - mSystemWindowInsets.bottom)); - ((ListView) mAdapterView).addFooterView(mFooterSpacer); - - resetButtonBar(); - return insets.consumeSystemWindowInsets(); } @@ -565,7 +582,7 @@ public class ResolverActivity extends Activity { intent.getData().getHost(), mAdapter.getFilteredItem().getDisplayLabel()); } else if (mAdapter.areAllTargetsBrowsers()) { - dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES); + dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES); } else { dialogTitle = getString(ActionTitle.BROWSABLE_HOST_TITLE_RES, intent.getData().getHost()); @@ -1308,6 +1325,7 @@ public class ResolverActivity extends Activity { // In case this method is called again (due to activity recreation), avoid adding a new // header if one is already present. if (useHeader && listView != null && listView.getHeaderViewsCount() == 0) { + listView.setHeaderDividersEnabled(true); listView.addHeaderView(LayoutInflater.from(this).inflate( R.layout.resolver_different_item_header, listView, false)); } @@ -1350,11 +1368,13 @@ public class ResolverActivity extends Activity { final ViewGroup buttonLayout = findViewById(R.id.button_bar); if (buttonLayout != null) { buttonLayout.setVisibility(View.VISIBLE); - int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0; - buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(), - buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize( - R.dimen.resolver_button_bar_spacing) + inset); + if (!useLayoutWithDefault()) { + int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0; + buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(), + buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize( + R.dimen.resolver_button_bar_spacing) + inset); + } mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once); mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always); @@ -1411,6 +1431,7 @@ public class ResolverActivity extends Activity { private final Intent mResolvedIntent; private final List<Intent> mSourceIntents = new ArrayList<>(); private boolean mIsSuspended; + private boolean mPinned = false; public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel, CharSequence pInfo, Intent pOrigIntent) { @@ -1514,6 +1535,15 @@ public class ResolverActivity extends Activity { public boolean isSuspended() { return mIsSuspended; } + + @Override + public boolean isPinned() { + return mPinned; + } + + public void setPinned(boolean pinned) { + mPinned = pinned; + } } List<DisplayResolveInfo> getDisplayList() { @@ -1614,6 +1644,11 @@ public class ResolverActivity extends Activity { * @return true if this target can be selected by the user */ boolean isSuspended(); + + /** + * @return true if this target should be pinned to the front by the request of the user + */ + boolean isPinned(); } public class ResolveListAdapter extends BaseAdapter { @@ -1920,6 +1955,10 @@ public class ResolverActivity extends Activity { final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent); final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel, extraInfo, replaceIntent); + dri.setPinned(rci.isPinned()); + if (rci.isPinned()) { + Log.i(TAG, "Pinned item: " + rci.name); + } addResolveInfo(dri); if (replaceIntent == intent) { // Only add alternates if we didn't get a specific replacement from @@ -2061,7 +2100,9 @@ public class ResolverActivity extends Activity { CharSequence subLabel = info.getExtendedInfo(); if (TextUtils.equals(label, subLabel)) subLabel = null; - if (!TextUtils.equals(holder.text2.getText(), subLabel)) { + if (!TextUtils.equals(holder.text2.getText(), subLabel) + && !TextUtils.isEmpty(subLabel)) { + holder.text2.setVisibility(View.VISIBLE); holder.text2.setText(subLabel); } @@ -2086,6 +2127,7 @@ public class ResolverActivity extends Activity { public final ComponentName name; private final List<Intent> mIntents = new ArrayList<>(); private final List<ResolveInfo> mResolveInfos = new ArrayList<>(); + private boolean mPinned; public ResolvedComponentInfo(ComponentName name, Intent intent, ResolveInfo info) { this.name = name; @@ -2126,6 +2168,15 @@ public class ResolverActivity extends Activity { } return -1; } + + public boolean isPinned() { + return mPinned; + } + + public void setPinned(boolean pinned) { + mPinned = pinned; + } + } static class ViewHolder { diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index 5f92cddbaa38..7efd5e16392c 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -156,11 +156,22 @@ public class ResolverListController { newInfo.activityInfo.packageName, newInfo.activityInfo.name); final ResolverActivity.ResolvedComponentInfo rci = new ResolverActivity.ResolvedComponentInfo(name, intent, newInfo); + rci.setPinned(isComponentPinned(name)); into.add(rci); } } } + + /** + * Whether this component is pinned by the user. Always false for resolver; overridden in + * Chooser. + */ + public boolean isComponentPinned(ComponentName name) { + return false; + } + + // Filter out any activities that the launched uid does not have permission for. // To preserve the inputList, optionally will return the original list if any modification has // been made. diff --git a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java index a49240cd0019..aec4bfab373c 100644 --- a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java +++ b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java @@ -23,6 +23,7 @@ import android.app.DialogFragment; import android.content.ComponentName; import android.content.DialogInterface; import android.content.Intent; +import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; @@ -36,26 +37,33 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { private static final String NAME_KEY = "componentName"; private static final String TITLE_KEY = "title"; + private static final String PINNED_KEY = "pinned"; // Sync with R.array.resolver_target_actions_* resources - private static final int APP_INFO_INDEX = 0; + private static final int TOGGLE_PIN_INDEX = 0; + private static final int APP_INFO_INDEX = 1; public ResolverTargetActionsDialogFragment() { } - public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name) { + public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name, + boolean pinned) { Bundle args = new Bundle(); args.putCharSequence(TITLE_KEY, title); args.putParcelable(NAME_KEY, name); + args.putBoolean(PINNED_KEY, pinned); setArguments(args); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Bundle args = getArguments(); + final int itemRes = args.getBoolean(PINNED_KEY, false) + ? R.array.resolver_target_actions_unpin + : R.array.resolver_target_actions_pin; return new Builder(getContext()) .setCancelable(true) - .setItems(R.array.resolver_target_actions, this) + .setItems(itemRes, this) .setTitle(args.getCharSequence(TITLE_KEY)) .create(); } @@ -65,6 +73,19 @@ public class ResolverTargetActionsDialogFragment extends DialogFragment final Bundle args = getArguments(); ComponentName name = args.getParcelable(NAME_KEY); switch (which) { + case TOGGLE_PIN_INDEX: + SharedPreferences sp = ChooserActivity.getPinnedSharedPrefs(getContext()); + final String key = name.flattenToString(); + boolean currentVal = sp.getBoolean(name.flattenToString(), false); + if (currentVal) { + sp.edit().remove(key).apply(); + } else { + sp.edit().putBoolean(key, true).apply(); + } + + // Force the chooser to requery and resort things + ((ChooserActivity) getActivity()).handlePackagesChanged(); + break; case APP_INFO_INDEX: Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", name.getPackageName(), null)) diff --git a/core/java/com/android/internal/car/ICarStatsService.aidl b/core/java/com/android/internal/car/ICarStatsService.aidl new file mode 100644 index 000000000000..170b448ba33f --- /dev/null +++ b/core/java/com/android/internal/car/ICarStatsService.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.car; + +import android.os.StatsLogEventWrapper; + +/** + * Interface for pulling statsd atoms from automotive devices. + * + * @hide + */ +interface ICarStatsService { + /** + * Pull the specified atom. Results will be sent to statsd when complete. + */ + StatsLogEventWrapper[] pullData(int atomId); +} diff --git a/core/java/com/android/internal/colorextraction/ColorExtractor.java b/core/java/com/android/internal/colorextraction/ColorExtractor.java index 3003ce80cbc1..f0da0d5decd7 100644 --- a/core/java/com/android/internal/colorextraction/ColorExtractor.java +++ b/core/java/com/android/internal/colorextraction/ColorExtractor.java @@ -73,8 +73,10 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener } mOnColorsChangedListeners = new ArrayList<>(); - wallpaperManager.addOnColorsChangedListener(this, null /* handler */); - initExtractColors(wallpaperManager, immediately); + if (wallpaperManager.isWallpaperSupported()) { + wallpaperManager.addOnColorsChangedListener(this, null /* handler */); + initExtractColors(wallpaperManager, immediately); + } } private void initExtractColors(WallpaperManager wallpaperManager, boolean immediately) { diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 33b532537686..334e61a24193 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -47,6 +47,20 @@ public final class SystemUiDeviceConfigFlags { */ public static final String NAS_MAX_SUGGESTIONS = "nas_max_suggestions"; + // Flags related to screenshot intelligence + + /** + * (bool) Whether to enable smart actions in screenshot notifications. + */ + public static final String ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS = + "enable_screenshot_notification_smart_actions"; + + /** + * (int) Timeout value in ms to get smart actions for screenshot notification. + */ + public static final String SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS = + "screenshot_notification_smart_actions_timeout_ms"; + // Flags related to Smart Suggestions - these are read in SmartReplyConstants. /** (boolean) Whether to enable smart suggestions in notifications. */ diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 183c0fb70e27..2cbf287ab41b 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -1870,6 +1870,7 @@ public class BatteryStatsImpl extends BatteryStats { mCount = computeCurrentCountLocked(); mUnpluggedReportedTotalTime = mCurrentReportedTotalTime = 0; mUnpluggedReportedCount = mCurrentReportedCount = 0; + mTrackingReportedValues = false; } public void setUpdateVersion(int version) { diff --git a/core/java/com/android/internal/util/ConnectivityUtil.java b/core/java/com/android/internal/util/ConnectivityUtil.java new file mode 100644 index 000000000000..b1d4fa0d3fd3 --- /dev/null +++ b/core/java/com/android/internal/util/ConnectivityUtil.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import android.Manifest; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.location.LocationManager; +import android.os.Binder; +import android.os.Build; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + + +/** + * Utility methods for common functionality using by different networks. + * + * @hide + */ +public class ConnectivityUtil { + + private static final String TAG = "ConnectivityUtil"; + + private final Context mContext; + private final AppOpsManager mAppOps; + private final UserManager mUserManager; + + public ConnectivityUtil(Context context) { + mContext = context; + mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + } + + /** + * API to determine if the caller has fine/coarse location permission (depending on + * config/targetSDK level) and the location mode is enabled for the user. SecurityException is + * thrown if the caller has no permission or the location mode is disabled. + * @param pkgName package name of the application requesting access + * @param featureId The feature in the package + * @param uid The uid of the package + * @param message A message describing why the permission was checked. Only needed if this is + * not inside of a two-way binder call from the data receiver + */ + public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid, + @Nullable String message) + throws SecurityException { + checkPackage(uid, pkgName); + + // Location mode must be enabled + if (!isLocationModeEnabled()) { + // Location mode is disabled, scan results cannot be returned + throw new SecurityException("Location mode is disabled for the device"); + } + + // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to + // location information. + boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, featureId, + uid, /* coarseForTargetSdkLessThanQ */ true, message); + + // If neither caller or app has location access, there is no need to check + // any other permissions. Deny access to scan results. + if (!canAppPackageUseLocation) { + throw new SecurityException("UID " + uid + " has no location permission"); + } + // If the User or profile is current, permission is granted + // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. + if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) { + throw new SecurityException("UID " + uid + " profile not permitted"); + } + } + + /** + * Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or + * android.Manifest.permission.ACCESS_COARSE_LOCATION (depending on config/targetSDK level) + * and a corresponding app op is allowed for this package and uid. + * + * @param pkgName PackageName of the application requesting access + * @param featureId The feature in the package + * @param uid The uid of the package + * @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE + * else (false or targetSDK >= Q) then will check for FINE + * @param message A message describing why the permission was checked. Only needed if this is + * not inside of a two-way binder call from the data receiver + */ + public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId, + int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) { + boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid); + + String permissionType = Manifest.permission.ACCESS_FINE_LOCATION; + if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) { + // Having FINE permission implies having COARSE permission (but not the reverse) + permissionType = Manifest.permission.ACCESS_COARSE_LOCATION; + } + if (getUidPermission(permissionType, uid) + == PackageManager.PERMISSION_DENIED) { + return false; + } + + // Always checking FINE - even if will not enforce. This will record the request for FINE + // so that a location request by the app is surfaced to the user. + boolean isFineLocationAllowed = noteAppOpAllowed( + AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid, message); + if (isFineLocationAllowed) { + return true; + } + if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) { + return noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName, featureId, uid, + message); + } + return false; + } + + /** + * Retrieves a handle to LocationManager (if not already done) and check if location is enabled. + */ + public boolean isLocationModeEnabled() { + LocationManager locationManager = + (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); + try { + return locationManager.isLocationEnabledForUser(UserHandle.of( + getCurrentUser())); + } catch (Exception e) { + Log.e(TAG, "Failure to get location mode via API, falling back to settings", e); + return false; + } + } + + private boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) { + long ident = Binder.clearCallingIdentity(); + try { + if (mContext.getPackageManager().getApplicationInfoAsUser( + packageName, 0, + UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion + < versionCode) { + return true; + } + } catch (PackageManager.NameNotFoundException e) { + // In case of exception, assume unknown app (more strict checking) + // Note: This case will never happen since checkPackage is + // called to verify validity before checking App's version. + } finally { + Binder.restoreCallingIdentity(ident); + } + return false; + } + + private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId, + int uid, @Nullable String message) { + return mAppOps.noteOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED; + } + + private void checkPackage(int uid, String pkgName) throws SecurityException { + if (pkgName == null) { + throw new SecurityException("Checking UID " + uid + " but Package Name is Null"); + } + mAppOps.checkPackage(uid, pkgName); + } + + private boolean isCurrentProfile(int uid) { + UserHandle currentUser = UserHandle.of(getCurrentUser()); + UserHandle callingUser = UserHandle.getUserHandleForUid(uid); + return currentUser.equals(callingUser) + || mUserManager.isSameProfileGroup( + currentUser.getIdentifier(), callingUser.getIdentifier()); + } + + private boolean checkInteractAcrossUsersFull(int uid) { + return getUidPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid) + == PackageManager.PERMISSION_GRANTED; + } + + @VisibleForTesting + protected int getCurrentUser() { + return ActivityManager.getCurrentUser(); + } + + private int getUidPermission(String permissionType, int uid) { + // We don't care about pid, pass in -1 + return mContext.checkPermission(permissionType, -1, uid); + } +} diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index cac691cf7d45..d24d78c6f3da 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -6,6 +6,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -58,7 +59,7 @@ public class ScreenshotHelper { */ public void takeScreenshot(final int screenshotType, final boolean hasStatus, final boolean hasNav, @NonNull Handler handler, - @Nullable Consumer<Boolean> completionConsumer) { + @Nullable Consumer<Uri> completionConsumer) { takeScreenshot(screenshotType, hasStatus, hasNav, SCREENSHOT_TIMEOUT_MS, handler, completionConsumer); } @@ -83,12 +84,12 @@ public class ScreenshotHelper { * the screenshot attempt will be cancelled and `completionConsumer` * will be run. * @param handler A handler used in case the screenshot times out - * @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the - * screenshot was taken. + * @param completionConsumer Consumes `null` if a screenshot was not taken, and the URI of the + * screenshot if the screenshot was taken. */ public void takeScreenshot(final int screenshotType, final boolean hasStatus, final boolean hasNav, long timeoutMs, @NonNull Handler handler, - @Nullable Consumer<Boolean> completionConsumer) { + @Nullable Consumer<Uri> completionConsumer) { synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { return; @@ -108,7 +109,7 @@ public class ScreenshotHelper { } } if (completionConsumer != null) { - completionConsumer.accept(false); + completionConsumer.accept(null); } } }; @@ -134,8 +135,9 @@ public class ScreenshotHelper { handler.removeCallbacks(mScreenshotTimeout); } } + if (completionConsumer != null) { - completionConsumer.accept(true); + completionConsumer.accept((Uri) msg.obj); } } }; @@ -148,7 +150,7 @@ public class ScreenshotHelper { } catch (RemoteException e) { Log.e(TAG, "Couldn't take screenshot: " + e); if (completionConsumer != null) { - completionConsumer.accept(false); + completionConsumer.accept(null); } } } diff --git a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp index 173818b13837..d443fd8cdf14 100644 --- a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp +++ b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp @@ -1,6 +1,7 @@ #include "ByteBufferStreamAdaptor.h" #include "core_jni_helpers.h" #include "Utils.h" +#include <jni.h> #include <SkStream.h> @@ -9,6 +10,24 @@ using namespace android; static jmethodID gByteBuffer_getMethodID; static jmethodID gByteBuffer_setPositionMethodID; +/** + * Helper method for accessing the JNI interface pointer. + * + * Image decoding (which this supports) is started on a thread that is already + * attached to the Java VM. But an AnimatedImageDrawable continues decoding on + * the AnimatedImageThread, which is not attached. This will attach if + * necessary. + */ +static JNIEnv* requireEnv(JavaVM* jvm) { + JNIEnv* env; + if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); + } + } + return env; +} + class ByteBufferStream : public SkStreamAsset { private: ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length, @@ -46,7 +65,7 @@ public: } ~ByteBufferStream() override { - auto* env = get_env_or_die(mJvm); + auto* env = requireEnv(mJvm); env->DeleteGlobalRef(mByteBuffer); env->DeleteGlobalRef(mStorage); } @@ -63,7 +82,7 @@ public: return this->setPosition(mPosition + size) ? size : 0; } - auto* env = get_env_or_die(mJvm); + auto* env = requireEnv(mJvm); size_t bytesRead = 0; do { const size_t requested = (size > kStorageSize) ? kStorageSize : size; @@ -146,7 +165,7 @@ private: // Range has already been checked by the caller. bool setPosition(size_t newPosition) { - auto* env = get_env_or_die(mJvm); + auto* env = requireEnv(mJvm); env->CallObjectMethod(mByteBuffer, gByteBuffer_setPositionMethodID, newPosition + mInitialPosition); if (env->ExceptionCheck()) { @@ -185,7 +204,7 @@ public: } ~ByteArrayStream() override { - auto* env = get_env_or_die(mJvm); + auto* env = requireEnv(mJvm); env->DeleteGlobalRef(mByteArray); } @@ -197,7 +216,7 @@ public: return 0; } - auto* env = get_env_or_die(mJvm); + auto* env = requireEnv(mJvm); if (buffer) { env->GetByteArrayRegion(mByteArray, mPosition + mOffset, size, reinterpret_cast<jbyte*>(buffer)); diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index ff14a2acc4d7..ca10c8d5a252 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -488,7 +488,7 @@ static jlong create(JNIEnv* env, jclass clazz, jlong rootNodePtr, jlong surfaceP proxy->setWideGamut(true); } proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer); - proxy->setSurface(surface); + proxy->setSurface(surface, false); // Shadows can't be used via this interface, so just set the light source // to all 0s. proxy->setLightAlpha(0, 0); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 030d3be8a670..b2aef78a79af 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -463,15 +463,6 @@ static void nativeSetInputWindowInfo(JNIEnv* env, jclass clazz, jlong transactio transaction->setInputWindowInfo(ctrl, *handle->getInfo()); } -static void nativeTransferTouchFocus(JNIEnv* env, jclass clazz, jlong transactionObj, - jobject fromTokenObj, jobject toTokenObj) { - auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); - - sp<IBinder> fromToken(ibinderForJavaObject(env, fromTokenObj)); - sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj)); - transaction->transferTouchFocus(fromToken, toToken); -} - static void nativeSyncInputWindows(JNIEnv* env, jclass clazz, jlong transactionObj) { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); transaction->syncInputWindows(); @@ -1381,8 +1372,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeCaptureLayers }, {"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V", (void*)nativeSetInputWindowInfo }, - {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)V", - (void*)nativeTransferTouchFocus }, {"nativeSetMetadata", "(JJILandroid/os/Parcel;)V", (void*)nativeSetMetadata }, {"nativeGetDisplayedContentSamplingAttributes", diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 2b702bacd14e..2cedd847e97d 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2407,8 +2407,13 @@ enum PageId { // OS: Q SETTINGS_AWARE_DISPLAY = 1750; + // OPEN: Settings > System > Input & Gesture > tap gesture + // CATEGORY: SETTINGS + // OS: Q + SETTINGS_GESTURE_TAP = 1751; + // OPEN: Settings > Developer Options > Platform Compat // CATEGORY: SETTINGS // OS: R - SETTINGS_PLATFORM_COMPAT_DASHBOARD = 1805; + SETTINGS_PLATFORM_COMPAT_DASHBOARD = 1805; } diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index ca7131477cdc..3d99b726e6fe 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -385,6 +385,7 @@ message GlobalSettingsProto { optional SettingProto provisioned = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto factory_reset_timeout_millis = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto unsupported_countries = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Euicc euicc = 52; diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 61799eefdca6..ef413b9b04cf 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -211,6 +211,11 @@ message SecureSettingsProto { optional SettingProto silence_timer_touch_count = 11 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto skip_touch_count = 12 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto aware_tap_pause_gesture_count = 13 [ + (android.privacy).dest = + DEST_AUTOMATIC ]; + optional SettingProto aware_tap_pause_touch_count = 14 [ (android.privacy).dest = + DEST_AUTOMATIC ]; } optional Gesture gesture = 74; diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index 28733798b9ad..7452f809a14d 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -232,6 +232,8 @@ message ConstantsProto { // Whether or not TimeController should skip setting wakeup alarms for jobs that aren't // ready now. optional bool skip_not_ready_jobs = 1; + // Whether or not TimeController will use a non-wakeup alarm for delay constraints. + optional bool use_non_wakeup_alarm_for_delay = 2; } optional TimeController time_controller = 25; diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto index 1ec05fb5e9fc..ecb4193a2c6c 100644 --- a/core/proto/android/service/notification.proto +++ b/core/proto/android/service/notification.proto @@ -264,3 +264,14 @@ message ZenPolicyProto { optional Sender priority_calls = 16; optional Sender priority_messages = 17; } + +// Next Tag: 2 +message PackageRemoteViewInfoProto { + optional string package_name = 1; + // add per-package additional info here (like channels) +} + +// Next Tag: 2 +message NotificationRemoteViewsProto { + repeated PackageRemoteViewInfoProto package_remote_view_info = 1; +}
\ No newline at end of file diff --git a/core/proto/android/stats/otaupdate/updateengine_enums.proto b/core/proto/android/stats/otaupdate/updateengine_enums.proto new file mode 100644 index 000000000000..a6e9919ba606 --- /dev/null +++ b/core/proto/android/stats/otaupdate/updateengine_enums.proto @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.stats.otaupdate; + +// The payload type of an OTA update attempt on A/B devices. +enum PayloadType { + FULL = 10000; + DELTA = 10001; +} + +// The attempt result reported by the update engine for an OTA update. +enum AttemptResult { + UPDATE_SUCCEEDED = 10000; + INTERNAL_ERROR = 10001; + PAYLOAD_DOWNLOAD_ERROR = 10002; + METADATA_MALFORMED = 10003; + OPERATION_MALFORMED = 10004; + OPERATION_EXECUTION_ERROR = 10005; + METADATA_VERIFICATION_FAILED = 10006; + PAYLOAD_VERIFICATION_FAILED = 10007; + VERIFICATION_FAILED = 10008; + POSTINSTALL_FAILED = 10009; + ABNORMAL_TERMINATION = 10010; + UPDATE_CANCELED = 10011; + UPDATE_SUCCEEDED_NOT_ACTIVE = 10012; +} + +// The error code reported by the update engine after an OTA update attempt +// on A/B devices. More details in system/update_engine/common/error_code.h +enum ErrorCode { + SUCCESS = 10000; + ERROR = 10001; + FILESYSTEM_COPIER_ERROR = 10004; + POST_INSTALL_RUNNER_ERROR = 10005; + PAYLOAD_MISMATCHED_TYPE_ERROR = 10006; + INSTALL_DEVICE_OPEN_ERROR = 10007; + KERNEL_DEVICE_OPEN_ERROR = 10008; + DOWNLOAD_TRANSFER_ERROR = 10009; + PAYLOAD_HASH_MISMATCH_ERROR = 10010; + PAYLOAD_SIZE_MISMATCH_ERROR = 10011; + DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 10012; + DOWNLOAD_NEW_PARTITION_INFO_ERROR = 10013; + DOWNLOAD_WRITE_ERROR = 10014; + NEW_ROOTFS_VERIFICATION_ERROR = 10015; + SIGNED_DELTA_PAYLOAD_EXPECTED_ERROR = 10017; + DOWNLOAD_PAYLOAD_PUB_KEY_VERIFICATION_ERROR = 10018; + DOWNLOAD_STATE_INITIALIZATION_ERROR = 10020; + DOWNLOAD_INVALID_METADATA_MAGIC_STRING = 10021; + DOWNLOAD_SIGNATURE_MISSING_IN_MANIFEST = 10022; + DOWNLOAD_MANIFEST_PARSE_ERROR = 10023; + DOWNLOAD_METADATA_SIGNATURE_ERROR = 10024; + DOWNLOAD_METADATA_SIGNATURE_VERIFICATION_ERROR = 10025; + DOWNLOAD_METADATA_SIGNATURE_MISMATCH = 10026; + DOWNLOAD_OPERATION_HASH_VERIFICATION_ERROR = 10027; + DOWNLOAD_OPERATION_EXECUTION_ERROR = 10028; + DOWNLOAD_OPERATION_HASH_MISMATCH = 10029; + DOWNLOAD_INVALID_METADATA_SIZE = 10032; + DOWNLOAD_INVALID_METADATA_SIGNATURE = 10033; + DOWNLOAD_OPERATION_HASH_MISSING_ERROR = 10038; + DOWNLOAD_METADATA_SIGNATURE_MISSING_ERROR = 10039; + UNSUPPORTED_MAJOR_PAYLOAD_VERSION = 10044; + UNSUPPORTED_MINOR_PAYLOAD_VERSION = 10045; + FILESYSTEM_VERIFIER_ERROR = 10047; + USER_CANCELED = 10048; + PAYLOAD_TIMESTAMP_ERROR = 10051; + UPDATED_BUT_NOT_ACTIVE = 10052; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5cd717a9058d..818203cc2fa8 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -346,6 +346,10 @@ <protected-broadcast android:name="com.android.server.am.DELETE_DUMPHEAP" /> <protected-broadcast android:name="com.android.server.net.action.SNOOZE_WARNING" /> <protected-broadcast android:name="com.android.server.net.action.SNOOZE_RAPID" /> + <protected-broadcast android:name="com.android.server.wifi.ACTION_SHOW_SET_RANDOMIZATION_DETAILS" /> + <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_ALLOWED_APP" /> + <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISALLOWED_APP" /> + <protected-broadcast android:name="com.android.server.wifi.action.NetworkSuggestion.USER_DISMISSED" /> <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" /> <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" /> <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" /> @@ -2551,7 +2555,7 @@ <!-- Allows telephony to suggest the time / time zone. <p>Not for use by third-party applications. - @hide + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @hide --> <permission android:name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE" android:protectionLevel="signature|telephony" /> diff --git a/core/res/res/drawable/chooser_action_button_bg.xml b/core/res/res/drawable/chooser_action_button_bg.xml new file mode 100644 index 000000000000..a434c0b9b6a9 --- /dev/null +++ b/core/res/res/drawable/chooser_action_button_bg.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="@color/lighter_gray"> + <item> + <inset + android:insetLeft="0dp" + android:insetTop="8dp" + android:insetRight="0dp" + android:insetBottom="8dp"> + <shape android:shape="rectangle"> + <corners android:radius="16dp"></corners> + <stroke android:width="1dp" + android:color="?attr/textColorSecondary" /> + <solid android:color="?attr/colorBackground" /> + </shape> + </inset> + </item> +</ripple> diff --git a/core/res/res/drawable/ic_content_copy_gm2.xml b/core/res/res/drawable/ic_content_copy_gm2.xml deleted file mode 100644 index ee58738b75d0..000000000000 --- a/core/res/res/drawable/ic_content_copy_gm2.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2019 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24"> - <path - android:fillColor="?android:attr/textColorSecondary" - android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/> -</vector> diff --git a/core/res/res/drawable/ic_menu_copy_material.xml b/core/res/res/drawable/ic_menu_copy_material.xml index c03723b1fb33..8c9f1c514b97 100644 --- a/core/res/res/drawable/ic_menu_copy_material.xml +++ b/core/res/res/drawable/ic_menu_copy_material.xml @@ -1,26 +1,27 @@ -<!-- -Copyright (C) 2014 The Android Open Source Project +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. --> + <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0" - android:autoMirrored="true" - android:tint="?attr/colorControlNormal"> - <path - android:pathData="M16,1L4,1C2.9,1 2,1.9 2,3l0,14l2,0L4,3l12,0L16,1zM19,5L8,5C6.9,5 6,5.9 6,7l0,14c0,1.1 0.9,2 2,2l11,0c1.1,0 2,-0.9 2,-2L21,7C21,5.9 20.1,5 19,5zM19,21L8,21L8,7l11,0L19,21z" - android:fillColor="@color/white"/> + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:autoMirrored="true" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@color/white" + android:pathData="M18,21L4,21L4,7L2,7v14c0,1.1 0.9,2 2,2h14v-2zM21,17L21,3c0,-1.1 -0.9,-2 -2,-2L8,1c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2zM19,17L8,17L8,3h11v14z"/> </vector> diff --git a/core/res/res/layout/chooser_action_button.xml b/core/res/res/layout/chooser_action_button.xml new file mode 100644 index 000000000000..119b2e90292d --- /dev/null +++ b/core/res/res/layout/chooser_action_button.xml @@ -0,0 +1,30 @@ +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<Button xmlns:android="http://schemas.android.com/apk/res/android" + android:gravity="center_vertical|start" + android:paddingStart="12dp" + android:paddingEnd="12dp" + android:drawablePadding="8dp" + android:textColor="?android:textColorSecondary" + android:textSize="12sp" + android:maxWidth="192dp" + android:singleLine="true" + android:clickable="true" + android:background="@drawable/chooser_action_button_bg" + android:drawableTint="?android:attr/colorControlNormal" + android:drawableTintMode="src_in" + /> diff --git a/core/res/res/layout/chooser_action_row.xml b/core/res/res/layout/chooser_action_row.xml new file mode 100644 index 000000000000..ea7561124181 --- /dev/null +++ b/core/res/res/layout/chooser_action_row.xml @@ -0,0 +1,26 @@ +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingLeft="@dimen/chooser_edge_margin_normal" + android:paddingRight="@dimen/chooser_edge_margin_normal" + android:gravity="center" + > + +</LinearLayout> diff --git a/core/res/res/layout/chooser_grid_preview_file.xml b/core/res/res/layout/chooser_grid_preview_file.xml index f7d60c91052d..2a39215a4bd8 100644 --- a/core/res/res/layout/chooser_grid_preview_file.xml +++ b/core/res/res/layout/chooser_grid_preview_file.xml @@ -65,13 +65,15 @@ android:gravity="start|top" android:paddingRight="24dp" android:singleLine="true"/> - <Button - android:id="@+id/file_copy_button" - android:layout_width="24dp" - android:layout_height="24dp" - android:gravity="center" - android:layout_gravity="center_vertical" - android:background="@drawable/ic_content_copy_gm2"/> </LinearLayout> + + <include + android:id="@+id/chooser_action_row" + layout="@layout/chooser_action_row" + android:layout_width="@dimen/chooser_preview_width" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/chooser_view_spacing" + android:layout_gravity="center" + /> </LinearLayout> diff --git a/core/res/res/layout/chooser_grid_preview_image.xml b/core/res/res/layout/chooser_grid_preview_image.xml index 79a0de4b271f..62df1650612a 100644 --- a/core/res/res/layout/chooser_grid_preview_image.xml +++ b/core/res/res/layout/chooser_grid_preview_image.xml @@ -78,5 +78,15 @@ android:scaleType="centerCrop"/> </RelativeLayout> + + <include + android:id="@+id/chooser_action_row" + layout="@layout/chooser_action_row" + android:layout_width="@dimen/chooser_preview_width" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/chooser_view_spacing" + android:layout_gravity="center" + /> + </LinearLayout> diff --git a/core/res/res/layout/chooser_grid_preview_text.xml b/core/res/res/layout/chooser_grid_preview_text.xml index 9c725b93eec6..002917463ab3 100644 --- a/core/res/res/layout/chooser_grid_preview_text.xml +++ b/core/res/res/layout/chooser_grid_preview_text.xml @@ -37,50 +37,27 @@ <TextView android:id="@+id/content_preview_text" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentStart="true" - android:layout_toStartOf="@id/copy_button" android:layout_centerVertical="true" android:ellipsize="end" android:fontFamily="@android:string/config_headlineFontFamily" android:textColor="?android:attr/textColorPrimary" - android:maxLines="2"/> - - <LinearLayout - android:id="@+id/copy_button" - android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - android:layout_alignParentEnd="true" - android:layout_marginStart="@dimen/chooser_view_spacing" - android:gravity="center" - android:minWidth="48dp" - android:minHeight="48dp" - android:clickable="true" - style="?attr/borderlessButtonStyle"> - - <ImageView - android:layout_width="24dp" - android:layout_height="24dp" - android:gravity="top|center_horizontal" - android:src="@drawable/ic_content_copy_gm2" /> + android:maxLines="2" + android:focusable="true"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="4dp" - android:gravity="center_horizontal" - android:text="@string/copy" - android:textColor="?android:textColorSecondary" - android:textSize="12sp" - android:maxWidth="72dp" - android:maxLines="2" - android:ellipsize="end" /> - </LinearLayout> </RelativeLayout> + <include + android:id="@+id/chooser_action_row" + layout="@layout/chooser_action_row" + android:layout_width="@dimen/chooser_preview_width" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/chooser_view_spacing" + android:layout_gravity="center" + /> + <!-- Required sub-layout so we can get the nice rounded corners--> <!-- around this section --> <LinearLayout diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml index 0bdb25a8d307..485709523e66 100644 --- a/core/res/res/layout/resolve_list_item.xml +++ b/core/res/res/layout/resolve_list_item.xml @@ -22,8 +22,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?attr/listPreferredItemHeightSmall" - android:paddingTop="4dp" - android:paddingBottom="4dp" android:background="?attr/activatedBackgroundIndicator"> <!-- Activity icon when presenting dialog @@ -32,7 +30,8 @@ android:layout_width="@dimen/resolver_icon_size" android:layout_height="@dimen/resolver_icon_size" android:layout_gravity="start|center_vertical" - android:layout_marginStart="?attr/listPreferredItemPaddingStart" + android:layout_marginStart="@dimen/resolver_icon_margin" + android:layout_marginEnd="@dimen/resolver_icon_margin" android:layout_marginTop="12dp" android:layout_marginBottom="12dp" android:scaleType="fitCenter" /> @@ -40,8 +39,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:gravity="start|center_vertical" android:orientation="vertical" - android:paddingStart="?attr/listPreferredItemPaddingStart" - android:paddingEnd="?attr/listPreferredItemPaddingEnd" + android:paddingEnd="@dimen/resolver_edge_margin" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_gravity="start|center_vertical"> @@ -49,14 +47,20 @@ <TextView android:id="@android:id/text1" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="?attr/textAppearanceMedium" - android:textColor="?attr/textColorPrimary" + android:layout_gravity="start|center_vertical" + android:textColor="?android:attr/textColorPrimary" + android:fontFamily="@android:string/config_bodyFontFamily" + android:textSize="16sp" android:minLines="1" android:maxLines="1" android:ellipsize="marquee" /> <!-- Extended activity info to distinguish between duplicate activity names --> <TextView android:id="@android:id/text2" - android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:attr/textColorSecondary" + android:fontFamily="@android:string/config_bodyFontFamily" + android:layout_gravity="start|center_vertical" + android:textSize="14sp" + android:visibility="gone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:minLines="1" diff --git a/core/res/res/layout/resolver_different_item_header.xml b/core/res/res/layout/resolver_different_item_header.xml index 7d9ffd72870d..0a35edc42329 100644 --- a/core/res/res/layout/resolver_different_item_header.xml +++ b/core/res/res/layout/resolver_different_item_header.xml @@ -22,12 +22,12 @@ android:layout_height="wrap_content" android:layout_alwaysShow="true" android:text="@string/use_a_different_app" - android:minHeight="56dp" - android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorPrimary" + android:fontFamily="@android:string/config_headlineFontFamilyMedium" + android:textSize="16sp" android:gravity="start|center_vertical" - android:paddingStart="16dp" - android:paddingEnd="16dp" - android:paddingTop="8dp" - android:paddingBottom="8dp" - android:elevation="8dp" - /> + android:paddingStart="@dimen/resolver_edge_margin" + android:paddingEnd="@dimen/resolver_edge_margin" + android:paddingTop="@dimen/resolver_small_margin" + android:paddingBottom="@dimen/resolver_edge_margin" + android:elevation="1dp" /> diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml index 1dd420746e8a..6e45e7a4c509 100644 --- a/core/res/res/layout/resolver_list.xml +++ b/core/res/res/layout/resolver_list.xml @@ -29,16 +29,18 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alwaysShow="true" - android:elevation="8dp" - android:background="?attr/colorBackgroundFloating"> + android:elevation="@dimen/resolver_elevation" + android:paddingTop="@dimen/resolver_small_margin" + android:paddingStart="@dimen/resolver_edge_margin" + android:paddingEnd="@dimen/resolver_edge_margin" + android:paddingBottom="@dimen/resolver_edge_margin" + android:background="@drawable/bottomsheet_background"> <TextView android:id="@+id/profile_button" android:layout_width="wrap_content" android:layout_height="48dp" android:layout_marginEnd="8dp" - android:paddingStart="8dp" - android:paddingEnd="8dp" android:visibility="gone" style="?attr/borderlessButtonStyle" android:textAppearance="?attr/textAppearanceButton" @@ -50,36 +52,49 @@ <TextView android:id="@+id/title" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:minHeight="56dp" - android:textAppearance="?attr/textAppearanceMedium" - android:gravity="start|center_vertical" - android:paddingStart="?attr/dialogPreferredPadding" - android:paddingEnd="?attr/dialogPreferredPadding" - android:paddingTop="8dp" android:layout_below="@id/profile_button" android:layout_alignParentStart="true" - android:paddingBottom="8dp" /> + android:textColor="?android:attr/textColorPrimary" + android:fontFamily="@android:string/config_headlineFontFamilyMedium" + android:textSize="16sp" + android:gravity="start|center_vertical" /> </RelativeLayout> + <View + android:layout_alwaysShow="true" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/colorBackgroundFloating" + android:foreground="?attr/dividerVertical" /> <ListView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/resolver_list" android:clipToPadding="false" - android:scrollbarStyle="outsideOverlay" android:background="?attr/colorBackgroundFloating" - android:elevation="8dp" + android:elevation="@dimen/resolver_elevation" android:nestedScrollingEnabled="true" + android:scrollbarStyle="outsideOverlay" android:scrollIndicators="top|bottom" - android:divider="@null" /> + android:divider="?attr/dividerVertical" + android:footerDividersEnabled="false" + android:headerDividersEnabled="false" + android:dividerHeight="1dp" /> + <View + android:layout_alwaysShow="true" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/colorBackgroundFloating" + android:foreground="?attr/dividerVertical" /> + <TextView android:id="@+id/empty" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorBackgroundFloating" - android:elevation="8dp" + android:elevation="@dimen/resolver_elevation" android:layout_alwaysShow="true" android:text="@string/noApplications" android:padding="32dp" @@ -102,18 +117,19 @@ android:background="?attr/colorBackgroundFloating" android:paddingTop="@dimen/resolver_button_bar_spacing" android:paddingBottom="@dimen/resolver_button_bar_spacing" - android:paddingStart="12dp" - android:paddingEnd="12dp" - android:elevation="8dp"> + android:paddingStart="@dimen/resolver_edge_margin" + android:paddingEnd="@dimen/resolver_small_margin" + android:elevation="@dimen/resolver_elevation"> <Button android:id="@+id/button_once" android:layout_width="wrap_content" android:layout_gravity="start" android:maxLines="2" - style="?attr/buttonBarNegativeButtonStyle" - android:minHeight="@dimen/alert_dialog_button_bar_height" + style="?attr/buttonBarButtonStyle" + android:fontFamily="@android:string/config_headlineFontFamilyMedium" android:layout_height="wrap_content" + android:textAllCaps="false" android:enabled="false" android:text="@string/activity_resolver_use_once" android:onClick="onButtonClick" /> @@ -123,8 +139,9 @@ android:layout_width="wrap_content" android:layout_gravity="end" android:maxLines="2" - android:minHeight="@dimen/alert_dialog_button_bar_height" - style="?attr/buttonBarPositiveButtonStyle" + style="?attr/buttonBarButtonStyle" + android:fontFamily="@android:string/config_headlineFontFamilyMedium" + android:textAllCaps="false" android:layout_height="wrap_content" android:enabled="false" android:text="@string/activity_resolver_use_always" diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml index 740a7eb9374e..dbba0b7bcc25 100644 --- a/core/res/res/layout/resolver_list_with_default.xml +++ b/core/res/res/layout/resolver_list_with_default.xml @@ -29,22 +29,22 @@ android:layout_height="wrap_content" android:layout_alwaysShow="true" android:orientation="vertical" - android:background="?attr/colorBackgroundFloating" - android:elevation="8dp"> + android:background="@drawable/bottomsheet_background" + android:paddingTop="@dimen/resolver_small_margin" + android:elevation="@dimen/resolver_elevation"> <LinearLayout android:layout_width="match_parent" - android:layout_height="64dp" - android:orientation="horizontal"> - + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingBottom="@dimen/resolver_edge_margin" + android:paddingEnd="@dimen/resolver_edge_margin"> <ImageView android:id="@+id/icon" - android:layout_width="24dp" - android:layout_height="24dp" + android:layout_width="@dimen/resolver_icon_size" + android:layout_height="@dimen/resolver_icon_size" android:layout_gravity="start|top" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" - android:layout_marginTop="20dp" + android:layout_marginStart="@dimen/resolver_icon_margin" android:src="@drawable/resolver_icon_placeholder" android:scaleType="fitCenter" /> @@ -52,9 +52,11 @@ android:id="@+id/title" android:layout_width="0dp" android:layout_weight="1" - android:layout_height="?attr/listPreferredItemHeight" - android:layout_marginStart="16dp" - android:textAppearance="?attr/textAppearanceMedium" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/resolver_icon_margin" + android:textColor="?android:attr/textColorPrimary" + android:fontFamily="@android:string/config_headlineFontFamilyMedium" + android:textSize="16sp" android:gravity="start|center_vertical" android:paddingEnd="16dp" /> @@ -107,21 +109,22 @@ android:orientation="horizontal" android:layoutDirection="locale" android:measureWithLargestChild="true" - android:paddingTop="8dp" - android:paddingBottom="8dp" - android:paddingStart="12dp" - android:paddingEnd="12dp" - android:elevation="8dp"> + android:paddingTop="@dimen/resolver_button_bar_spacing" + android:paddingBottom="@dimen/resolver_button_bar_spacing" + android:paddingStart="@dimen/resolver_edge_margin" + android:paddingEnd="@dimen/resolver_small_margin" + android:elevation="@dimen/resolver_elevation"> <Button android:id="@+id/button_once" android:layout_width="wrap_content" android:layout_gravity="start" android:maxLines="2" - style="?attr/buttonBarNegativeButtonStyle" - android:minHeight="@dimen/alert_dialog_button_bar_height" + style="?attr/buttonBarButtonStyle" + android:fontFamily="@android:string/config_headlineFontFamilyMedium" android:layout_height="wrap_content" android:enabled="false" + android:textAllCaps="false" android:text="@string/activity_resolver_use_once" android:onClick="onButtonClick" /> @@ -130,29 +133,40 @@ android:layout_width="wrap_content" android:layout_gravity="end" android:maxLines="2" - android:minHeight="@dimen/alert_dialog_button_bar_height" - style="?attr/buttonBarPositiveButtonStyle" + style="?attr/buttonBarButtonStyle" + android:fontFamily="@android:string/config_headlineFontFamilyMedium" android:layout_height="wrap_content" android:enabled="false" + android:textAllCaps="false" android:text="@string/activity_resolver_use_always" android:onClick="onButtonClick" /> </LinearLayout> - - <View - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?attr/dividerVertical" /> </LinearLayout> + <View + android:layout_alwaysShow="true" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/colorBackgroundFloating" + android:foreground="?attr/dividerVertical" /> <ListView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/resolver_list" android:clipToPadding="false" - android:scrollbarStyle="outsideOverlay" android:background="?attr/colorBackgroundFloating" - android:elevation="8dp" + android:elevation="@dimen/resolver_elevation" android:nestedScrollingEnabled="true" - android:divider="@null" /> - + android:scrollbarStyle="outsideOverlay" + android:scrollIndicators="top|bottom" + android:divider="?attr/dividerVertical" + android:footerDividersEnabled="false" + android:headerDividersEnabled="false" + android:dividerHeight="1dp" /> + <View + android:layout_alwaysShow="true" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/colorBackgroundFloating" + android:foreground="?attr/dividerVertical" /> </com.android.internal.widget.ResolverDrawerLayout> diff --git a/core/res/res/values-mcc310-mnc170/config.xml b/core/res/res/values-mcc310-mnc170/config.xml index 26b9192e0cc3..12e448cd6b21 100644 --- a/core/res/res/values-mcc310-mnc170/config.xml +++ b/core/res/res/values-mcc310-mnc170/config.xml @@ -22,5 +22,9 @@ <resources> <!-- Enable 5 bar signal strength icon --> <bool name="config_inflateSignalStrength">true</bool> + + <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing + check after reboot or airplane mode toggling --> + <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool> </resources> diff --git a/core/res/res/values-mcc310-mnc380/config.xml b/core/res/res/values-mcc310-mnc380/config.xml index 26b9192e0cc3..12e448cd6b21 100644 --- a/core/res/res/values-mcc310-mnc380/config.xml +++ b/core/res/res/values-mcc310-mnc380/config.xml @@ -22,5 +22,9 @@ <resources> <!-- Enable 5 bar signal strength icon --> <bool name="config_inflateSignalStrength">true</bool> + + <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing + check after reboot or airplane mode toggling --> + <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool> </resources> diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml index 3fb3f0f7e9ff..22b8fefcecf4 100644 --- a/core/res/res/values-mcc310-mnc410/config.xml +++ b/core/res/res/values-mcc310-mnc410/config.xml @@ -52,4 +52,7 @@ <!-- Enable 5 bar signal strength icon --> <bool name="config_inflateSignalStrength">true</bool> + <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing + check after reboot or airplane mode toggling --> + <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool> </resources> diff --git a/core/res/res/values-mcc310-mnc560/config.xml b/core/res/res/values-mcc310-mnc560/config.xml index 26b9192e0cc3..12e448cd6b21 100644 --- a/core/res/res/values-mcc310-mnc560/config.xml +++ b/core/res/res/values-mcc310-mnc560/config.xml @@ -22,5 +22,9 @@ <resources> <!-- Enable 5 bar signal strength icon --> <bool name="config_inflateSignalStrength">true</bool> + + <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing + check after reboot or airplane mode toggling --> + <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool> </resources> diff --git a/core/res/res/values-mcc311-mnc180/config.xml b/core/res/res/values-mcc311-mnc180/config.xml index 26b9192e0cc3..12e448cd6b21 100644 --- a/core/res/res/values-mcc311-mnc180/config.xml +++ b/core/res/res/values-mcc311-mnc180/config.xml @@ -22,5 +22,9 @@ <resources> <!-- Enable 5 bar signal strength icon --> <bool name="config_inflateSignalStrength">true</bool> + + <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing + check after reboot or airplane mode toggling --> + <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool> </resources> diff --git a/core/res/res/values-mcc313-mnc100/config.xml b/core/res/res/values-mcc313-mnc100/config.xml index ccd03f10616a..a8e481a6a3a4 100644 --- a/core/res/res/values-mcc313-mnc100/config.xml +++ b/core/res/res/values-mcc313-mnc100/config.xml @@ -42,4 +42,8 @@ <item>"#8"</item> <item>"#9"</item> </string-array> + + <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing + check after reboot or airplane mode toggling --> + <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">true</bool> </resources> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index f05898561b8a..dca9c7234e13 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -175,7 +175,15 @@ </array> <!-- Used in ResolverTargetActionsDialogFragment --> - <string-array name="resolver_target_actions"> + + <!-- Used in ResolverTargetActionsDialogFragment --> + <string-array name="resolver_target_actions_pin"> + <item>@string/pin_target</item> + <item>@string/app_info</item> + </string-array> + + <string-array name="resolver_target_actions_unpin"> + <item>@string/unpin_target</item> <item>@string/app_info</item> </string-array> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 17045d812f4b..01fb7d432605 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -352,10 +352,10 @@ will be given a single shared user ID, so they can for example run in the same process. Note that for them to actually get the same user ID, they must also be signed with the same signature. - @deprecated Shared user id's cause non-deterministic behaviour within the - package manager. As such, it's use is discouraged, deprecated, and will - be removed altogether in a future version of Android. Instead, proper - communication mechanisms such as services and providers should be used + @deprecated Shared user IDs cause non-deterministic behavior within the + package manager. As such, its use is strongly discouraged and may be + removed in a future version of Android. Instead, apps should use proper + communication mechanisms, such as services and content providers, to facilitate interoperability between shared components. --> <attr name="sharedUserId" format="string" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 04d1eef9cba1..17bd132985fa 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3235,7 +3235,9 @@ The path is assumed to be specified in display coordinates with pixel units and in the display's native orientation, with the origin of the coordinate system at the - center top of the display. + center top of the display. Optionally, you can append either `@left` or `@right` to the + end of the path string, in order to change the path origin to either the top left, + or top right of the display. To facilitate writing device-independent emulation overlays, the marker `@dp` can be appended after the path string to interpret coordinates in dp instead of px units. @@ -4322,4 +4324,12 @@ create additional screen real estate outside beyond the keyboard. Note that the user needs to have a confirmed way to dismiss the keyboard when desired. --> <bool name="config_automotiveHideNavBarForKeyboard">false</bool> + + <!-- Component name that accepts ACTION_SEND intents for nearby (proximity-based) sharing. + Used by ChooserActivity. --> + <string translatable="false" name="config_defaultNearbySharingComponent"></string> + + <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing + check after reboot or airplane mode toggling --> + <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 609659b62948..4fdb498451ae 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -750,7 +750,7 @@ <dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen> - <!-- chooser (sharesheet) spacing --> + <!-- chooser/resolver (sharesheet) spacing --> <dimen name="chooser_corner_radius">8dp</dimen> <dimen name="chooser_row_text_option_translate">25dp</dimen> <dimen name="chooser_view_spacing">18dp</dimen> @@ -759,11 +759,16 @@ <dimen name="chooser_preview_image_font_size">20sp</dimen> <dimen name="chooser_preview_image_border">1dp</dimen> <dimen name="chooser_preview_width">-1px</dimen> - <dimen name="resolver_icon_size">42dp</dimen> - <dimen name="resolver_button_bar_spacing">8dp</dimen> - <dimen name="resolver_badge_size">18dp</dimen> <dimen name="chooser_target_width">90dp</dimen> <dimen name="chooser_header_scroll_elevation">4dp</dimen> <dimen name="chooser_max_collapsed_height">288dp</dimen> <dimen name="chooser_direct_share_label_placeholder_max_width">72dp</dimen> + <dimen name="resolver_icon_size">32dp</dimen> + <dimen name="resolver_button_bar_spacing">8dp</dimen> + <dimen name="resolver_badge_size">18dp</dimen> + <dimen name="resolver_icon_margin">16dp</dimen> + <dimen name="resolver_small_margin">18dp</dimen> + <dimen name="resolver_edge_margin">24dp</dimen> + <dimen name="resolver_elevation">1dp</dimen> + <dimen name="chooser_action_button_icon_size">18dp</dimen> </resources> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index 2b0c86b49577..ba64bf00c63a 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -193,4 +193,7 @@ <!-- A tag used to save the index where the custom view is stored --> <item type="id" name="notification_custom_view_index_tag" /> + + <!-- Marks the "copy to clipboard" button in the ChooserActivity --> + <item type="id" name="chooser_copy_button" /> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4aa44fc6ebaa..f78a53e294aa 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1045,47 +1045,26 @@ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_readContacts">read your contacts</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_readContacts" product="tablet">Allows the app to read - data about your contacts stored on your tablet, including the frequency - with which you\'ve called, emailed, or communicated in other ways with - specific individuals. This permission allows apps to save your contact - data, and malicious apps may share contact data without your - knowledge.</string> + <string name="permdesc_readContacts" product="tablet">Allows the app to read data about your contacts stored on your tablet. + This permission allows apps to save your contact data, and malicious apps may share contact data without your knowledge.</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_readContacts" product="tv">Allows the app to read - data about your contacts stored on your TV, including the frequency - with which you\'ve called, emailed, or communicated in other ways with - specific individuals. This permission allows apps to save your contact - data, and malicious apps may share contact data without your - knowledge.</string> + <string name="permdesc_readContacts" product="tv">Allows the app to read data about your contacts stored on your TV. + This permission allows apps to save your contact data, and malicious apps may share contact data without your knowledge.</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_readContacts" product="default">Allows the app to - read data about your contacts stored on your phone, including the - frequency with which you\'ve called, emailed, or communicated in other ways - with specific individuals. This permission allows apps to save your - contact data, and malicious apps may share contact data without your - knowledge.</string> + <string name="permdesc_readContacts" product="default">Allows the app to read data about your contacts stored on your phone. + This permission allows apps to save your contact data, and malicious apps may share contact data without your knowledge.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_writeContacts">modify your contacts</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_writeContacts" product="tablet">Allows the app to - modify the data about your contacts stored on your tablet, including the - frequency with which you\'ve called, emailed, or communicated in other ways - with specific contacts. This permission allows apps to delete contact - data.</string> + <string name="permdesc_writeContacts" product="tablet">Allows the app to modify the data about your contacts stored on your tablet. + This permission allows apps to delete contact data.</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_writeContacts" product="tv">Allows the app to - modify the data about your contacts stored on your TV, including the - frequency with which you\'ve called, emailed, or communicated in other ways - with specific contacts. This permission allows apps to delete contact - data.</string> + <string name="permdesc_writeContacts" product="tv">Allows the app to modify the data about your contacts stored on your TV. + This permission allows apps to delete contact data.</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_writeContacts" product="default">Allows the app to - modify the data about your contacts stored on your phone, including the - frequency with which you\'ve called, emailed, or communicated in other ways - with specific contacts. This permission allows apps to delete contact - data.</string> + <string name="permdesc_writeContacts" product="default">Allows the app to modify the data about your contacts stored on your phone. + This permission allows apps to delete contact data.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_readCallLog">read call log</string> @@ -1148,6 +1127,8 @@ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_accessCoarseLocation" product="tv">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when when the app is in the foreground. These location services must be turned on and available on your TV for the app to be able to use them.</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_accessCoarseLocation" product="automotive">This app can get your approximate location only when it is in the foreground. These location services must be turned on and available on your car for the app to be able to use them.</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_accessCoarseLocation" product="default">This app can get your location based on network sources such as cell towers and Wi-Fi networks, but only when the app is in the foreground. These location services must be turned on and available on your phone for the app to be able to use them.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> @@ -3377,6 +3358,26 @@ <!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, t his is shown instead. For example, if the ringtone was on a SD card and it had been removed, this woudl be shown for ringtones on that SD card. --> <string name="ringtone_unknown">Unknown</string> + <!-- Start of string constants used to inform the user that the current network may be failing to connect due to not supporting randomized MAC--> + <!-- Notification title [CHAR_LIMIT=NONE]--> + <string name="wifi_cannot_connect_with_randomized_mac_title">Can\u2019t connect to <xliff:g id="ssid" example="SSID_1">%1$s</xliff:g></string> + <!-- Notification text [CHAR_LIMIT=NONE]--> + <string name="wifi_cannot_connect_with_randomized_mac_message">Tap to change privacy settings and retry</string> + <!-- Title of the dialog which pops up when the notification is tapped. [CHAR_LIMIT=NONE]--> + <string name="wifi_disable_mac_randomization_dialog_title">Change privacy setting?</string> + <!-- Dialog text [CHAR_LIMIT=NONE]--> + <string name="wifi_disable_mac_randomization_dialog_message"><xliff:g id="ssid" example="SSID_1">%1$s</xliff:g> may want to connect using your device MAC address, a unique identifier. This may allow your device\u2019s location to be tracked by nearby devices. + \n\nIf you continue, <xliff:g id="ssid" example="SSID_1">%1$s</xliff:g> will change your privacy setting and try to connect again.</string> + <!-- Text of the button that will disable MAC randomization for the network when tapped. [CHAR_LIMIT=NONE]--> + <string name="wifi_disable_mac_randomization_dialog_confirm_text">Change setting</string> + <!-- Toast message which shows up after MAC randomization for the network is disabled. [CHAR_LIMIT=NONE]--> + <string name="wifi_disable_mac_randomization_dialog_success">Setting updated. Try connecting again.</string> + <!-- Toast message which shows up if the operation failed. [CHAR_LIMIT=NONE]--> + <string name="wifi_disable_mac_randomization_dialog_failure">Can\u2019t change privacy setting</string> + <!-- Toast message which shows up if the network no longer exists on the device. [CHAR_LIMIT=NONE]--> + <string name="wifi_disable_mac_randomization_dialog_network_not_found">Network not found</string> + <!-- End of string constants used to inform the user that the current network may be failing to connect due to not supporting randomized MAC--> + <!-- A notification is shown when there are open wireless networks nearby. This is the notification's title. --> <plurals name="wifi_available"> <item quantity="one">Wi-Fi network available</item> @@ -4852,10 +4853,10 @@ <string name="confirm_battery_saver">OK</string> <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, with a "learn more" link. --> - <string name="battery_saver_description_with_learn_more">Battery Saver turns off or restricts background activity, some visual effects \u0026 other high-power features to extend battery life. <annotation id="url">Learn More</annotation></string> + <string name="battery_saver_description_with_learn_more">To extend battery life, Battery Saver:\n·Turns on Dark theme\n·Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d\n\n<annotation id="url">Learn more</annotation></string> <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, without a "learn more" link. --> - <string name="battery_saver_description">Battery Saver turns off or restricts background activity, some visual effects \u0026 other high-power features to extend battery life.</string> + <string name="battery_saver_description">To extend battery life, Battery Saver:\n·Turns on Dark theme\n·Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d</string> <!-- [CHAR_LIMIT=NONE] Data saver: Feature description --> <string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string> @@ -5085,6 +5086,10 @@ <string name="usb_mtp_launch_notification_description">Tap to view files</string> <!-- Resolver target actions strings --> + <!-- Pin this app to the top of the Sharesheet app list. [CHAR LIMIT=60]--> + <string name="pin_target">Pin</string> + <!-- Un-pin this app in the Sharesheet, so that it is sorted normally. [CHAR LIMIT=60]--> + <string name="unpin_target">Unpin</string> <!-- View application info for a target. --> <string name="app_info">App info</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 383fcd4753f0..32749a202a00 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -66,8 +66,7 @@ <java-symbol type="id" name="content_preview_text_layout" /> <java-symbol type="id" name="content_preview_title" /> <java-symbol type="id" name="content_preview_title_layout" /> - <java-symbol type="id" name="copy_button" /> - <java-symbol type="id" name="file_copy_button" /> + <java-symbol type="id" name="chooser_action_row" /> <java-symbol type="id" name="current_scene" /> <java-symbol type="id" name="scene_layoutid_cache" /> <java-symbol type="id" name="customPanel" /> @@ -2067,6 +2066,14 @@ <java-symbol type="layout" name="safe_mode" /> <java-symbol type="layout" name="simple_list_item_2_single_choice" /> <java-symbol type="layout" name="app_error_dialog" /> + <java-symbol type="string" name="wifi_cannot_connect_with_randomized_mac_title" /> + <java-symbol type="string" name="wifi_cannot_connect_with_randomized_mac_message" /> + <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_title" /> + <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_message" /> + <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_confirm_text" /> + <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_success" /> + <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_failure" /> + <java-symbol type="string" name="wifi_disable_mac_randomization_dialog_network_not_found" /> <java-symbol type="plurals" name="wifi_available" /> <java-symbol type="plurals" name="wifi_available_detailed" /> <java-symbol type="string" name="wifi_available_title" /> @@ -3051,7 +3058,8 @@ <java-symbol type="color" name="notification_material_background_color" /> <!-- Resolver target actions --> - <java-symbol type="array" name="resolver_target_actions" /> + <java-symbol type="array" name="resolver_target_actions_pin" /> + <java-symbol type="array" name="resolver_target_actions_unpin" /> <java-symbol type="array" name="non_removable_euicc_slots" /> @@ -3809,6 +3817,10 @@ <java-symbol type="dimen" name="resolver_icon_size"/> <java-symbol type="dimen" name="resolver_badge_size"/> <java-symbol type="dimen" name="resolver_button_bar_spacing"/> + <java-symbol type="dimen" name="resolver_icon_margin"/> + <java-symbol type="dimen" name="resolver_small_margin"/> + <java-symbol type="dimen" name="resolver_edge_margin"/> + <java-symbol type="dimen" name="resolver_elevation"/> <!-- For DropBox --> <java-symbol type="integer" name="config_dropboxLowPriorityBroadcastRateLimitPeriod" /> @@ -3841,5 +3853,11 @@ <java-symbol type="drawable" name="android_logotype" /> <java-symbol type="layout" name="platlogo_layout" /> + <java-symbol type="id" name="chooser_copy_button" /> + <java-symbol type="layout" name="chooser_action_button" /> + <java-symbol type="dimen" name="chooser_action_button_icon_size" /> + <java-symbol type="string" name="config_defaultNearbySharingComponent" /> + <java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" /> + <java-symbol type="bool" name="reset_geo_fencing_check_after_boot_or_apm" /> </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 628926231c65..2b37a0c1af6b 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1733,6 +1733,7 @@ easier. <item name="colorBackground">@color/background_device_default_light</item> <item name="colorBackgroundFloating">@color/background_device_default_light</item> <item name="layout_gravity">center</item> + <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item> </style> <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification"> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index e60e5555cc9d..83f3c3172a51 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -264,6 +264,7 @@ public class SettingsBackupTest { Settings.Global.ERROR_LOGCAT_PREFIX, Settings.Global.EUICC_PROVISIONED, Settings.Global.EUICC_SUPPORTED_COUNTRIES, + Settings.Global.EUICC_UNSUPPORTED_COUNTRIES, Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS, Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS, Settings.Global.FANCY_IME_ANIMATIONS, @@ -595,7 +596,10 @@ public class SettingsBackupTest { Settings.Secure.ANR_SHOW_BACKGROUND, Settings.Secure.ASSISTANT, Settings.Secure.ASSIST_DISCLOSURE_ENABLED, + Settings.Secure.ASSIST_GESTURE_ENABLED, Settings.Secure.ASSIST_GESTURE_SENSITIVITY, + Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED, + Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE, Settings.Secure.ASSIST_SCREENSHOT_ENABLED, Settings.Secure.ASSIST_STRUCTURE_ENABLED, @@ -723,6 +727,13 @@ public class SettingsBackupTest { Settings.Secure.BIOMETRIC_DEBUG_ENABLED, Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED, + Settings.Secure.AWARE_ENABLED, + Settings.Secure.SKIP_GESTURE, + Settings.Secure.SILENCE_GESTURE, + Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, + Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, + Settings.Secure.TAP_GESTURE, + Settings.Secure.NEARBY_SHARING_COMPONENT, // not user configurable Settings.Secure.FACE_UNLOCK_RE_ENROLL); @Test diff --git a/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java new file mode 100644 index 000000000000..36dd3e4e72b9 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/app/AbstractResolverComparatorTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import static junit.framework.Assert.assertEquals; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ResolveInfo; +import android.os.Message; + +import androidx.test.InstrumentationRegistry; + +import org.junit.Test; + +import java.util.List; + +public class AbstractResolverComparatorTest { + + @Test + public void testPinned() { + ResolverActivity.ResolvedComponentInfo r1 = new ResolverActivity.ResolvedComponentInfo( + new ComponentName("package", "class"), new Intent(), new ResolveInfo() + ); + r1.setPinned(true); + + ResolverActivity.ResolvedComponentInfo r2 = new ResolverActivity.ResolvedComponentInfo( + new ComponentName("zackage", "zlass"), new Intent(), new ResolveInfo() + ); + + Context context = InstrumentationRegistry.getTargetContext(); + AbstractResolverComparator comparator = getTestComparator(context); + + assertEquals("Pinned ranks over unpinned", -1, comparator.compare(r1, r2)); + assertEquals("Unpinned ranks under pinned", 1, comparator.compare(r2, r1)); + } + + + @Test + public void testBothPinned() { + ResolveInfo pmInfo1 = new ResolveInfo(); + pmInfo1.activityInfo = new ActivityInfo(); + pmInfo1.activityInfo.packageName = "aaa"; + + ResolverActivity.ResolvedComponentInfo r1 = new ResolverActivity.ResolvedComponentInfo( + new ComponentName("package", "class"), new Intent(), pmInfo1); + r1.setPinned(true); + + ResolveInfo pmInfo2 = new ResolveInfo(); + pmInfo2.activityInfo = new ActivityInfo(); + pmInfo2.activityInfo.packageName = "zzz"; + ResolverActivity.ResolvedComponentInfo r2 = new ResolverActivity.ResolvedComponentInfo( + new ComponentName("zackage", "zlass"), new Intent(), pmInfo2); + r2.setPinned(true); + + Context context = InstrumentationRegistry.getTargetContext(); + AbstractResolverComparator comparator = getTestComparator(context); + + assertEquals("Both pinned should rank alphabetically", -1, comparator.compare(r1, r2)); + } + + private AbstractResolverComparator getTestComparator(Context context) { + Intent intent = new Intent(); + + AbstractResolverComparator testComparator = + new AbstractResolverComparator(context, intent) { + + @Override + int compare(ResolveInfo lhs, ResolveInfo rhs) { + // Used for testing pinning, so we should never get here --- the overrides should + // determine the result instead. + return 1; + } + + @Override + void doCompute(List<ResolverActivity.ResolvedComponentInfo> targets) {} + + @Override + float getScore(ComponentName name) { + return 0; + } + + @Override + void handleResultMessage(Message message) {} + }; + return testComparator; + } + +} diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index c44b7d81868d..4dfa9bfa7e21 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -469,8 +469,8 @@ public class ChooserActivityTest { .launchActivity(Intent.createChooser(sendIntent, null)); waitForIdle(); - onView(withId(R.id.copy_button)).check(matches(isDisplayed())); - onView(withId(R.id.copy_button)).perform(click()); + onView(withId(R.id.chooser_copy_button)).check(matches(isDisplayed())); + onView(withId(R.id.chooser_copy_button)).perform(click()); ClipboardManager clipboard = (ClipboardManager) activity.getSystemService( Context.CLIPBOARD_SERVICE); ClipData clipData = clipboard.getPrimaryClip(); diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java index 848364584ef3..e16d1caa98eb 100644 --- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java +++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java @@ -19,7 +19,7 @@ package com.android.internal.util; import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION; -import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNull; import static junit.framework.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -80,8 +80,8 @@ public final class ScreenshotHelperTest { CountDownLatch lock = new CountDownLatch(1); mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, false, false, timeoutMs, mHandler, - worked -> { - assertFalse(worked); + uri -> { + assertNull(uri); lock.countDown(); }); diff --git a/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java b/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java new file mode 100644 index 000000000000..556471260141 --- /dev/null +++ b/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.util; + +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.Manifest; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.location.LocationManager; +import android.os.Binder; +import android.os.Build; +import android.os.UserHandle; +import android.os.UserManager; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.HashMap; + +/** Unit tests for {@link ConnectivityUtil}. */ +public class ConnectivityUtilTest { + + public static final String TAG = "ConnectivityUtilTest"; + + // Mock objects for testing + @Mock private Context mMockContext; + @Mock private PackageManager mMockPkgMgr; + @Mock private ApplicationInfo mMockApplInfo; + @Mock private AppOpsManager mMockAppOps; + @Mock private UserManager mMockUserManager; + @Mock private LocationManager mLocationManager; + + private static final String TEST_PKG_NAME = "com.google.somePackage"; + private static final String TEST_FEATURE_ID = "com.google.someFeature"; + private static final int MANAGED_PROFILE_UID = 1100000; + private static final int OTHER_USER_UID = 1200000; + + private final String mInteractAcrossUsersFullPermission = + "android.permission.INTERACT_ACROSS_USERS_FULL"; + private final String mManifestStringCoarse = + Manifest.permission.ACCESS_COARSE_LOCATION; + private final String mManifestStringFine = + Manifest.permission.ACCESS_FINE_LOCATION; + + // Test variables + private int mWifiScanAllowApps; + private int mUid; + private int mCoarseLocationPermission; + private int mAllowCoarseLocationApps; + private int mFineLocationPermission; + private int mAllowFineLocationApps; + private int mCurrentUser; + private boolean mIsLocationEnabled; + private boolean mThrowSecurityException; + private Answer<Integer> mReturnPermission; + private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>(); + + private class TestConnectivityUtil extends ConnectivityUtil { + + TestConnectivityUtil(Context context) { + super(context); + } + + @Override + protected int getCurrentUser() { + return mCurrentUser; + } + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + initTestVars(); + } + + private void setupMocks() throws Exception { + when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PKG_NAME), eq(0), any())) + .thenReturn(mMockApplInfo); + when(mMockContext.getPackageManager()).thenReturn(mMockPkgMgr); + when(mMockAppOps.noteOp(AppOpsManager.OPSTR_WIFI_SCAN, mUid, TEST_PKG_NAME)) + .thenReturn(mWifiScanAllowApps); + when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_COARSE_LOCATION), eq(mUid), + eq(TEST_PKG_NAME))) + .thenReturn(mAllowCoarseLocationApps); + when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(mUid), + eq(TEST_PKG_NAME))) + .thenReturn(mAllowFineLocationApps); + if (mThrowSecurityException) { + doThrow(new SecurityException("Package " + TEST_PKG_NAME + " doesn't belong" + + " to application bound to user " + mUid)) + .when(mMockAppOps).checkPackage(mUid, TEST_PKG_NAME); + } + when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)) + .thenReturn(mMockAppOps); + when(mMockContext.getSystemService(Context.USER_SERVICE)) + .thenReturn(mMockUserManager); + when(mMockContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager); + } + + private void setupTestCase() throws Exception { + setupMocks(); + setupMockInterface(); + } + + private void initTestVars() { + mPermissionsList.clear(); + mReturnPermission = createPermissionAnswer(); + mWifiScanAllowApps = AppOpsManager.MODE_ERRORED; + mUid = OTHER_USER_UID; + mThrowSecurityException = true; + mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M; + mIsLocationEnabled = false; + mCurrentUser = UserHandle.USER_SYSTEM; + mCoarseLocationPermission = PackageManager.PERMISSION_DENIED; + mFineLocationPermission = PackageManager.PERMISSION_DENIED; + mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED; + mAllowFineLocationApps = AppOpsManager.MODE_ERRORED; + } + + private void setupMockInterface() { + Binder.restoreCallingIdentity((((long) mUid) << 32) | Binder.getCallingPid()); + doAnswer(mReturnPermission).when(mMockContext).checkPermission( + anyString(), anyInt(), anyInt()); + when(mMockUserManager.isSameProfileGroup(UserHandle.SYSTEM.getIdentifier(), + UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID).getIdentifier())) + .thenReturn(true); + when(mMockContext.checkPermission(mManifestStringCoarse, -1, mUid)) + .thenReturn(mCoarseLocationPermission); + when(mMockContext.checkPermission(mManifestStringFine, -1, mUid)) + .thenReturn(mFineLocationPermission); + when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled); + } + + private Answer<Integer> createPermissionAnswer() { + return new Answer<Integer>() { + @Override + public Integer answer(InvocationOnMock invocation) { + int myUid = (int) invocation.getArguments()[1]; + String myPermission = (String) invocation.getArguments()[0]; + mPermissionsList.get(myPermission); + if (mPermissionsList.containsKey(myPermission)) { + int uid = mPermissionsList.get(myPermission); + if (myUid == uid) { + return PackageManager.PERMISSION_GRANTED; + } + } + return PackageManager.PERMISSION_DENIED; + } + }; + } + + @Test + public void testEnforceLocationPermission_HasAllPermissions_BeforeQ() throws Exception { + mIsLocationEnabled = true; + mThrowSecurityException = false; + mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED; + mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED; + mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; + mUid = mCurrentUser; + setupTestCase(); + new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null); + } + + @Test + public void testEnforceLocationPermission_HasAllPermissions_AfterQ() throws Exception { + mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q; + mIsLocationEnabled = true; + mThrowSecurityException = false; + mUid = mCurrentUser; + mFineLocationPermission = PackageManager.PERMISSION_GRANTED; + mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED; + mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; + setupTestCase(); + new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null); + } + + @Test + public void testEnforceLocationPermission_PkgNameAndUidMismatch() throws Exception { + mThrowSecurityException = true; + mIsLocationEnabled = true; + mFineLocationPermission = PackageManager.PERMISSION_GRANTED; + mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED; + mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; + setupTestCase(); + + assertThrows(SecurityException.class, + () -> new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + } + + @Test + public void testenforceCanAccessScanResults_UserOrProfileNotCurrent() throws Exception { + mIsLocationEnabled = true; + mThrowSecurityException = false; + mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED; + mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED; + mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; + setupTestCase(); + + assertThrows(SecurityException.class, + () -> new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + } + + @Test + public void testenforceCanAccessScanResults_NoCoarseLocationPermission() throws Exception { + mThrowSecurityException = false; + mIsLocationEnabled = true; + setupTestCase(); + assertThrows(SecurityException.class, + () -> new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + } + + @Test + public void testenforceCanAccessScanResults_NoFineLocationPermission() throws Exception { + mThrowSecurityException = false; + mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q; + mIsLocationEnabled = true; + mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED; + mAllowFineLocationApps = AppOpsManager.MODE_ERRORED; + mUid = MANAGED_PROFILE_UID; + setupTestCase(); + + assertThrows(SecurityException.class, + () -> new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString()); + } + + @Test + public void testenforceCanAccessScanResults_LocationModeDisabled() throws Exception { + mThrowSecurityException = false; + mUid = MANAGED_PROFILE_UID; + mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; + mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid); + mIsLocationEnabled = false; + + setupTestCase(); + + assertThrows(SecurityException.class, + () -> new TestConnectivityUtil(mMockContext) + .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + } + + private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) { + try { + r.run(); + Assert.fail("Expected " + exceptionClass + " to be thrown."); + } catch (Exception exception) { + assertTrue(exceptionClass.isInstance(exception)); + } + } +} diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index a305d48c4633..1d735674d72d 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -22,7 +22,6 @@ <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> <permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/> <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/> - <permission name="android.permission.CONNECTIVITY_INTERNAL"/> <permission name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"/> <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/> <permission name="android.permission.CONTROL_VPN"/> @@ -38,6 +37,7 @@ <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/> <permission name="android.permission.MODIFY_PHONE_STATE"/> <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> + <permission name="android.permission.OBSERVE_NETWORK_POLICY"/> <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/> <permission name="android.permission.READ_DREAM_STATE"/> <permission name="android.permission.READ_FRAME_BUFFER"/> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 3477aedefacf..a818119f8103 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -240,6 +240,7 @@ applications that come with the platform <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/> <permission name="android.permission.TETHER_PRIVILEGED"/> <permission name="android.permission.UPDATE_APP_OPS_STATS"/> + <permission name="android.permission.UPDATE_DEVICE_STATS"/> </privapp-permissions> <privapp-permissions package="com.android.server.telecom"> diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index d03472856cf4..9a0ca3e4ad9b 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -2095,9 +2095,11 @@ public class Canvas extends BaseCanvas { /** * Draw the text, with origin at (x,y), using the specified paint, along the specified path. The - * paint's Align setting determins where along the path to start the text. + * paint's Align setting determines where along the path to start the text. * * @param text The text to be drawn + * @param index The starting index within the text to be drawn + * @param count Starting from index, the number of characters to draw * @param path The path the text should follow for its baseline * @param hOffset The distance along the path to add to the text's starting position * @param vOffset The distance above(-) or below(+) the path to position the text @@ -2110,7 +2112,7 @@ public class Canvas extends BaseCanvas { /** * Draw the text, with origin at (x,y), using the specified paint, along the specified path. The - * paint's Align setting determins where along the path to start the text. + * paint's Align setting determines where along the path to start the text. * * @param text The text to be drawn * @param path The path the text should follow for its baseline diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index dcb669d84272..3b586242e5b1 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1114,7 +1114,7 @@ public class Paint { * Return the width for stroking. * <p /> * A value of 0 strokes in hairline mode. - * Hairlines always draws a single pixel independent of the canva's matrix. + * Hairlines always draws a single pixel independent of the canvas's matrix. * * @return the paint's stroke width, used whenever the paint's style is * Stroke or StrokeAndFill. @@ -1126,7 +1126,7 @@ public class Paint { /** * Set the width for stroking. * Pass 0 to stroke in hairline mode. - * Hairlines always draws a single pixel independent of the canva's matrix. + * Hairlines always draws a single pixel independent of the canvas's matrix. * * @param width set the paint's stroke width, used whenever the paint's * style is Stroke or StrokeAndFill. @@ -1958,8 +1958,8 @@ public class Paint { * <code> * Paint paint = new Paint(); * paint.setStartHyphenEdit(Paint.START_HYPHEN_EDIT_INSERT_HYPHEN); - * paint.measureText("abc", 0, 3); // Returns the width of "‐abc" - * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "‐abc" + * paint.measureText("abc", 0, 3); // Returns the width of "-abc" + * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "-abc" * </code> * </pre> * @@ -1985,8 +1985,8 @@ public class Paint { * <code> * Paint paint = new Paint(); * paint.setEndHyphenEdit(Paint.END_HYPHEN_EDIT_INSERT_HYPHEN); - * paint.measureText("abc", 0, 3); // Returns the width of "abc‐" - * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "abc‐" + * paint.measureText("abc", 0, 3); // Returns the width of "abc-" + * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "abc-" * </code> * </pre> * diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 4c08a3829934..98de9c3ea88e 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -25,11 +25,6 @@ cc_defaults { // GCC false-positives on this warning, and since we -Werror that's // a problem "-Wno-free-nonheap-object", - - // Clang is producing non-determistic binary when the new pass manager is - // enabled. Disable the new PM as a temporary workaround. - // b/142372146 - "-fno-experimental-new-pass-manager", ], include_dirs: [ diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 9898a1c30856..827cced2883b 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -143,13 +143,15 @@ void CanvasContext::destroy() { mAnimationContext->destroy(); } -void CanvasContext::setSurface(sp<Surface>&& surface) { +void CanvasContext::setSurface(sp<Surface>&& surface, bool enableTimeout) { ATRACE_CALL(); if (surface) { mNativeSurface = new ReliableSurface{std::move(surface)}; - // TODO: Fix error handling & re-shorten timeout - mNativeSurface->setDequeueTimeout(4000_ms); + if (enableTimeout) { + // TODO: Fix error handling & re-shorten timeout + mNativeSurface->setDequeueTimeout(4000_ms); + } } else { mNativeSurface = nullptr; } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 982c087b031a..a0233ca357aa 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -110,7 +110,7 @@ public: // Won't take effect until next EGLSurface creation void setSwapBehavior(SwapBehavior swapBehavior); - void setSurface(sp<Surface>&& surface); + void setSurface(sp<Surface>&& surface, bool enableTimeout = true); bool pauseSurface(); void setStopped(bool stopped); bool hasSurface() const { return mNativeSurface.get(); } diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 1a1b9dac37f6..edb82f4db16d 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -82,9 +82,10 @@ void RenderProxy::setName(const char* name) { mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); }); } -void RenderProxy::setSurface(const sp<Surface>& surface) { - mRenderThread.queue().post( - [this, surf = surface]() mutable { mContext->setSurface(std::move(surf)); }); +void RenderProxy::setSurface(const sp<Surface>& surface, bool enableTimeout) { + mRenderThread.queue().post([this, surf = surface, enableTimeout]() mutable { + mContext->setSurface(std::move(surf), enableTimeout); + }); } void RenderProxy::allocateBuffers() { diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index a0f08cbd26f9..76cd0ee2a2ce 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -69,7 +69,7 @@ public: ANDROID_API bool loadSystemProperties(); ANDROID_API void setName(const char* name); - ANDROID_API void setSurface(const sp<Surface>& surface); + ANDROID_API void setSurface(const sp<Surface>& surface, bool enableTimeout = true); ANDROID_API void allocateBuffers(); ANDROID_API bool pause(); ANDROID_API void setStopped(bool stopped); diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index 6f12c78e8621..efee09581383 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -158,7 +158,10 @@ public class Location implements Parcelable { * <p>By default time, latitude and longitude are 0, and the location * has no bearing, altitude, speed, accuracy or extras. * - * @param provider the name of the provider that generated this location + * @param provider the source that provides the location. It can be of type + * {@link LocationManager#GPS_PROVIDER}, {@link LocationManager#NETWORK_PROVIDER}, + * or {@link LocationManager#PASSIVE_PROVIDER}. You can also define your own + * provider string, in which case an empty string is a valid provider. */ public Location(String provider) { mProvider = provider; diff --git a/location/java/android/location/OnNmeaMessageListener.java b/location/java/android/location/OnNmeaMessageListener.java index ccf6ce854317..05647bc5237b 100644 --- a/location/java/android/location/OnNmeaMessageListener.java +++ b/location/java/android/location/OnNmeaMessageListener.java @@ -28,7 +28,9 @@ public interface OnNmeaMessageListener { /** * Called when an NMEA message is received. * @param message NMEA message - * @param timestamp milliseconds since January 1, 1970. + * @param timestamp Date and time of the location fix, as reported by the GNSS + * chipset. The value is specified in milliseconds since 0:00 + * UTC 1 January 1970. */ void onNmeaMessage(String message, long timestamp); } diff --git a/media/Android.bp b/media/Android.bp index d7cd054ddc3a..43635684d9b3 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -33,6 +33,8 @@ java_library { "framework_media_annotation", "android_system_stubs_current", ], + + plugins: ["java_api_finder"], } filegroup { diff --git a/media/OWNERS b/media/OWNERS index 8bd037a14150..be605831a24b 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -5,6 +5,7 @@ elaurent@google.com etalvala@google.com gkasten@google.com hdmoon@google.com +hkuang@google.com hunga@google.com insun@google.com jaewan@google.com diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 32eec0054d9b..94d32c63edb1 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -908,6 +908,7 @@ public class AudioManager { /** @hide */ @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setMasterMute(boolean mute, int flags) { final IAudioService service = getService(); try { diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index b41792710cbf..aea7f333183f 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -1345,7 +1345,9 @@ public class ExifInterface { private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private boolean mHasThumbnail; - // The following values used for indicating a thumbnail position. + private boolean mHasThumbnailStrips; + private boolean mAreThumbnailStripsConsecutive; + // Used to indicate the position of the thumbnail (includes offset to EXIF data segment). private int mThumbnailOffset; private int mThumbnailLength; private byte[] mThumbnailBytes; @@ -2043,10 +2045,12 @@ public class ExifInterface { /** * Returns the offset and length of thumbnail inside the image file, or - * {@code null} if there is no thumbnail. + * {@code null} if either there is no thumbnail or the thumbnail bytes are stored + * non-consecutively. * * @return two-element array, the offset in the first value, and length in - * the second, or {@code null} if no thumbnail was found. + * the second, or {@code null} if no thumbnail was found or the thumbnail strips are + * not placed consecutively. * @throws IllegalStateException if {@link #saveAttributes()} has been * called since the underlying file was initially parsed, since * that means offsets may have changed. @@ -2058,10 +2062,12 @@ public class ExifInterface { } if (mHasThumbnail) { + if (mHasThumbnailStrips && !mAreThumbnailStripsConsecutive) { + return null; + } return new long[] { mThumbnailOffset, mThumbnailLength }; - } else { - return null; } + return null; } /** @@ -2536,10 +2542,9 @@ public class ExifInterface { final byte[] value = Arrays.copyOfRange(bytes, IDENTIFIER_EXIF_APP1.length, bytes.length); - readExifSegment(value, imageType); - // Save offset values for createJpegThumbnailBitmap() function mExifOffset = (int) offset; + readExifSegment(value, imageType); } else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) { // See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6 final long offset = start + IDENTIFIER_XMP_APP1.length; @@ -2843,6 +2848,8 @@ public class ExifInterface { if (in.read(bytes) != length) { throw new IOException("Can't read exif"); } + // Save offset values for handling thumbnail and attribute offsets. + mExifOffset = offset; readExifSegment(bytes, IFD_TYPE_PRIMARY); } @@ -2988,7 +2995,7 @@ public class ExifInterface { // Write EXIF APP1 segment dataOutputStream.writeByte(MARKER); dataOutputStream.writeByte(MARKER_APP1); - writeExifSegment(dataOutputStream, 6); + writeExifSegment(dataOutputStream); byte[] bytes = new byte[4096]; @@ -3319,7 +3326,7 @@ public class ExifInterface { continue; } - final int bytesOffset = dataInputStream.peek(); + final int bytesOffset = dataInputStream.peek() + mExifOffset; final byte[] bytes = new byte[(int) byteCount]; dataInputStream.readFully(bytes); ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, @@ -3451,31 +3458,28 @@ public class ExifInterface { // The following code limits the size of thumbnail size not to overflow EXIF data area. thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset); - if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF - || mMimeType == IMAGE_TYPE_RW2) { - thumbnailOffset += mExifOffset; - } else if (mMimeType == IMAGE_TYPE_ORF) { + if (mMimeType == IMAGE_TYPE_ORF) { // Update offset value since RAF files have IFD data preceding MakerNote data. thumbnailOffset += mOrfMakerNoteOffset; } - if (DEBUG) { - Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset - + ", length: " + thumbnailLength); - } if (thumbnailOffset > 0 && thumbnailLength > 0) { mHasThumbnail = true; - mThumbnailOffset = thumbnailOffset; + mThumbnailOffset = thumbnailOffset + mExifOffset; mThumbnailLength = thumbnailLength; mThumbnailCompression = DATA_JPEG; if (mFilename == null && mAssetInputStream == null && mSeekableFileDescriptor == null) { // Save the thumbnail in memory if the input doesn't support reading again. - byte[] thumbnailBytes = new byte[thumbnailLength]; - in.seek(thumbnailOffset); + byte[] thumbnailBytes = new byte[mThumbnailLength]; + in.seek(mThumbnailOffset); in.readFully(thumbnailBytes); mThumbnailBytes = thumbnailBytes; } + if (DEBUG) { + Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset + + ", length: " + thumbnailLength); + } } } } @@ -3494,12 +3498,16 @@ public class ExifInterface { long[] stripByteCounts = convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder)); - if (stripOffsets == null) { - Log.w(TAG, "stripOffsets should not be null."); + if (stripOffsets == null || stripOffsets.length == 0) { + Log.w(TAG, "stripOffsets should not be null or have zero length."); return; } - if (stripByteCounts == null) { - Log.w(TAG, "stripByteCounts should not be null."); + if (stripByteCounts == null || stripByteCounts.length == 0) { + Log.w(TAG, "stripByteCounts should not be null or have zero length."); + return; + } + if (stripOffsets.length != stripByteCounts.length) { + Log.w(TAG, "stripOffsets and stripByteCounts should have same length."); return; } @@ -3509,10 +3517,18 @@ public class ExifInterface { int bytesRead = 0; int bytesAdded = 0; + mHasThumbnail = mHasThumbnailStrips = mAreThumbnailStripsConsecutive = true; for (int i = 0; i < stripOffsets.length; i++) { int stripOffset = (int) stripOffsets[i]; int stripByteCount = (int) stripByteCounts[i]; + // Check if strips are consecutive + // TODO: Add test for non-consecutive thumbnail image + if (i < stripOffsets.length - 1 + && stripOffset + stripByteCount != stripOffsets[i + 1]) { + mAreThumbnailStripsConsecutive = false; + } + // Skip to offset int skipBytes = stripOffset - bytesRead; if (skipBytes < 0) { @@ -3531,10 +3547,13 @@ public class ExifInterface { stripBytes.length); bytesAdded += stripBytes.length; } - - mHasThumbnail = true; mThumbnailBytes = totalStripBytes; - mThumbnailLength = totalStripBytes.length; + + if (mAreThumbnailStripsConsecutive) { + // Need to add mExifOffset, which is the offset to the EXIF data segment + mThumbnailOffset = (int) stripOffsets[0] + mExifOffset; + mThumbnailLength = totalStripBytes.length; + } } } @@ -3691,8 +3710,7 @@ public class ExifInterface { } // Writes an Exif segment into the given output stream. - private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream, - int exifOffsetFromBeginning) throws IOException { + private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream) throws IOException { // The following variables are for calculating each IFD tag group size in bytes. int[] ifdOffsets = new int[EXIF_TAGS.length]; int[] ifdDataSizes = new int[EXIF_TAGS.length]; @@ -3751,6 +3769,8 @@ public class ExifInterface { } // Calculate IFD offsets. + // 8 bytes are for TIFF headers: 2 bytes (byte order) + 2 bytes (identifier) + 4 bytes + // (offset of IFDs) int position = 8; for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { if (!mAttributes[ifdType].isEmpty()) { @@ -3762,7 +3782,8 @@ public class ExifInterface { int thumbnailOffset = position; mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name, ExifAttribute.createULong(thumbnailOffset, mExifByteOrder)); - mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset; + // Need to add mExifOffset, which is the offset to the EXIF data segment + mThumbnailOffset = thumbnailOffset + mExifOffset; position += mThumbnailLength; } diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java index b5acbd457db0..79b861136b64 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -394,7 +394,7 @@ public abstract class Image implements AutoCloseable { * <p>The row stride for this color plane, in bytes.</p> * * <p>This is the distance between the start of two consecutive rows of - * pixels in the image. Note that row stried is undefined for some formats + * pixels in the image. Note that row stride is undefined for some formats * such as * {@link android.graphics.ImageFormat#RAW_PRIVATE RAW_PRIVATE}, * and calling getRowStride on images of these formats will diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index f813d1b68419..7bc2b31b16fa 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -192,13 +192,15 @@ public class ImageWriter implements AutoCloseable { mMaxImages = maxImages; - if (format == ImageFormat.UNKNOWN) { - format = SurfaceUtils.getSurfaceFormat(surface); - } // Note that the underlying BufferQueue is working in synchronous mode // to avoid dropping any buffers. mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format); + // nativeInit internally overrides UNKNOWN format. So does surface format query after + // nativeInit and before getEstimatedNativeAllocBytes(). + if (format == ImageFormat.UNKNOWN) { + format = SurfaceUtils.getSurfaceFormat(surface); + } // Estimate the native buffer allocation size and register it so it gets accounted for // during GC. Note that this doesn't include the buffers required by the buffer queue // itself and the buffers requested by the producer. diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index e702f19903d0..0e8cc8d59549 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -362,7 +362,8 @@ import java.util.concurrent.locks.ReentrantLock; </tr> <tr> <td>FLAC</td> - <td>mandatory metadata block (called the STREAMINFO block),<br> + <td>"fLaC", the FLAC stream marker in ASCII,<br> + followed by the STREAMINFO block (the mandatory metadata block),<br> optionally followed by any number of other metadata blocks</td> <td class=NA>Not Used</td> <td class=NA>Not Used</td> diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java index 56e5566df29c..77596a5de815 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java +++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java @@ -22,7 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.hardware.soundtrigger.IRecognitionStatusCallback; import android.hardware.soundtrigger.SoundTrigger; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java index ada77c53bb34..3f0aec63283c 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerManager.java +++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java @@ -23,7 +23,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.hardware.soundtrigger.SoundTrigger; diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index 1b9cac0c8c99..377b2bc19c6b 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -20,7 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.StringRes; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 5c11ed9bb7b4..7fbb3376d5fb 100755 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -22,9 +22,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager; import android.app.Service; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; import android.graphics.PixelFormat; diff --git a/media/java/android/mtp/MtpPropertyList.java b/media/java/android/mtp/MtpPropertyList.java index 557f099c25c1..53d838d84518 100644 --- a/media/java/android/mtp/MtpPropertyList.java +++ b/media/java/android/mtp/MtpPropertyList.java @@ -16,7 +16,8 @@ package android.mtp; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import java.util.ArrayList; import java.util.List; diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java index 65d0fef74b25..d385816c6f86 100644 --- a/media/java/android/mtp/MtpStorage.java +++ b/media/java/android/mtp/MtpStorage.java @@ -16,7 +16,7 @@ package android.mtp; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.storage.StorageVolume; import android.provider.MediaStore; diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java index 86a1076af122..06adf30a8303 100644 --- a/media/java/android/service/media/MediaBrowserService.java +++ b/media/java/android/service/media/MediaBrowserService.java @@ -21,8 +21,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.UnsupportedAppUsage; import android.app.Service; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; diff --git a/media/mca/effect/java/android/media/effect/SingleFilterEffect.java b/media/mca/effect/java/android/media/effect/SingleFilterEffect.java index dfbf5d20e074..121443f56285 100644 --- a/media/mca/effect/java/android/media/effect/SingleFilterEffect.java +++ b/media/mca/effect/java/android/media/effect/SingleFilterEffect.java @@ -17,12 +17,11 @@ package android.media.effect; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.filterfw.core.Filter; import android.filterfw.core.FilterFactory; import android.filterfw.core.FilterFunction; import android.filterfw.core.Frame; -import android.media.effect.EffectContext; /** * Effect subclass for effects based on a single Filter. Subclasses need only invoke the diff --git a/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java b/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java index 52615bf09faa..3a7f1ed4f7ec 100644 --- a/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java +++ b/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java @@ -17,11 +17,11 @@ package android.filterfw; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.filterfw.core.AsyncRunner; -import android.filterfw.core.FilterGraph; import android.filterfw.core.FilterContext; +import android.filterfw.core.FilterGraph; import android.filterfw.core.FrameManager; import android.filterfw.core.GraphRunner; import android.filterfw.core.RoundRobinScheduler; diff --git a/media/mca/filterfw/java/android/filterfw/core/Filter.java b/media/mca/filterfw/java/android/filterfw/core/Filter.java index 4f56b923f6ed..a608ef5be3f4 100644 --- a/media/mca/filterfw/java/android/filterfw/core/Filter.java +++ b/media/mca/filterfw/java/android/filterfw/core/Filter.java @@ -17,19 +17,15 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.FilterContext; -import android.filterfw.core.FilterPort; -import android.filterfw.core.KeyValueMap; -import android.filterfw.io.TextGraphReader; -import android.filterfw.io.GraphIOException; +import android.compat.annotation.UnsupportedAppUsage; import android.filterfw.format.ObjectFormat; +import android.filterfw.io.GraphIOException; +import android.filterfw.io.TextGraphReader; import android.util.Log; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.Field; -import java.lang.Thread; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterContext.java b/media/mca/filterfw/java/android/filterfw/core/FilterContext.java index a19220ef85f8..6b0a2193dceb 100644 --- a/media/mca/filterfw/java/android/filterfw/core/FilterContext.java +++ b/media/mca/filterfw/java/android/filterfw/core/FilterContext.java @@ -17,11 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.Filter; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameManager; -import android.filterfw.core.GLEnvironment; +import android.compat.annotation.UnsupportedAppUsage; import java.util.HashMap; import java.util.HashSet; diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java index e6ca11ffca3c..35a298fd6dfb 100644 --- a/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java +++ b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java @@ -17,6 +17,11 @@ package android.filterfw.core; +import android.compat.annotation.UnsupportedAppUsage; +import android.filterpacks.base.FrameBranch; +import android.filterpacks.base.NullFilter; +import android.util.Log; + import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -25,14 +30,6 @@ import java.util.Map.Entry; import java.util.Set; import java.util.Stack; -import android.filterfw.core.FilterContext; -import android.filterfw.core.KeyValueMap; -import android.filterpacks.base.FrameBranch; -import android.filterpacks.base.NullFilter; - -import android.annotation.UnsupportedAppUsage; -import android.util.Log; - /** * @hide */ diff --git a/media/mca/filterfw/java/android/filterfw/core/Frame.java b/media/mca/filterfw/java/android/filterfw/core/Frame.java index e880783247a3..c4d935ae4873 100644 --- a/media/mca/filterfw/java/android/filterfw/core/Frame.java +++ b/media/mca/filterfw/java/android/filterfw/core/Frame.java @@ -17,9 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.FrameManager; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Bitmap; import java.nio.ByteBuffer; diff --git a/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java b/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java index eb0ff0a32c3f..a87e9b9ffbcf 100644 --- a/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java +++ b/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java @@ -17,9 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.KeyValueMap; -import android.filterfw.core.MutableFrameFormat; +import android.compat.annotation.UnsupportedAppUsage; import java.util.Arrays; import java.util.Map.Entry; diff --git a/media/mca/filterfw/java/android/filterfw/core/FrameManager.java b/media/mca/filterfw/java/android/filterfw/core/FrameManager.java index 85c8fcd9787d..e49aaf1d6fad 100644 --- a/media/mca/filterfw/java/android/filterfw/core/FrameManager.java +++ b/media/mca/filterfw/java/android/filterfw/core/FrameManager.java @@ -17,10 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.MutableFrameFormat; +import android.compat.annotation.UnsupportedAppUsage; /** * @hide diff --git a/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java b/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java index e25d6a7d70ab..7e4e8a64a81f 100644 --- a/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java +++ b/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java @@ -17,13 +17,12 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.NativeAllocatorTag; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.SurfaceTexture; +import android.media.MediaRecorder; import android.os.Looper; import android.util.Log; import android.view.Surface; -import android.media.MediaRecorder; /** * @hide diff --git a/media/mca/filterfw/java/android/filterfw/core/GLFrame.java b/media/mca/filterfw/java/android/filterfw/core/GLFrame.java index 9e3025fafb6e..1ccd7feaa7c3 100644 --- a/media/mca/filterfw/java/android/filterfw/core/GLFrame.java +++ b/media/mca/filterfw/java/android/filterfw/core/GLFrame.java @@ -17,15 +17,10 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.Frame; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.FrameManager; -import android.filterfw.core.NativeFrame; -import android.filterfw.core.StopWatchMap; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Bitmap; -import android.opengl.GLES20; import android.graphics.Rect; +import android.opengl.GLES20; import java.nio.ByteBuffer; diff --git a/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java b/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java index 250cfaaba9d4..b57e8bb7262e 100644 --- a/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java +++ b/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java @@ -17,7 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * @hide diff --git a/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java b/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java index ae2ad99899f0..da00b1ffb180 100644 --- a/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java +++ b/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java @@ -17,9 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.FrameFormat; -import android.filterfw.core.KeyValueMap; +import android.compat.annotation.UnsupportedAppUsage; import java.util.Arrays; diff --git a/media/mca/filterfw/java/android/filterfw/core/Program.java b/media/mca/filterfw/java/android/filterfw/core/Program.java index 376c08554eb2..145388e4437e 100644 --- a/media/mca/filterfw/java/android/filterfw/core/Program.java +++ b/media/mca/filterfw/java/android/filterfw/core/Program.java @@ -17,8 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.Frame; +import android.compat.annotation.UnsupportedAppUsage; /** * @hide diff --git a/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java b/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java index f41636e7cf76..e043be0e27bd 100644 --- a/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java +++ b/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java @@ -17,12 +17,7 @@ package android.filterfw.core; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.core.Frame; -import android.filterfw.core.NativeAllocatorTag; -import android.filterfw.core.Program; -import android.filterfw.core.StopWatchMap; -import android.filterfw.core.VertexFrame; +import android.compat.annotation.UnsupportedAppUsage; import android.filterfw.geometry.Quad; import android.opengl.GLES20; diff --git a/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java b/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java index ac087305287f..0e05092d0cdd 100644 --- a/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java +++ b/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java @@ -17,7 +17,7 @@ package android.filterfw.format; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.filterfw.core.FrameFormat; import android.filterfw.core.MutableFrameFormat; import android.graphics.Bitmap; diff --git a/media/mca/filterfw/java/android/filterfw/geometry/Point.java b/media/mca/filterfw/java/android/filterfw/geometry/Point.java index d7acf12dd1de..96d2d7b08b74 100644 --- a/media/mca/filterfw/java/android/filterfw/geometry/Point.java +++ b/media/mca/filterfw/java/android/filterfw/geometry/Point.java @@ -17,8 +17,7 @@ package android.filterfw.geometry; -import android.annotation.UnsupportedAppUsage; -import java.lang.Math; +import android.compat.annotation.UnsupportedAppUsage; /** * @hide diff --git a/media/mca/filterfw/java/android/filterfw/geometry/Quad.java b/media/mca/filterfw/java/android/filterfw/geometry/Quad.java index 610e5b80399d..2b308a91576f 100644 --- a/media/mca/filterfw/java/android/filterfw/geometry/Quad.java +++ b/media/mca/filterfw/java/android/filterfw/geometry/Quad.java @@ -17,10 +17,8 @@ package android.filterfw.geometry; -import android.annotation.UnsupportedAppUsage; -import android.filterfw.geometry.Point; +import android.compat.annotation.UnsupportedAppUsage; -import java.lang.Float; import java.util.Arrays; import java.util.Collections; import java.util.List; diff --git a/opengl/java/android/opengl/EGL14.java b/opengl/java/android/opengl/EGL14.java index 728e6e18cc31..90b46fd5901a 100644 --- a/opengl/java/android/opengl/EGL14.java +++ b/opengl/java/android/opengl/EGL14.java @@ -18,11 +18,11 @@ package android.opengl; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.SurfaceTexture; import android.view.Surface; -import android.view.SurfaceView; import android.view.SurfaceHolder; +import android.view.SurfaceView; /** * EGL 1.4 diff --git a/opengl/java/android/opengl/GLES20.java b/opengl/java/android/opengl/GLES20.java index d66e7ac84a3b..e853e4447daa 100644 --- a/opengl/java/android/opengl/GLES20.java +++ b/opengl/java/android/opengl/GLES20.java @@ -19,7 +19,7 @@ package android.opengl; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** OpenGL ES 2.0 */ diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index 8a3e6a0b0fd5..75131b0f6b9c 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -16,7 +16,7 @@ package android.opengl; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Trace; import android.util.AttributeSet; diff --git a/opengl/java/com/google/android/gles_jni/EGLImpl.java b/opengl/java/com/google/android/gles_jni/EGLImpl.java index f94f69f0fd3f..b4ea0a6132a5 100644 --- a/opengl/java/com/google/android/gles_jni/EGLImpl.java +++ b/opengl/java/com/google/android/gles_jni/EGLImpl.java @@ -16,13 +16,12 @@ package com.google.android.gles_jni; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.SurfaceTexture; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; -import dalvik.annotation.compat.UnsupportedAppUsage; - import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; diff --git a/opengl/java/com/google/android/gles_jni/GLImpl.java b/opengl/java/com/google/android/gles_jni/GLImpl.java index 2a8d07f03148..3c808a6ada48 100644 --- a/opengl/java/com/google/android/gles_jni/GLImpl.java +++ b/opengl/java/com/google/android/gles_jni/GLImpl.java @@ -20,14 +20,13 @@ package com.google.android.gles_jni; import android.app.AppGlobals; +import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.os.Build; import android.os.UserHandle; import android.util.Log; -import dalvik.annotation.compat.UnsupportedAppUsage; - import java.nio.Buffer; import javax.microedition.khronos.opengles.GL10; diff --git a/opengl/java/javax/microedition/khronos/egl/EGL10.java b/opengl/java/javax/microedition/khronos/egl/EGL10.java index 8a2517062d4d..ea571c7311a1 100644 --- a/opengl/java/javax/microedition/khronos/egl/EGL10.java +++ b/opengl/java/javax/microedition/khronos/egl/EGL10.java @@ -16,8 +16,7 @@ package javax.microedition.khronos.egl; -import android.annotation.UnsupportedAppUsage; -import java.lang.String; +import android.compat.annotation.UnsupportedAppUsage; public interface EGL10 extends EGL { int EGL_SUCCESS = 0x3000; diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml index 261b9f508ccd..c5da135f529e 100644 --- a/packages/CarSystemUI/AndroidManifest.xml +++ b/packages/CarSystemUI/AndroidManifest.xml @@ -25,4 +25,6 @@ <uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST"/> <!-- This permission is required to get bluetooth broadcast. --> <uses-permission android:name="android.permission.BLUETOOTH" /> + <!-- Allows us to defer some actions until after the BOOT_COMPLETED event --> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> </manifest> diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml new file mode 100644 index 000000000000..8247211dcb32 --- /dev/null +++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<com.android.systemui.statusbar.car.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/car_top_bar" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/system_bar_background" + android:orientation="vertical"> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1"> + + <FrameLayout + android:id="@+id/left_hvac_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentStart="true" + > + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/hvacleft" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@null" + systemui:broadcast="true" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" + /> + + <com.android.systemui.statusbar.hvac.AnimatedTemperatureView + android:id="@+id/lefttext" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingStart="@*android:dimen/car_padding_4" + android:paddingEnd="16dp" + android:gravity="center_vertical|start" + android:minEms="4" + android:textAppearance="@style/TextAppearance.CarStatus" + systemui:hvacAreaId="49" + systemui:hvacMaxText="@string/hvac_max_text" + systemui:hvacMaxValue="@dimen/hvac_max_value" + systemui:hvacMinText="@string/hvac_min_text" + systemui:hvacMinValue="@dimen/hvac_min_value" + systemui:hvacPivotOffset="60dp" + systemui:hvacPropertyId="358614275" + systemui:hvacTempFormat="%.0f\u00B0" + /> + </FrameLayout> + + <FrameLayout + android:id="@+id/clock_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_centerInParent="true"> + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/qs" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@null"/> + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:elevation="5dp" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.StatusBar.Clock"/> + </FrameLayout> + + <LinearLayout + android:id="@+id/system_icon_area" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:layout_toEndOf="@+id/clock_container" + android:paddingStart="@*android:dimen/car_padding_1" + android:gravity="center_vertical" + android:orientation="horizontal" + > + + <include + layout="@layout/system_icons" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:paddingStart="4dp" + android:gravity="center_vertical" + /> + </LinearLayout> + + <FrameLayout + android:id="@+id/right_hvac_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentEnd="true" + > + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/hvacright" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@null" + systemui:broadcast="true" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" + /> + + <com.android.systemui.statusbar.hvac.AnimatedTemperatureView + android:id="@+id/righttext" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingStart="16dp" + android:paddingEnd="@*android:dimen/car_padding_4" + android:gravity="center_vertical|end" + android:minEms="4" + android:textAppearance="@style/TextAppearance.CarStatus" + systemui:hvacAreaId="68" + systemui:hvacMaxText="@string/hvac_max_text" + systemui:hvacMaxValue="@dimen/hvac_max_value" + systemui:hvacMinText="@string/hvac_min_text" + systemui:hvacMinValue="@dimen/hvac_min_value" + systemui:hvacPivotOffset="60dp" + systemui:hvacPropertyId="358614275" + systemui:hvacTempFormat="%.0f\u00B0" + /> + </FrameLayout> + </RelativeLayout> + +</com.android.systemui.statusbar.car.CarNavigationBarView> diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml index e1bcc2e5f86c..7fee8051c472 100644 --- a/packages/CarSystemUI/res/layout/super_status_bar.xml +++ b/packages/CarSystemUI/res/layout/super_status_bar.xml @@ -42,14 +42,6 @@ </com.android.systemui.statusbar.BackDropView> <com.android.systemui.statusbar.ScrimView - android:id="@+id/scrim_for_bubble" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:importantForAccessibility="no" - sysui:ignoreRightInset="true" - /> - - <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_behind" android:layout_width="match_parent" android:layout_height="match_parent" @@ -69,10 +61,10 @@ android:visibility="gone" /> - <include layout="@layout/car_top_navigation_bar" + <FrameLayout + android:id="@+id/car_top_navigation_bar_container" android:layout_width="match_parent" - android:layout_height="wrap_content" - /> + android:layout_height="wrap_content"/> </LinearLayout> <include layout="@layout/brightness_mirror"/> diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml index 467c4a41893d..cbf22870af96 100644 --- a/packages/CarSystemUI/res/values/config.xml +++ b/packages/CarSystemUI/res/values/config.xml @@ -40,4 +40,19 @@ slots that may be reused for things like IME control. --> <integer name="config_maxNotificationIcons">0</integer> + <!-- + Initial alpha percent value for the background when the notification + shade is open. Should be a number between, and inclusive, 0 and 100. + If the number is 0, then the background alpha starts off fully + transparent. If the number if 100, then the background alpha starts off + fully opaque. --> + <integer name="config_initialNotificationBackgroundAlpha">0</integer> + <!-- + Final alpha percent value for the background when the notification + shade is fully open. Should be a number between, and inclusive, 0 and + 100. If this value is smaller than + config_initialNotificationBackgroundAlpha, the background will default + to a constant alpha percent value using the initial alpha. --> + <integer name="config_finalNotificationBackgroundAlpha">100</integer> + </resources> diff --git a/packages/CarSystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml index fb67b302a4ae..ba3d329ef32a 100644 --- a/packages/CarSystemUI/res/values/integers_car.xml +++ b/packages/CarSystemUI/res/values/integers_car.xml @@ -34,4 +34,9 @@ <!-- The delay before the unlock dialog pops up --> <integer name="unlock_dialog_delay_ms">0</integer> + <!-- Timeout values in milliseconds for displaying volume dialog--> + <integer name="car_volume_dialog_display_normal_timeout">3000</integer> + <integer name="car_volume_dialog_display_hovering_timeout">16000</integer> + <integer name="car_volume_dialog_display_expanded_normal_timeout">6000</integer> + <integer name="car_volume_dialog_display_expanded_hovering_timeout">32000</integer> </resources> diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java index c7654e81e0b1..a4235540e337 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java @@ -20,6 +20,7 @@ import android.content.Context; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; +import com.android.systemui.car.CarServiceProvider; import com.android.systemui.statusbar.car.CarFacetButtonController; import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -36,6 +37,7 @@ import dagger.Component; public class CarSystemUIFactory extends SystemUIFactory { private CarDependencyComponent mCarDependencyComponent; + private CarServiceProvider mCarServiceProvider; @Override protected SystemUIRootComponent buildSystemUIRootComponent(Context context) { @@ -48,6 +50,14 @@ public class CarSystemUIFactory extends SystemUIFactory { .build(); } + /** Gets a {@link CarServiceProvider}. */ + public CarServiceProvider getCarServiceProvider(Context context) { + if (mCarServiceProvider == null) { + mCarServiceProvider = new CarServiceProvider(context); + } + return mCarServiceProvider; + } + public CarDependencyComponent getCarDependencyComponent() { return mCarDependencyComponent; } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java index afd722ba0091..447e579ece42 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java @@ -22,6 +22,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.policy.BatteryController; import javax.inject.Inject; import javax.inject.Singleton; @@ -34,8 +35,9 @@ public class CarNotificationInterruptionStateProvider extends @Inject public CarNotificationInterruptionStateProvider(Context context, NotificationFilter filter, - StatusBarStateController stateController) { - super(context, filter, stateController); + StatusBarStateController stateController, + BatteryController batteryController) { + super(context, filter, stateController, batteryController); } @Override diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java new file mode 100644 index 000000000000..9ee368ee497f --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarServiceProvider.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.car; + +import android.car.Car; +import android.car.Car.CarServiceLifecycleListener; +import android.content.Context; + +import java.util.ArrayList; +import java.util.List; + +/** + * Connects to the car service a single time for shared use across all of system ui. + */ +public class CarServiceProvider { + + private final Context mContext; + private final List<CarServiceLifecycleListener> mListeners = new ArrayList<>(); + private Car mCar; + + public CarServiceProvider(Context context) { + mContext = context; + mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, + (car, ready) -> { + mCar = car; + + synchronized (mListeners) { + for (CarServiceLifecycleListener listener : mListeners) { + listener.onLifecycleChanged(mCar, ready); + } + } + }); + } + + /** + * Let's other components hook into the connection to the car service. If we're already + * connected + * to the car service, the callback is immediately triggered. + */ + public void addListener(CarServiceLifecycleListener listener) { + if (mCar.isConnected()) { + listener.onLifecycleChanged(mCar, /* ready= */ true); + } + mListeners.add(listener); + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/SUWProgressController.java b/packages/CarSystemUI/src/com/android/systemui/car/SUWProgressController.java new file mode 100644 index 000000000000..d95293992ec3 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/SUWProgressController.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.car; + +import android.app.ActivityManager; +import android.car.settings.CarSettings; +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.provider.Settings; + +import com.android.systemui.Dependency; + +import java.util.ArrayList; + +/** + * A controller that monitors the status of SUW progress for each user. + */ +public class SUWProgressController { + private static final Uri USER_SETUP_IN_PROGRESS_URI = Settings.Secure.getUriFor( + CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS); + private final ArrayList<SUWProgressListener> mListeners = new ArrayList<>(); + private final ContentObserver mCarSettingsObserver = new ContentObserver( + Dependency.get(Dependency.MAIN_HANDLER)) { + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + if (USER_SETUP_IN_PROGRESS_URI.equals(uri)) { + notifyUserSetupInProgressChanged(); + } + } + }; + private final ContentResolver mContentResolver; + + public SUWProgressController(Context context) { + mContentResolver = context.getContentResolver(); + } + + /** + * Returns {@code true} then SUW is in progress for the given user. + */ + public boolean isUserSetupInProgress(int user) { + return Settings.Secure.getIntForUser(mContentResolver, + CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS, /* def= */ 0, user) != 0; + } + + /** + * Returns {@code true} then SUW is in progress for the current user. + */ + public boolean isCurrentUserSetupInProgress() { + return isUserSetupInProgress(ActivityManager.getCurrentUser()); + } + + /** + * Adds a {@link SUWProgressListener} callback. + */ + public void addCallback(SUWProgressListener listener) { + mListeners.add(listener); + if (mListeners.size() == 1) { + startListening(ActivityManager.getCurrentUser()); + } + listener.onUserSetupInProgressChanged(); + } + + /** + * Removes a {@link SUWProgressListener} callback. + */ + public void removeCallback(SUWProgressListener listener) { + mListeners.remove(listener); + if (mListeners.size() == 0) { + stopListening(); + } + } + + private void startListening(int user) { + mContentResolver.registerContentObserver( + USER_SETUP_IN_PROGRESS_URI, /* notifyForDescendants= */ true, mCarSettingsObserver, + user); + } + + private void stopListening() { + mContentResolver.unregisterContentObserver(mCarSettingsObserver); + } + + /** + * Allows SUWProgressController to switch its listeners to observe SUW progress for new user. + */ + public void onUserSwitched() { + if (mListeners.size() == 0) { + return; + } + + mContentResolver.unregisterContentObserver(mCarSettingsObserver); + mContentResolver.registerContentObserver( + USER_SETUP_IN_PROGRESS_URI, /* notifyForDescendants= */ true, mCarSettingsObserver, + ActivityManager.getCurrentUser()); + } + + private void notifyUserSetupInProgressChanged() { + for (int i = mListeners.size() - 1; i >= 0; --i) { + mListeners.get(i).onUserSetupInProgressChanged(); + } + } + + /** + * A listener that listens for changes in SUW progress for a user. + */ + public interface SUWProgressListener { + /** + * A callback for when a change occurs in SUW progress for a user. + */ + default void onUserSetupInProgressChanged() { + } + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java index 58f80a4ed968..d79849ccafc6 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java @@ -257,6 +257,11 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo return false; } + @Override + public boolean isAodPowerSave() { + return false; + } + private void notifyBatteryLevelChanged() { for (int i = 0, size = mChangeCallbacks.size(); i < size; i++) { mChangeCallbacks.get(i) diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java index a371a1d8db01..90d20ba19d0e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java @@ -110,24 +110,32 @@ public class CarFacetButton extends LinearLayout { mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER); } - setOnClickListener(v -> { - intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected); - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - }); + intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected); + setOnClickListener(getButtonClickListener(intent)); if (longPressIntentString != null) { final Intent longPressIntent = Intent.parseUri(longPressIntentString, Intent.URI_INTENT_SCHEME); - setOnLongClickListener(v -> { - mContext.startActivityAsUser(longPressIntent, UserHandle.CURRENT); - return true; - }); + setOnLongClickListener(getButtonLongClickListener(longPressIntent)); } } catch (Exception e) { throw new RuntimeException("Failed to attach intent", e); } } + /** Defines the behavior of a button click. */ + protected OnClickListener getButtonClickListener(Intent toSend) { + return v -> mContext.startActivityAsUser(toSend, UserHandle.CURRENT); + } + + /** Defines the behavior of a long click. */ + protected OnLongClickListener getButtonLongClickListener(Intent toSend) { + return v -> { + mContext.startActivityAsUser(toSend, UserHandle.CURRENT); + return true; + }; + } + private void setupIcons(TypedArray styledAttributes) { mSelectedAlpha = styledAttributes.getFloat( R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha); diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java index 8879742db00a..bdf23c56797b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java @@ -90,17 +90,7 @@ public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImag try { if (mIntent != null) { final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME); - setOnClickListener(v -> { - try { - if (mBroadcastIntent) { - mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT); - return; - } - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - } catch (Exception e) { - Log.e(TAG, "Failed to launch intent", e); - } - }); + setOnClickListener(getButtonClickListener(intent)); } } catch (URISyntaxException e) { throw new RuntimeException("Failed to attach intent", e); @@ -109,21 +99,41 @@ public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImag try { if (mLongIntent != null) { final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME); - setOnLongClickListener(v -> { - try { - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - } catch (Exception e) { - Log.e(TAG, "Failed to launch intent", e); - } - // consume event either way - return true; - }); + setOnLongClickListener(getButtonLongClickListener(intent)); } } catch (URISyntaxException e) { throw new RuntimeException("Failed to attach long press intent", e); } } + /** Defines the behavior of a button click. */ + protected OnClickListener getButtonClickListener(Intent toSend) { + return v -> { + try { + if (mBroadcastIntent) { + mContext.sendBroadcastAsUser(toSend, UserHandle.CURRENT); + return; + } + mContext.startActivityAsUser(toSend, UserHandle.CURRENT); + } catch (Exception e) { + Log.e(TAG, "Failed to launch intent", e); + } + }; + } + + /** Defines the behavior of a long click. */ + protected OnLongClickListener getButtonLongClickListener(Intent toSend) { + return v -> { + try { + mContext.startActivityAsUser(toSend, UserHandle.CURRENT); + } catch (Exception e) { + Log.e(TAG, "Failed to launch intent", e); + } + // consume event either way + return true; + }; + } + /** * @param selected true if should indicate if this is a selected state, false otherwise */ diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 25191f6a9617..0046c20ab164 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -23,16 +23,24 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.car.Car; +import android.car.CarNotConnectedException; import android.car.drivingstate.CarDrivingStateEvent; import android.car.drivingstate.CarUxRestrictionsManager; import android.car.hardware.power.CarPowerManager.CarPowerStateListener; -import android.car.trust.CarTrustAgentEnrollmentManager; +import android.car.media.CarAudioManager; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.inputmethodservice.InputMethodService; +import android.media.AudioManager; +import android.os.Build; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.util.Log; import android.view.Display; import android.view.GestureDetector; @@ -61,26 +69,48 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.BatteryMeterView; import com.android.systemui.CarSystemUIFactory; import com.android.systemui.Dependency; +import com.android.systemui.ForegroundServiceController; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; +import com.android.systemui.car.SUWProgressController; import com.android.systemui.classifier.FalsingLog; +import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.qs.car.CarQSFragment; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.FlingAnimationUtils; +import com.android.systemui.statusbar.NavigationBarController; +import com.android.systemui.statusbar.NotificationListener; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.car.hvac.HvacController; import com.android.systemui.statusbar.car.hvac.TemperatureView; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; +import com.android.systemui.statusbar.phone.LightBarController; +import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper; +import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.KeyguardMonitor; +import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.UserSwitcherController; +import com.android.systemui.statusbar.policy.ZenModeController; +import com.android.systemui.volume.VolumeUI; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -101,6 +131,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private float mOpeningVelocity = DEFAULT_FLING_VELOCITY; private float mClosingVelocity = DEFAULT_FLING_VELOCITY; + private float mBackgroundAlphaDiff; + private float mInitialBackgroundAlpha; + private TaskStackListenerImpl mTaskStackListener; private FullscreenUserSwitcher mFullscreenUserSwitcher; @@ -109,9 +142,11 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private BatteryMeterView mBatteryMeterView; private Drawable mNotificationPanelBackground; + private ViewGroup mTopNavigationBarContainer; private ViewGroup mNavigationBarWindow; private ViewGroup mLeftNavigationBarWindow; private ViewGroup mRightNavigationBarWindow; + private CarNavigationBarView mTopNavigationBarView; private CarNavigationBarView mNavigationBarView; private CarNavigationBarView mLeftNavigationBarView; private CarNavigationBarView mRightNavigationBarView; @@ -123,7 +158,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private CarFacetButtonController mCarFacetButtonController; private ActivityManagerWrapper mActivityManagerWrapper; private DeviceProvisionedController mDeviceProvisionedController; - private boolean mDeviceIsProvisioned = true; + private SUWProgressController mSUWProgressController; + private boolean mDeviceIsSetUpForUser = true; + private boolean mIsUserSetupInProgress = false; private HvacController mHvacController; private DrivingStateHelper mDrivingStateHelper; private PowerManagerHelper mPowerManagerHelper; @@ -132,6 +169,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private NotificationDataManager mNotificationDataManager; private NotificationClickHandlerFactory mNotificationClickHandlerFactory; private ScreenLifecycle mScreenLifecycle; + private CarAudioManager mCarAudioManager; + private VolumeUI mVolumeUI; // The container for the notifications. private CarNotificationView mNotificationView; @@ -144,6 +183,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private boolean mNotificationListAtBottom; // Was the notification list at the bottom when the user first touched the screen private boolean mNotificationListAtBottomAtTimeOfTouch; + // To be attached to the top navigation bar (i.e. status bar) to pull down the notification + // panel. + private View.OnTouchListener mTopNavBarNotificationTouchListener; // To be attached to the navigation bars such that they can close the notification panel if // it's open. private View.OnTouchListener mNavBarNotificationTouchListener; @@ -174,6 +216,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private boolean mHideNavBarForKeyboard; private boolean mBottomNavBarVisible; + private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper; + private final CarPowerStateListener mCarPowerStateListener = (int state) -> { // When the car powers on, clear all notifications and mute/unread states. @@ -188,12 +232,55 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } }; + private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback = + new CarAudioManager.CarVolumeCallback() { + @Override + public void onGroupVolumeChanged(int zoneId, int groupId, int flags) { + if (mVolumeUI == null && (flags & AudioManager.FLAG_SHOW_UI) != 0) { + new Handler(Looper.getMainLooper()).post(() -> { + // Initialize Volume UI + mVolumeUI = new VolumeUI(); + mVolumeUI.mComponents = mComponents; + mVolumeUI.mContext = mContext; + mVolumeUI.start(); + }); + mCarAudioManager.unregisterCarVolumeCallback(mVolumeChangeCallback); + } + } + + @Override + public void onMasterMuteChanged(int zoneId, int flags) { + // ignored + } + }; + + private boolean mBootCompleted = false; + private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mBootCompleted = true; + String action = intent.getAction(); + if (action != null && action.equals(Intent.ACTION_BOOT_COMPLETED)) { + initHvac(); + } + } + }; + @Override public void start() { + // Non blocking call to connect to car service. Call this early so that we'll be connected + // asap. + ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext); + + // Defer some actions for CarStatusBar initialization until after boot complete event + registerBootCompletedReceiver(); + // get the provisioned state before calling the parent class since it's that flow that // builds the nav bar mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); - mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned(); + mSUWProgressController = new SUWProgressController(mContext); + mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup(); + mIsUserSetupInProgress = mSUWProgressController.isCurrentUserSetupInProgress(); // Keyboard related setup, before nav bars are created. mHideNavBarForKeyboard = mContext.getResources().getBoolean( @@ -205,6 +292,29 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mScreenLifecycle = Dependency.get(ScreenLifecycle.class); mScreenLifecycle.addObserver(mScreenObserver); + // Need to initialize HVAC controller before calling super.start - before system bars are + // created. + mHvacController = new HvacController(mContext); + + // Notification bar related setup. + mInitialBackgroundAlpha = (float) mContext.getResources().getInteger( + R.integer.config_initialNotificationBackgroundAlpha) / 100; + if (mInitialBackgroundAlpha < 0 || mInitialBackgroundAlpha > 100) { + throw new RuntimeException( + "Unable to setup notification bar due to incorrect initial background alpha" + + " percentage"); + } + float finalBackgroundAlpha = Math.max( + mInitialBackgroundAlpha, + (float) mContext.getResources().getInteger( + R.integer.config_finalNotificationBackgroundAlpha) / 100); + if (finalBackgroundAlpha < 0 || finalBackgroundAlpha > 100) { + throw new RuntimeException( + "Unable to setup notification bar due to incorrect final background alpha" + + " percentage"); + } + mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha; + super.start(); mTaskStackListener = new TaskStackListenerImpl(); mActivityManagerWrapper = ActivityManagerWrapper.getInstance(); @@ -223,25 +333,30 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mHvacController.connectToCarService(); - CarSystemUIFactory factory = SystemUIFactory.getInstance(); - if (!mDeviceIsProvisioned) { - mDeviceProvisionedController.addCallback( - new DeviceProvisionedController.DeviceProvisionedListener() { - @Override - public void onDeviceProvisionedChanged() { - mHandler.post(() -> { - // on initial boot we are getting a call even though the value - // is the same so we are confirming the reset is needed - boolean deviceProvisioned = - mDeviceProvisionedController.isDeviceProvisioned(); - if (mDeviceIsProvisioned != deviceProvisioned) { - mDeviceIsProvisioned = deviceProvisioned; - restartNavBars(); - } - }); - } - }); - } + mSUWProgressController.addCallback( + new SUWProgressController.SUWProgressListener() { + @Override + public void onUserSetupInProgressChanged() { + mHandler.post(() -> restartNavBarsIfNecessary()); + } + }); + mDeviceProvisionedController.addCallback( + new DeviceProvisionedController.DeviceProvisionedListener() { + @Override + public void onUserSetupChanged() { + mHandler.post(() -> restartNavBarsIfNecessary()); + } + + @Override + public void onUserSwitched() { + mSUWProgressController.onUserSwitched(); + mHandler.post(() -> restartNavBarsIfNecessary()); + } + }); + + // Used by onDrivingStateChanged and it can be called inside + // DrivingStateHelper.connectToCarService() + mSwitchToGuestTimer = new SwitchToGuestTimer(mContext); // Register a listener for driving state changes. mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged); @@ -250,7 +365,84 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mPowerManagerHelper = new PowerManagerHelper(mContext, mCarPowerStateListener); mPowerManagerHelper.connectToCarService(); - mSwitchToGuestTimer = new SwitchToGuestTimer(mContext); + ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext) + .addListener((car, ready) -> { + if (!ready || mCarAudioManager != null) { + return; + } + try { + mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE); + Log.d(TAG, "Registering mVolumeChangeCallback."); + // This volume call back is never unregistered because CarStatusBar is + // never destroyed. + mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback); + } catch (CarNotConnectedException e) { + Log.wtf(TAG, " mVolumeChangeCallback failed to connect to car ", e); + } + }); + } + + @Override + protected void getDependencies() { + // Keyguard + mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); + mScreenLifecycle = Dependency.get(ScreenLifecycle.class); + + // Policy + mZenController = Dependency.get(ZenModeController.class); + if (Build.IS_USERDEBUG) { + mNetworkController = Dependency.get(NetworkController.class); + } + + // Icon + mIconController = Dependency.get(StatusBarIconController.class); + mLightBarController = Dependency.get(LightBarController.class); + + // Notifications + mEntryManager = Dependency.get(NotificationEntryManager.class); + mForegroundServiceController = Dependency.get(ForegroundServiceController.class); + mGroupAlertTransferHelper = Dependency.get(NotificationGroupAlertTransferHelper.class); + mGroupManager = Dependency.get(NotificationGroupManager.class); + mGutsManager = Dependency.get(NotificationGutsManager.class); + mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class); + mMediaManager = Dependency.get(NotificationMediaManager.class); + mNotificationListener = Dependency.get(NotificationListener.class); + mNotificationLogger = Dependency.get(NotificationLogger.class); + mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); + mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); + mVisualStabilityManager = Dependency.get(VisualStabilityManager.class); + + // Others + mColorExtractor = Dependency.get(SysuiColorExtractor.class); + mNavigationBarController = Dependency.get(NavigationBarController.class); + mUserSwitcherController = Dependency.get(UserSwitcherController.class); + } + + @Override + protected void setUpQuickSettingsTilePanel() { + // ignore. + } + + private void registerBootCompletedReceiver() { + IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); + bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + mContext.registerReceiver(mBootCompletedReceiver, bootCompletedFilter); + } + + private void unregisterBootCompletedListener() { + mContext.unregisterReceiver(mBootCompletedReceiver); + } + + private void restartNavBarsIfNecessary() { + boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup(); + boolean currentUserSetupInProgress = mSUWProgressController.isCurrentUserSetupInProgress(); + if (mIsUserSetupInProgress != currentUserSetupInProgress + || mDeviceIsSetUpForUser != currentUserSetup) { + mDeviceIsSetUpForUser = currentUserSetup; + mIsUserSetupInProgress = currentUserSetupInProgress; + restartNavBars(); + } } /** @@ -260,9 +452,11 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private void restartNavBars() { // remove and reattach all hvac components such that we don't keep a reference to unused // ui elements - mHvacController.removeAllComponents(); - addTemperatureViewToController(mStatusBarWindow); + if (mHvacController != null) { + mHvacController.removeAllComponents(); + } mCarFacetButtonController.removeAll(); + if (mNavigationBarWindow != null) { mNavigationBarWindow.removeAllViews(); mNavigationBarView = null; @@ -279,6 +473,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } buildNavBarContent(); + if (mBootCompleted) { + initHvac(); + } // If the UI was rebuilt (day/night change) while the keyguard was up we need to // correctly respect that state. if (mIsKeyguard) { @@ -290,6 +487,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } private void addTemperatureViewToController(View v) { + if (v == null) return; + if (v instanceof TemperatureView) { mHvacController.addHvacTextView((TemperatureView) v); } else if (v instanceof ViewGroup) { @@ -299,7 +498,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } } } - /** * Allows for showing or hiding just the navigation bars. This is indented to be used when * the full screen user selector is shown. @@ -356,7 +554,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt @Override protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) { super.makeStatusBarView(result); - mHvacController = new HvacController(mContext); CarSystemUIFactory factory = SystemUIFactory.getInstance(); mCarFacetButtonController = factory.getCarDependencyComponent() @@ -381,7 +578,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt * touch listeners needed for opening and closing the notification panel */ private void connectNotificationsUI() { - // Attached to the status bar to detect pull down of the notification shade. + // Attached to the top navigation bar (i.e. status bar) to detect pull down of the + // notification shade. GestureDetector openGestureDetector = new GestureDetector(mContext, new OpenNotificationGestureListener() { @Override @@ -414,6 +612,18 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext, new HandleBarCloseNotificationGestureListener()); + mTopNavBarNotificationTouchListener = (v, event) -> { + if (!mDeviceIsSetUpForUser || mIsUserSetupInProgress) { + return true; + } + boolean consumed = openGestureDetector.onTouchEvent(event); + if (consumed) { + return true; + } + maybeCompleteAnimation(event); + return true; + }; + mNavBarNotificationTouchListener = (v, event) -> { boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event); @@ -424,21 +634,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt return true; }; - // The following are the ui elements that the user would call the status bar. - // This will set the status bar so it they can make call backs. - CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar); - topBar.setStatusBar(this); - topBar.setStatusBarWindowTouchListener((v1, event1) -> { - - boolean consumed = openGestureDetector.onTouchEvent(event1); - if (consumed) { - return true; - } - maybeCompleteAnimation(event1); - return true; - } - ); - mNotificationClickHandlerFactory = new NotificationClickHandlerFactory( mBarService, launchResult -> { @@ -447,33 +642,16 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt animateCollapsePanels(); } }); - Car car = Car.createCar(mContext); - CarUxRestrictionsManager carUxRestrictionsManager = (CarUxRestrictionsManager) - car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE); + CarNotificationListener carNotificationListener = new CarNotificationListener(); - CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper = - new CarUxRestrictionManagerWrapper(); - carUxRestrictionManagerWrapper.setCarUxRestrictionsManager(carUxRestrictionsManager); + mCarUxRestrictionManagerWrapper = new CarUxRestrictionManagerWrapper(); mNotificationDataManager = new NotificationDataManager(); - mNotificationDataManager.setOnUnseenCountUpdateListener( - () -> { - if (mNavigationBarView != null && mNotificationDataManager != null) { - Boolean hasUnseen = - mNotificationDataManager.getUnseenNotificationCount() > 0; - if (mNavigationBarView != null) { - mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); - } - - if (mLeftNavigationBarView != null) { - mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); - } - - if (mRightNavigationBarView != null) { - mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); - } - } - }); + mNotificationDataManager.setOnUnseenCountUpdateListener(() -> { + if (mNavigationBarView != null && mNotificationDataManager != null) { + onUseenCountUpdate(mNotificationDataManager.getUnseenNotificationCount()); + } + }); mEnableHeadsUpNotificationWhenNotificationShadeOpen = mContext.getResources().getBoolean( R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen); @@ -482,7 +660,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mNotificationClickHandlerFactory, mNotificationDataManager); mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager); - carNotificationListener.registerAsSystemService(mContext, carUxRestrictionManagerWrapper, + carNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper, carHeadsUpNotificationManager, mNotificationDataManager); mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view); @@ -577,14 +755,45 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt return handled || isTracking; } }); + ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext) + .addListener((car, ready) -> { + if (!ready) { + return; + } + CarUxRestrictionsManager carUxRestrictionsManager = + (CarUxRestrictionsManager) + car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE); + mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager( + carUxRestrictionsManager); + + mNotificationViewController = new NotificationViewController( + mNotificationView, + PreprocessingManager.getInstance(mContext), + carNotificationListener, + mCarUxRestrictionManagerWrapper, + mNotificationDataManager); + mNotificationViewController.enable(); + }); + } + + /** + * This method is automatically called whenever there is an update to the number of unseen + * notifications. This method can be extended by OEMs to customize the desired logic. + */ + protected void onUseenCountUpdate(int unseenNotificationCount) { + boolean hasUnseen = unseenNotificationCount > 0; - mNotificationViewController = new NotificationViewController( - mNotificationView, - PreprocessingManager.getInstance(mContext), - carNotificationListener, - carUxRestrictionManagerWrapper, - mNotificationDataManager); - mNotificationViewController.enable(); + if (mNavigationBarView != null) { + mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); + } + + if (mLeftNavigationBarView != null) { + mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); + } + + if (mRightNavigationBarView != null) { + mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); + } } /** @@ -745,23 +954,32 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } private void buildNavBarContent() { + boolean shouldBuildNavBarContent = mDeviceIsSetUpForUser && !mIsUserSetupInProgress; + + // Always build top bar. + buildTopBar(shouldBuildNavBarContent ? R.layout.car_top_navigation_bar : + R.layout.car_top_navigation_bar_unprovisioned); + if (mShowBottom) { - buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar : + buildBottomBar(shouldBuildNavBarContent ? R.layout.car_navigation_bar : R.layout.car_navigation_bar_unprovisioned); } if (mShowLeft) { - buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar : + buildLeft(shouldBuildNavBarContent ? R.layout.car_left_navigation_bar : R.layout.car_left_navigation_bar_unprovisioned); } if (mShowRight) { - buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar : + buildRight(shouldBuildNavBarContent ? R.layout.car_right_navigation_bar : R.layout.car_right_navigation_bar_unprovisioned); } } private void buildNavBarWindows() { + mTopNavigationBarContainer = mStatusBarWindow + .findViewById(R.id.car_top_navigation_bar_container); + if (mShowBottom) { mNavigationBarWindow = (ViewGroup) View.inflate(mContext, R.layout.navigation_bar_window, null); @@ -794,15 +1012,25 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0; - if (!isKeyboardVisible) { - attachBottomNavBarWindow(); - } else { - detachBottomNavBarWindow(); - } + showBottomNavBarWindow(isKeyboardVisible); } private void attachNavBarWindows() { - attachBottomNavBarWindow(); + if (mShowBottom && !mBottomNavBarVisible) { + mBottomNavBarVisible = true; + + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, + PixelFormat.TRANSLUCENT); + lp.setTitle("CarNavigationBar"); + lp.windowAnimations = 0; + mWindowManager.addView(mNavigationBarWindow, lp); + } if (mShowLeft) { int width = mContext.getResources().getDimensionPixelSize( @@ -840,47 +1068,31 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } } - /** - * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of - * attaching the bottom nav bar. - */ - protected void attachBottomNavBarWindow() { + private void showBottomNavBarWindow(boolean isKeyboardVisible) { if (!mShowBottom) { return; } - if (mBottomNavBarVisible) { + // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do + // nothing. Same with if keyboard is not visible and bottom nav bar is visible. + if (isKeyboardVisible ^ mBottomNavBarVisible) { return; } - mBottomNavBarVisible = true; - - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, - PixelFormat.TRANSLUCENT); - lp.setTitle("CarNavigationBar"); - lp.windowAnimations = 0; - mWindowManager.addView(mNavigationBarWindow, lp); - } - /** - * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of - * detaching the bottom nav bar. - */ - protected void detachBottomNavBarWindow() { - if (!mShowBottom) { - return; - } + mNavigationBarWindow.setVisibility(isKeyboardVisible ? View.GONE : View.VISIBLE); + mBottomNavBarVisible = !isKeyboardVisible; + } - if (!mBottomNavBarVisible) { - return; + private void buildTopBar(int layout) { + mTopNavigationBarContainer.removeAllViews(); + View.inflate(mContext, layout, mTopNavigationBarContainer); + mTopNavigationBarView = (CarNavigationBarView) mTopNavigationBarContainer.getChildAt(0); + if (mTopNavigationBarView == null) { + Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_top_navigation_bar"); + throw new RuntimeException("Unable to build top nav bar due to missing layout"); } - mBottomNavBarVisible = false; - mWindowManager.removeView(mNavigationBarWindow); + mTopNavigationBarView.setStatusBar(this); + mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener); } private void buildBottomBar(int layout) { @@ -891,10 +1103,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0); if (mNavigationBarView == null) { Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); - throw new RuntimeException("Unable to build botom nav bar due to missing layout"); + throw new RuntimeException("Unable to build bottom nav bar due to missing layout"); } mNavigationBarView.setStatusBar(this); - addTemperatureViewToController(mNavigationBarView); mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener); } @@ -902,11 +1113,10 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt View.inflate(mContext, layout, mLeftNavigationBarWindow); mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0); if (mLeftNavigationBarView == null) { - Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); + Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_left_navigation_bar"); throw new RuntimeException("Unable to build left nav bar due to missing layout"); } mLeftNavigationBarView.setStatusBar(this); - addTemperatureViewToController(mLeftNavigationBarView); mLeftNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener); } @@ -915,14 +1125,24 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt View.inflate(mContext, layout, mRightNavigationBarWindow); mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0); if (mRightNavigationBarView == null) { - Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); + Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_right_navigation_bar"); throw new RuntimeException("Unable to build right nav bar due to missing layout"); } mRightNavigationBarView.setStatusBar(this); - addTemperatureViewToController(mRightNavigationBarView); mRightNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener); } + private void initHvac() { + if (mHvacController == null) { + mHvacController = Dependency.get(HvacController.class); + mHvacController.connectToCarService(); + } + addTemperatureViewToController(mTopNavigationBarView); + addTemperatureViewToController(mNavigationBarView); + addTemperatureViewToController(mLeftNavigationBarView); + addTemperatureViewToController(mRightNavigationBarView); + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { //When executing dump() function simultaneously, we need to serialize them @@ -1030,12 +1250,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt UserSwitcherController userSwitcherController = Dependency.get(UserSwitcherController.class); if (userSwitcherController.useFullscreenUserSwitcher()) { - Car car = Car.createCar(mContext); - CarTrustAgentEnrollmentManager enrollmentManager = (CarTrustAgentEnrollmentManager) car - .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE); mFullscreenUserSwitcher = new FullscreenUserSwitcher(this, - mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), - enrollmentManager, mContext); + mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext); } else { super.createUserSwitcher(); } @@ -1125,6 +1341,12 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mScrimController.setScrimBehindDrawable(mNotificationPanelBackground); } + @Override + public void onLocaleListChanged() { + restartNavBars(); + connectNotificationsUI(); + } + /** * Returns the {@link Drawable} that represents the wallpaper that the user has currently set. */ @@ -1146,17 +1368,22 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin); } if (mNotificationView.getHeight() > 0) { - // Calculates the alpha value for the background based on how much of the notification - // shade is visible to the user. When the notification shade is completely open then - // alpha value will be 1. - float alpha = (float) height / mNotificationView.getHeight(); Drawable background = mNotificationView.getBackground().mutate(); - - background.setAlpha((int) (alpha * 255)); + background.setAlpha((int) (getBackgroundAlpha(height) * 255)); mNotificationView.setBackground(background); } } + /** + * Calculates the alpha value for the background based on how much of the notification + * shade is visible to the user. When the notification shade is completely open then + * alpha value will be 1. + */ + private float getBackgroundAlpha(int height) { + return mInitialBackgroundAlpha + + ((float) height / mNotificationView.getHeight() * mBackgroundAlphaDiff); + } + private void calculatePercentageFromBottom(float height) { if (mNotificationView.getHeight() > 0) { mPercentageFromBottom = (int) Math.abs( @@ -1373,8 +1600,10 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt @Override protected void setHeadsUpVisible() { - // if the Notifications panel is showing don't show the Heads up - if (!mEnableHeadsUpNotificationWhenNotificationShadeOpen && mPanelExpanded) { + // if the Notifications panel is showing or SUW for user is in progress then don't show + // heads up notifications + if ((!mEnableHeadsUpNotificationWhenNotificationShadeOpen && mPanelExpanded) + || !mDeviceIsSetUpForUser || mIsUserSetupInProgress) { return; } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java index a4424260fef5..76ad04f6716f 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java @@ -17,18 +17,18 @@ package com.android.systemui.statusbar.car; import android.car.Car; -import android.car.CarNotConnectedException; +import android.car.Car.CarServiceLifecycleListener; import android.car.drivingstate.CarDrivingStateEvent; import android.car.drivingstate.CarDrivingStateManager; import android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener; -import android.content.ComponentName; import android.content.Context; -import android.content.ServiceConnection; -import android.os.IBinder; import android.util.Log; import androidx.annotation.NonNull; +import com.android.systemui.CarSystemUIFactory; +import com.android.systemui.SystemUIFactory; + /** * Helper class for connecting to the {@link CarDrivingStateManager} and listening for driving state * changes. @@ -38,7 +38,6 @@ public class DrivingStateHelper { private final Context mContext; private CarDrivingStateManager mDrivingStateManager; - private Car mCar; private CarDrivingStateEventListener mDrivingStateHandler; public DrivingStateHelper(Context context, @@ -55,16 +54,11 @@ public class DrivingStateHelper { if (mDrivingStateManager == null) { return false; } - try { - CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState(); - if (currentState != null) { - return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING - || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING; - } - } catch (CarNotConnectedException e) { - Log.e(TAG, "Cannot determine current driving state. Car not connected", e); + CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState(); + if (currentState != null) { + return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING + || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING; } - return false; // Default to false. } @@ -72,55 +66,25 @@ public class DrivingStateHelper { * Establishes connection with the Car service. */ public void connectToCarService() { - mCar = Car.createCar(mContext, mCarConnectionListener); - if (mCar != null) { - mCar.connect(); - } + ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext) + .addListener(mCarServiceLifecycleListener); } - /** - * Disconnects from Car service and cleans up listeners. - */ - public void disconnectFromCarService() { - if (mCar != null) { - mCar.disconnect(); + private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> { + if (!ready) { + return; } - } - - private final ServiceConnection mCarConnectionListener = - new ServiceConnection() { - public void onServiceConnected(ComponentName name, IBinder service) { - logD("Car Service connected"); - try { - mDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager( - Car.CAR_DRIVING_STATE_SERVICE); - if (mDrivingStateManager != null) { - mDrivingStateManager.registerListener(mDrivingStateHandler); - mDrivingStateHandler.onDrivingStateChanged( - mDrivingStateManager.getCurrentCarDrivingState()); - } else { - Log.e(TAG, "CarDrivingStateService service not available"); - } - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car not connected", e); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - destroyDrivingStateManager(); - } - }; - - private void destroyDrivingStateManager() { - try { - if (mDrivingStateManager != null) { - mDrivingStateManager.unregisterListener(); - } - } catch (CarNotConnectedException e) { - Log.e(TAG, "Error unregistering listeners", e); + logD("Car Service connected"); + mDrivingStateManager = (CarDrivingStateManager) car.getCarManager( + Car.CAR_DRIVING_STATE_SERVICE); + if (mDrivingStateManager != null) { + mDrivingStateManager.registerListener(mDrivingStateHandler); + mDrivingStateHandler.onDrivingStateChanged( + mDrivingStateManager.getCurrentCarDrivingState()); + } else { + Log.e(TAG, "CarDrivingStateService service not available"); } - } + }; private void logD(String message) { if (Log.isLoggable(TAG, Log.DEBUG)) { diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java index 0f7c1ee8ea7e..92834e8cd085 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.car; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.car.Car; import android.car.trust.CarTrustAgentEnrollmentManager; import android.car.userlib.CarUserManagerHelper; import android.content.BroadcastReceiver; @@ -33,7 +34,10 @@ import android.view.ViewStub; import androidx.recyclerview.widget.GridLayoutManager; +import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.CarSystemUIFactory; import com.android.systemui.R; +import com.android.systemui.SystemUIFactory; import com.android.systemui.statusbar.car.CarTrustAgentUnlockDialogHelper.OnHideListener; import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord; @@ -42,15 +46,13 @@ import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord; */ public class FullscreenUserSwitcher { private static final String TAG = FullscreenUserSwitcher.class.getSimpleName(); - // Because user 0 is headless, user count for single user is 2 - private static final int NUMBER_OF_BACKGROUND_USERS = 1; private final UserGridRecyclerView mUserGridView; private final View mParent; private final int mShortAnimDuration; private final CarStatusBar mStatusBar; private final Context mContext; private final UserManager mUserManager; - private final CarTrustAgentEnrollmentManager mEnrollmentManager; + private CarTrustAgentEnrollmentManager mEnrollmentManager; private CarTrustAgentUnlockDialogHelper mUnlockDialogHelper; private UserGridRecyclerView.UserRecord mSelectedUser; private CarUserManagerHelper mCarUserManagerHelper; @@ -65,12 +67,9 @@ public class FullscreenUserSwitcher { } }; - - public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, - CarTrustAgentEnrollmentManager enrollmentManager, Context context) { + public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) { mStatusBar = statusBar; mParent = containerStub.inflate(); - mEnrollmentManager = enrollmentManager; mContext = context; View container = mParent.findViewById(R.id.container); @@ -86,6 +85,15 @@ public class FullscreenUserSwitcher { mUnlockDialogHelper = new CarTrustAgentUnlockDialogHelper(mContext); mUserManager = mContext.getSystemService(UserManager.class); + ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext) + .addListener((car, ready) -> { + if (!ready) { + return; + } + mEnrollmentManager = (CarTrustAgentEnrollmentManager) car + .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE); + }); + mShortAnimDuration = container.getResources() .getInteger(android.R.integer.config_shortAnimTime); IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); @@ -109,27 +117,19 @@ public class FullscreenUserSwitcher { /* isStartGuestSession= */ false, /* isAddUser= */ false, /* isForeground= */ true); - // For single user without trusted device, hide the user switcher. - if (!hasMultipleUsers() && !hasTrustedDevice(initialUser)) { - dismissUserSwitcher(); - return; - } - // Show unlock dialog for initial user - if (hasTrustedDevice(initialUser)) { + + // If the initial user has screen lock and trusted device, display the unlock dialog on the + // keyguard. + if (hasScreenLock(initialUser) && hasTrustedDevice(initialUser)) { mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser, mOnHideListener); + } else { + // If no trusted device, dismiss the keyguard. + dismissUserSwitcher(); } } /** - * Check if there is only one possible user to login in. - * In a Multi-User system there is always one background user (user 0) - */ - private boolean hasMultipleUsers() { - return mUserManager.getUserCount() > NUMBER_OF_BACKGROUND_USERS + 1; - } - - /** * Makes user grid visible. */ public void show() { @@ -162,7 +162,7 @@ public class FullscreenUserSwitcher { */ private void onUserSelected(UserGridRecyclerView.UserRecord record) { mSelectedUser = record; - if (hasTrustedDevice(record.mInfo.id)) { + if (hasScreenLock(record.mInfo.id) && hasTrustedDevice(record.mInfo.id)) { mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener); return; } @@ -200,7 +200,15 @@ public class FullscreenUserSwitcher { } + private boolean hasScreenLock(int uid) { + LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext); + return lockPatternUtils.isSecure(uid); + } + private boolean hasTrustedDevice(int uid) { + if (mEnrollmentManager == null) { // car service not ready, so it cannot be available. + return false; + } return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty(); } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java index 8de1439c3306..d87b54c39fd7 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java @@ -18,15 +18,15 @@ package com.android.systemui.statusbar.car; import android.annotation.NonNull; import android.car.Car; -import android.car.CarNotConnectedException; +import android.car.Car.CarServiceLifecycleListener; import android.car.hardware.power.CarPowerManager; import android.car.hardware.power.CarPowerManager.CarPowerStateListener; -import android.content.ComponentName; import android.content.Context; -import android.content.ServiceConnection; -import android.os.IBinder; import android.util.Log; +import com.android.systemui.CarSystemUIFactory; +import com.android.systemui.SystemUIFactory; + /** * Helper class for connecting to the {@link CarPowerManager} and listening for power state changes. */ @@ -36,58 +36,32 @@ public class PowerManagerHelper { private final Context mContext; private final CarPowerStateListener mCarPowerStateListener; - private Car mCar; private CarPowerManager mCarPowerManager; - private final ServiceConnection mCarConnectionListener = - new ServiceConnection() { - public void onServiceConnected(ComponentName name, IBinder service) { - Log.d(TAG, "Car Service connected"); - try { - mCarPowerManager = (CarPowerManager) mCar.getCarManager(Car.POWER_SERVICE); - if (mCarPowerManager != null) { - mCarPowerManager.setListener(mCarPowerStateListener); - } else { - Log.e(TAG, "CarPowerManager service not available"); - } - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car not connected", e); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - destroyCarPowerManager(); - } - }; + private final CarServiceLifecycleListener mCarServiceLifecycleListener; PowerManagerHelper(Context context, @NonNull CarPowerStateListener listener) { mContext = context; mCarPowerStateListener = listener; + mCarServiceLifecycleListener = (car, ready) -> { + if (!ready) { + return; + } + Log.d(TAG, "Car Service connected"); + mCarPowerManager = (CarPowerManager) car.getCarManager(Car.POWER_SERVICE); + if (mCarPowerManager != null) { + mCarPowerManager.setListener(mCarPowerStateListener); + } else { + Log.e(TAG, "CarPowerManager service not available"); + } + }; } /** * Connect to Car service. */ void connectToCarService() { - mCar = Car.createCar(mContext, mCarConnectionListener); - if (mCar != null) { - mCar.connect(); - } - } - - /** - * Disconnects from Car service. - */ - void disconnectFromCarService() { - if (mCar != null) { - mCar.disconnect(); - } - } - - private void destroyCarPowerManager() { - if (mCarPowerManager != null) { - mCarPowerManager.clearListener(); - } + ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext) + .addListener(mCarServiceLifecycleListener); } } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java index 30429eda7be7..1d9675034bc5 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java @@ -20,17 +20,17 @@ import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS; import android.car.Car; +import android.car.Car.CarServiceLifecycleListener; import android.car.VehicleUnit; import android.car.hardware.CarPropertyValue; import android.car.hardware.hvac.CarHvacManager; import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback; -import android.content.ComponentName; import android.content.Context; -import android.content.ServiceConnection; -import android.os.Handler; -import android.os.IBinder; import android.util.Log; +import com.android.systemui.CarSystemUIFactory; +import com.android.systemui.SystemUIFactory; + import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -43,15 +43,12 @@ import java.util.Objects; * {@link TemperatureView}s */ public class HvacController { - public static final String TAG = "HvacController"; - public static final int BIND_TO_HVAC_RETRY_DELAY = 5000; private Context mContext; - private Handler mHandler; - private Car mCar; private CarHvacManager mHvacManager; private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>(); + /** * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to * match. @@ -83,39 +80,17 @@ public class HvacController { + " zone: " + zone); } }; - /** - * If the connection to car service goes away then restart it. - */ - private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() { - @Override - public void binderDied() { - Log.d(TAG, "Death of HVAC triggering a restart"); - if (mCar != null) { - mCar.disconnect(); - } - destroyHvacManager(); - mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY); - } - }; - /** - * Registers callbacks and initializes components upon connection. - */ - private ServiceConnection mServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - try { - service.linkToDeath(mRestart, 0); - mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE); - mHvacManager.registerCallback(mHardwareCallback); - initComponents(); - } catch (Exception e) { - Log.e(TAG, "Failed to correctly connect to HVAC", e); - } - } - @Override - public void onServiceDisconnected(ComponentName name) { - destroyHvacManager(); + private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> { + if (!ready) { + return; + } + try { + mHvacManager = (CarHvacManager) car.getCarManager(Car.HVAC_SERVICE); + mHvacManager.registerCallback(mHardwareCallback); + initComponents(); + } catch (Exception e) { + Log.e(TAG, "Failed to correctly connect to HVAC", e); } }; @@ -128,19 +103,8 @@ public class HvacController { * ({@link CarHvacManager}) will happen on the same thread this method was called from. */ public void connectToCarService() { - mHandler = new Handler(); - mCar = Car.createCar(mContext, mServiceConnection, mHandler); - if (mCar != null) { - // note: this connect call handles the retries - mCar.connect(); - } - } - - private void destroyHvacManager() { - if (mHvacManager != null) { - mHvacManager.unregisterCallback(mHardwareCallback); - mHvacManager = null; - } + ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext) + .addListener(mCarServiceLifecycleListener); } /** diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java index 71cc19b63ac1..aa78b9604285 100644 --- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java @@ -31,6 +31,10 @@ public class CarVolumeDialogComponent extends VolumeDialogComponent { } protected VolumeDialog createDefault() { - return new CarVolumeDialogImpl(mContext); + CarVolumeDialogImpl carVolumeDialog = new CarVolumeDialogImpl(mContext); + // Since VolumeUI is initialized when the first Volume Up/Down event is received we need to + // show the dialog on initialization too. + carVolumeDialog.show(Events.SHOW_REASON_VOLUME_CHANGED); + return carVolumeDialog; } } diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java index d0a63f058291..f91c90ec636f 100644 --- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java +++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java @@ -24,12 +24,10 @@ import android.annotation.Nullable; import android.app.Dialog; import android.app.KeyguardManager; import android.car.Car; -import android.car.CarNotConnectedException; +import android.car.Car.CarServiceLifecycleListener; import android.car.media.CarAudioManager; -import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; -import android.content.ServiceConnection; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Color; @@ -39,7 +37,6 @@ import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.os.Debug; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.util.AttributeSet; @@ -58,14 +55,15 @@ import android.widget.SeekBar.OnSeekBarChangeListener; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.android.systemui.CarSystemUIFactory; import com.android.systemui.R; +import com.android.systemui.SystemUIFactory; import com.android.systemui.plugins.VolumeDialog; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; /** @@ -79,8 +77,6 @@ public class CarVolumeDialogImpl implements VolumeDialog { private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems"; private static final String XML_TAG_VOLUME_ITEM = "item"; - private static final int HOVERING_TIMEOUT = 16000; - private static final int NORMAL_TIMEOUT = 3000; private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250; private static final int DISMISS_DELAY_IN_MILLIS = 50; private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100; @@ -94,12 +90,24 @@ public class CarVolumeDialogImpl implements VolumeDialog { // Volume items in the RecyclerView. private final List<CarVolumeItem> mCarVolumeLineItems = new ArrayList<>(); private final KeyguardManager mKeyguard; + private final int mNormalTimeout; + private final int mHoveringTimeout; + private final int mExpNormalTimeout; + private final int mExpHoveringTimeout; + private Window mWindow; private CustomDialog mDialog; private RecyclerView mListView; private CarVolumeItemAdapter mVolumeItemsAdapter; - private Car mCar; private CarAudioManager mCarAudioManager; + private boolean mHovering; + private int mCurrentlyDisplayingGroupId; + private int mPreviouslyDisplayingGroupId; + private boolean mShowing; + private boolean mDismissing; + private boolean mExpanded; + private View mExpandIcon; + private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback = new CarAudioManager.CarVolumeCallback() { @Override @@ -129,6 +137,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { volumeItem.progress = value; } if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { + mPreviouslyDisplayingGroupId = mCurrentlyDisplayingGroupId; mCurrentlyDisplayingGroupId = groupId; mHandler.obtainMessage(H.SHOW, Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget(); @@ -140,80 +149,51 @@ public class CarVolumeDialogImpl implements VolumeDialog { // ignored } }; - private boolean mHovering; - private int mCurrentlyDisplayingGroupId; - private boolean mShowing; - private boolean mExpanded; - private View mExpandIcon; - private final ServiceConnection mServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - try { - mExpanded = false; - mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE); - int volumeGroupCount = mCarAudioManager.getVolumeGroupCount(); - // Populates volume slider items from volume groups to UI. - for (int groupId = 0; groupId < volumeGroupCount; groupId++) { - VolumeItem volumeItem = getVolumeItemForUsages( - mCarAudioManager.getUsagesForVolumeGroupId(groupId)); - mAvailableVolumeItems.add(volumeItem); - // The first one is the default item. - if (groupId == 0) { - setuptListItem(0); - } - } - // If list is already initiated, update its content. - if (mVolumeItemsAdapter != null) { - mVolumeItemsAdapter.notifyDataSetChanged(); - } - mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); + private final CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> { + if (!ready) { + return; + } + mExpanded = false; + mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE); + int volumeGroupCount = mCarAudioManager.getVolumeGroupCount(); + // Populates volume slider items from volume groups to UI. + for (int groupId = 0; groupId < volumeGroupCount; groupId++) { + VolumeItem volumeItem = getVolumeItemForUsages( + mCarAudioManager.getUsagesForVolumeGroupId(groupId)); + mAvailableVolumeItems.add(volumeItem); + // The first one is the default item. + if (groupId == 0) { + clearAllAndSetupDefaultCarVolumeLineItem(0); } } - /** - * This does not get called when service is properly disconnected. - * So we need to also handle cleanups in destroy(). - */ - @Override - public void onServiceDisconnected(ComponentName name) { - cleanupAudioManager(); + // If list is already initiated, update its content. + if (mVolumeItemsAdapter != null) { + mVolumeItemsAdapter.notifyDataSetChanged(); } + mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback); }; - private void setuptListItem(int groupId) { - mCarVolumeLineItems.clear(); - VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); - volumeItem.defaultItem = true; - addCarVolumeListItem(volumeItem, /* volumeGroupId = */ groupId, - R.drawable.car_ic_keyboard_arrow_down, new ExpandIconListener() - ); - } - public CarVolumeDialogImpl(Context context) { mContext = context; mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); - mCar = Car.createCar(mContext, mServiceConnection); + mNormalTimeout = mContext.getResources().getInteger( + R.integer.car_volume_dialog_display_normal_timeout); + mHoveringTimeout = mContext.getResources().getInteger( + R.integer.car_volume_dialog_display_hovering_timeout); + mExpNormalTimeout = mContext.getResources().getInteger( + R.integer.car_volume_dialog_display_expanded_normal_timeout); + mExpHoveringTimeout = mContext.getResources().getInteger( + R.integer.car_volume_dialog_display_expanded_hovering_timeout); } private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) { - try { - return carAudioManager.getGroupVolume(volumeGroupId); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); - } - return 0; + return carAudioManager.getGroupVolume(volumeGroupId); } private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) { - try { - return carAudioManager.getGroupMaxVolume(volumeGroupId); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); - } - return 0; + return carAudioManager.getGroupMaxVolume(volumeGroupId); } /** @@ -224,17 +204,15 @@ public class CarVolumeDialogImpl implements VolumeDialog { public void init(int windowType, Callback callback) { initDialog(); - mCar.connect(); + ((CarSystemUIFactory) SystemUIFactory.getInstance()).getCarServiceProvider(mContext) + .addListener(mCarServiceLifecycleListener); } @Override public void destroy() { - mHandler.removeCallbacksAndMessages(null); + mHandler.removeCallbacksAndMessages(/* token= */ null); cleanupAudioManager(); - // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup - // audio manager beforehand. - mCar.disconnect(); } private void initDialog() { @@ -244,6 +222,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { mHovering = false; mShowing = false; + mDismissing = false; mExpanded = false; mWindow = mDialog.getWindow(); mWindow.requestFeature(Window.FEATURE_NO_TITLE); @@ -293,6 +272,19 @@ public class CarVolumeDialogImpl implements VolumeDialog { mListView.setLayoutManager(new LinearLayoutManager(mContext)); } + /** + * Reveals volume dialog. + */ + public void show(int reason) { + mHandler.obtainMessage(H.SHOW, reason).sendToTarget(); + } + + /** + * Hides volume dialog. + */ + public void dismiss(int reason) { + mHandler.obtainMessage(H.DISMISS, reason).sendToTarget(); + } private void showH(int reason) { if (D.BUG) { @@ -301,19 +293,36 @@ public class CarVolumeDialogImpl implements VolumeDialog { mHandler.removeMessages(H.SHOW); mHandler.removeMessages(H.DISMISS); + rescheduleTimeoutH(); + // Refresh the data set before showing. mVolumeItemsAdapter.notifyDataSetChanged(); + if (mShowing) { + if (mPreviouslyDisplayingGroupId == mCurrentlyDisplayingGroupId || mExpanded) { + return; + } + + clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId); return; } + mShowing = true; - setuptListItem(mCurrentlyDisplayingGroupId); + clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId); mDialog.show(); Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked()); } - private void rescheduleTimeoutH() { + private void clearAllAndSetupDefaultCarVolumeLineItem(int groupId) { + mCarVolumeLineItems.clear(); + VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); + volumeItem.defaultItem = true; + addCarVolumeListItem(volumeItem, /* volumeGroupId = */ groupId, + R.drawable.car_ic_keyboard_arrow_down, new ExpandIconListener()); + } + + protected void rescheduleTimeoutH() { mHandler.removeMessages(H.DISMISS); final int timeout = computeTimeoutH(); mHandler.sendMessageDelayed(mHandler @@ -325,7 +334,11 @@ public class CarVolumeDialogImpl implements VolumeDialog { } private int computeTimeoutH() { - return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT; + if (mExpanded) { + return mHovering ? mExpHoveringTimeout : mExpNormalTimeout; + } else { + return mHovering ? mHoveringTimeout : mNormalTimeout; + } } private void dismissH(int reason) { @@ -335,14 +348,11 @@ public class CarVolumeDialogImpl implements VolumeDialog { mHandler.removeMessages(H.DISMISS); mHandler.removeMessages(H.SHOW); - if (!mShowing) { + if (!mShowing || mDismissing) { return; } - mListView.animate().cancel(); - - mListView.setTranslationY(0); - mListView.setAlpha(1); + mDismissing = true; mListView.animate() .alpha(0) .translationY(-mListView.getHeight()) @@ -354,7 +364,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { } mDialog.dismiss(); mShowing = false; - mShowing = false; + mDismissing = false; // if mExpandIcon is null that means user never clicked on the expanded arrow // which implies that the dialog is still not expanded. In that case we do // not want to reset the state @@ -390,12 +400,13 @@ public class CarVolumeDialogImpl implements VolumeDialog { if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) { TypedArray item = mContext.getResources().obtainAttributes( attrs, R.styleable.carVolumeItems_item); - int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1); + int usage = item.getInt(R.styleable.carVolumeItems_item_usage, + /* defValue= */ -1); if (usage >= 0) { VolumeItem volumeItem = new VolumeItem(); volumeItem.rank = rank; - volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon, - 0); + volumeItem.icon = item.getResourceId( + R.styleable.carVolumeItems_item_icon, /* defValue= */ 0); mVolumeItems.put(usage, volumeItem); rank++; } @@ -420,22 +431,22 @@ public class CarVolumeDialogImpl implements VolumeDialog { return result; } - private CarVolumeItem addCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId, - int supplementalIconId, + private CarVolumeItem createCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId, + Drawable supplementalIcon, int seekbarProgressValue, @Nullable View.OnClickListener supplementalIconOnClickListener) { CarVolumeItem carVolumeItem = new CarVolumeItem(); carVolumeItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId)); - int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint); - int progress = getSeekbarValue(mCarAudioManager, volumeGroupId); - carVolumeItem.setProgress(progress); + carVolumeItem.setProgress(seekbarProgressValue); carVolumeItem.setOnSeekBarChangeListener( new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager)); - Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon); + carVolumeItem.setGroupId(volumeGroupId); + + int color = mContext.getColor(R.color.car_volume_dialog_tint); + Drawable primaryIcon = mContext.getDrawable(volumeItem.icon); primaryIcon.mutate().setTint(color); carVolumeItem.setPrimaryIcon(primaryIcon); - if (supplementalIconId != 0) { - Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId); + if (supplementalIcon != null) { supplementalIcon.mutate().setTint(color); carVolumeItem.setSupplementalIcon(supplementalIcon, /* showSupplementalIconDivider= */ true); @@ -444,21 +455,23 @@ public class CarVolumeDialogImpl implements VolumeDialog { carVolumeItem.setSupplementalIcon(/* drawable= */ null, /* showSupplementalIconDivider= */ false); } - carVolumeItem.setGroupId(volumeGroupId); - mCarVolumeLineItems.add(carVolumeItem); + volumeItem.carVolumeItem = carVolumeItem; - volumeItem.progress = progress; + volumeItem.progress = seekbarProgressValue; + return carVolumeItem; } - private VolumeItem findVolumeItem(CarVolumeItem targetItem) { - for (int i = 0; i < mVolumeItems.size(); ++i) { - VolumeItem volumeItem = mVolumeItems.valueAt(i); - if (volumeItem.carVolumeItem == targetItem) { - return volumeItem; - } - } - return null; + private CarVolumeItem addCarVolumeListItem(VolumeItem volumeItem, int volumeGroupId, + int supplementalIconId, + @Nullable View.OnClickListener supplementalIconOnClickListener) { + int seekbarProgressValue = getSeekbarValue(mCarAudioManager, volumeGroupId); + Drawable supplementalIcon = supplementalIconId == 0 ? null : mContext.getDrawable( + supplementalIconId); + CarVolumeItem carVolumeItem = createCarVolumeListItem(volumeItem, volumeGroupId, + supplementalIcon, seekbarProgressValue, supplementalIconOnClickListener); + mCarVolumeLineItems.add(carVolumeItem); + return carVolumeItem; } private void cleanupAudioManager() { @@ -544,6 +557,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { public void onClick(final View v) { mExpandIcon = v; toggleDialogExpansion(true); + rescheduleTimeoutH(); } } @@ -554,21 +568,15 @@ public class CarVolumeDialogImpl implements VolumeDialog { for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) { if (groupId != mCurrentlyDisplayingGroupId) { VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); - addCarVolumeListItem(volumeItem, groupId, 0, null); + addCarVolumeListItem(volumeItem, groupId, /* supplementalIconId= */ 0, + /* supplementalIconOnClickListener= */ null); } } inAnimator = AnimatorInflater.loadAnimator( mContext, R.anim.car_arrow_fade_in_rotate_up); } else { - // Only keeping the default stream if it is not expended. - Iterator itr = mCarVolumeLineItems.iterator(); - while (itr.hasNext()) { - CarVolumeItem carVolumeItem = (CarVolumeItem) itr.next(); - if (carVolumeItem.getGroupId() != mCurrentlyDisplayingGroupId) { - itr.remove(); - } - } + clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId); inAnimator = AnimatorInflater.loadAnimator( mContext, R.anim.car_arrow_fade_in_rotate_down); } @@ -606,18 +614,14 @@ public class CarVolumeDialogImpl implements VolumeDialog { // sent back down again. return; } - try { - if (mCarAudioManager == null) { - Log.w(TAG, "Ignoring volume change event because the car isn't connected"); - return; - } - mAvailableVolumeItems.get(mVolumeGroupId).progress = progress; - mAvailableVolumeItems.get( - mVolumeGroupId).carVolumeItem.setProgress(progress); - mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0); - } catch (CarNotConnectedException e) { - Log.e(TAG, "Car is not connected!", e); + if (mCarAudioManager == null) { + Log.w(TAG, "Ignoring volume change event because the car isn't connected"); + return; } + mAvailableVolumeItems.get(mVolumeGroupId).progress = progress; + mAvailableVolumeItems.get( + mVolumeGroupId).carVolumeItem.setProgress(progress); + mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0); } @Override diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index c2ce84023869..9ccb837cf613 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -56,6 +56,7 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.image.DynamicSystemClient; import android.os.image.DynamicSystemManager; +import android.text.TextUtils; import android.util.Log; import android.widget.Toast; @@ -74,6 +75,8 @@ public class DynamicSystemInstallationService extends Service // TODO (b/131866826): This is currently for test only. Will move this to System API. static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED"; + static final String KEY_DSU_SLOT = "KEY_DSU_SLOT"; + static final String DEFAULT_DSU_SLOT = "dsu"; /* * Intent actions @@ -244,10 +247,15 @@ public class DynamicSystemInstallationService extends Service long systemSize = intent.getLongExtra(DynamicSystemClient.KEY_SYSTEM_SIZE, 0); long userdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0); mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false); + String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT); + if (TextUtils.isEmpty(dsuSlot)) { + dsuSlot = DEFAULT_DSU_SLOT; + } // TODO: better constructor or builder - mInstallTask = new InstallationAsyncTask( - url, systemSize, userdataSize, this, mDynSystem, this); + mInstallTask = + new InstallationAsyncTask( + url, dsuSlot, systemSize, userdataSize, this, mDynSystem, this); mInstallTask.execute(); @@ -409,7 +417,9 @@ public class DynamicSystemInstallationService extends Service break; case STATUS_READY: - builder.setContentText(getString(R.string.notification_install_completed)); + String msgCompleted = getString(R.string.notification_install_completed); + builder.setContentText(msgCompleted) + .setStyle(new Notification.BigTextStyle().bigText(msgCompleted)); builder.addAction(new Notification.Action.Builder( null, getString(R.string.notification_action_discard), @@ -422,7 +432,9 @@ public class DynamicSystemInstallationService extends Service break; case STATUS_IN_USE: - builder.setContentText(getString(R.string.notification_dynsystem_in_use)); + String msgInUse = getString(R.string.notification_dynsystem_in_use); + builder.setContentText(msgInUse) + .setStyle(new Notification.BigTextStyle().bigText(msgInUse)); builder.addAction(new Notification.Action.Builder( null, getString(R.string.notification_action_uninstall), @@ -452,7 +464,49 @@ public class DynamicSystemInstallationService extends Service } private void postStatus(int status, int cause, Throwable detail) { - Log.d(TAG, "postStatus(): statusCode=" + status + ", causeCode=" + cause); + String statusString; + String causeString; + + switch (status) { + case STATUS_NOT_STARTED: + statusString = "NOT_STARTED"; + break; + case STATUS_IN_PROGRESS: + statusString = "IN_PROGRESS"; + break; + case STATUS_READY: + statusString = "READY"; + break; + case STATUS_IN_USE: + statusString = "IN_USE"; + break; + default: + statusString = "UNKNOWN"; + break; + } + + switch (cause) { + case CAUSE_INSTALL_COMPLETED: + causeString = "INSTALL_COMPLETED"; + break; + case CAUSE_INSTALL_CANCELLED: + causeString = "INSTALL_CANCELLED"; + break; + case CAUSE_ERROR_IO: + causeString = "ERROR_IO"; + break; + case CAUSE_ERROR_INVALID_URL: + causeString = "ERROR_INVALID_URL"; + break; + case CAUSE_ERROR_EXCEPTION: + causeString = "ERROR_EXCEPTION"; + break; + default: + causeString = "CAUSE_NOT_SPECIFIED"; + break; + } + + Log.d(TAG, "status=" + statusString + ", cause=" + causeString); boolean notifyOnNotificationBar = true; diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java index b206a1fccba4..9aea0e713179 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java @@ -89,10 +89,12 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog interface ProgressListener { void onProgressUpdate(Progress progress); + void onResult(int resultCode, Throwable detail); } private final String mUrl; + private final String mDsuSlot; private final long mSystemSize; private final long mUserdataSize; private final Context mContext; @@ -106,9 +108,16 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog private InputStream mStream; private ZipFile mZipFile; - InstallationAsyncTask(String url, long systemSize, long userdataSize, Context context, - DynamicSystemManager dynSystem, ProgressListener listener) { + InstallationAsyncTask( + String url, + String dsuSlot, + long systemSize, + long userdataSize, + Context context, + DynamicSystemManager dynSystem, + ProgressListener listener) { mUrl = url; + mDsuSlot = dsuSlot; mSystemSize = systemSize; mUserdataSize = userdataSize; mContext = context; @@ -126,14 +135,17 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog verifyAndPrepare(); - mDynSystem.startInstallation(); + mDynSystem.startInstallation(mDsuSlot); installUserdata(); if (isCancelled()) { mDynSystem.remove(); return null; } - + if (mUrl == null) { + mDynSystem.finishInstallation(); + return null; + } installImages(); if (isCancelled()) { mDynSystem.remove(); @@ -194,6 +206,9 @@ class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Prog } private void verifyAndPrepare() throws Exception { + if (mUrl == null) { + return; + } String extension = mUrl.substring(mUrl.lastIndexOf('.') + 1); if ("gz".equals(extension) || "gzip".equals(extension)) { diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java index 3b3933b7db10..e42ded74acd0 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java @@ -28,11 +28,9 @@ import android.os.image.DynamicSystemClient; import android.util.FeatureFlagUtils; import android.util.Log; - /** - * This Activity starts KeyguardManager and ask the user to confirm - * before any installation request. If the device is not protected by - * a password, it approves the request by default. + * This Activity starts KeyguardManager and ask the user to confirm before any installation request. + * If the device is not protected by a password, it approves the request by default. */ public class VerificationActivity extends Activity { @@ -88,11 +86,15 @@ public class VerificationActivity extends Activity { Uri url = callingIntent.getData(); Bundle extras = callingIntent.getExtras(); - sVerifiedUrl = url.toString(); + if (url != null) { + sVerifiedUrl = url.toString(); + } // start service Intent intent = new Intent(this, DynamicSystemInstallationService.class); - intent.setData(url); + if (url != null) { + intent.setData(url); + } intent.setAction(DynamicSystemClient.ACTION_START_INSTALL); intent.putExtras(extras); @@ -106,6 +108,7 @@ public class VerificationActivity extends Activity { } static boolean isVerified(String url) { + if (url == null) return true; return sVerifiedUrl != null && sVerifiedUrl.equals(url); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java index 5c9a06f91e6a..69f1c17f97b1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java @@ -18,6 +18,7 @@ package com.android.settingslib.utils; import android.os.Handler; import android.os.Looper; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -26,7 +27,7 @@ public class ThreadUtils { private static volatile Thread sMainThread; private static volatile Handler sMainThreadHandler; - private static volatile ExecutorService sSingleThreadExecutor; + private static volatile ExecutorService sThreadExecutor; /** * Returns true if the current thread is the UI thread. @@ -64,10 +65,16 @@ public class ThreadUtils { * @Return A future of the task that can be monitored for updates or cancelled. */ public static Future postOnBackgroundThread(Runnable runnable) { - if (sSingleThreadExecutor == null) { - sSingleThreadExecutor = Executors.newSingleThreadExecutor(); - } - return sSingleThreadExecutor.submit(runnable); + return getThreadExecutor().submit(runnable); + } + + /** + * Posts callable in background using shared background thread pool. + * + * @Return A future of the task that can be monitored for updates or cancelled. + */ + public static Future postOnBackgroundThread(Callable callable) { + return getThreadExecutor().submit(callable); } /** @@ -77,4 +84,11 @@ public class ThreadUtils { getUiThreadHandler().post(runnable); } + private static synchronized ExecutorService getThreadExecutor() { + if (sThreadExecutor == null) { + sThreadExecutor = Executors.newFixedThreadPool( + Runtime.getRuntime().availableProcessors()); + } + return sThreadExecutor; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 386ff65a4bf4..09c8f7398199 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -177,6 +177,8 @@ public class AccessPoint implements Comparable<AccessPoint> { static final String KEY_CARRIER_AP_EAP_TYPE = "key_carrier_ap_eap_type"; static final String KEY_CARRIER_NAME = "key_carrier_name"; static final String KEY_EAPTYPE = "eap_psktype"; + static final String KEY_IS_PSK_SAE_TRANSITION_MODE = "key_is_psk_sae_transition_mode"; + static final String KEY_IS_OWE_TRANSITION_MODE = "key_is_owe_transition_mode"; static final AtomicInteger sLastId = new AtomicInteger(0); /* @@ -191,15 +193,12 @@ public class AccessPoint implements Comparable<AccessPoint> { public static final int SECURITY_OWE = 4; public static final int SECURITY_SAE = 5; public static final int SECURITY_EAP_SUITE_B = 6; - public static final int SECURITY_PSK_SAE_TRANSITION = 7; - public static final int SECURITY_OWE_TRANSITION = 8; - public static final int SECURITY_MAX_VAL = 9; // Has to be the last + public static final int SECURITY_MAX_VAL = 7; // Has to be the last private static final int PSK_UNKNOWN = 0; private static final int PSK_WPA = 1; private static final int PSK_WPA2 = 2; private static final int PSK_WPA_WPA2 = 3; - private static final int PSK_SAE = 4; private static final int EAP_UNKNOWN = 0; private static final int EAP_WPA = 1; // WPA-EAP @@ -260,6 +259,9 @@ public class AccessPoint implements Comparable<AccessPoint> { private String mOsuFailure; private boolean mOsuProvisioningComplete = false; + private boolean mIsPskSaeTransitionMode = false; + private boolean mIsOweTransitionMode = false; + /** * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP. */ @@ -323,6 +325,13 @@ public class AccessPoint implements Comparable<AccessPoint> { if (savedState.containsKey(KEY_CARRIER_NAME)) { mCarrierName = savedState.getString(KEY_CARRIER_NAME); } + if (savedState.containsKey(KEY_IS_PSK_SAE_TRANSITION_MODE)) { + mIsPskSaeTransitionMode = savedState.getBoolean(KEY_IS_PSK_SAE_TRANSITION_MODE); + } + if (savedState.containsKey(KEY_IS_OWE_TRANSITION_MODE)) { + mIsOweTransitionMode = savedState.getBoolean(KEY_IS_OWE_TRANSITION_MODE); + } + update(mConfig, mInfo, mNetworkInfo); // Calculate required fields @@ -648,8 +657,15 @@ public class AccessPoint implements Comparable<AccessPoint> { return oldMetering == mIsScoredNetworkMetered; } - public static String getKey(ScanResult result) { - return getKey(result.SSID, result.BSSID, getSecurity(result)); + /** + * Generates an AccessPoint key for a given scan result + * + * @param context + * @param result Scan result + * @return AccessPoint key + */ + public static String getKey(Context context, ScanResult result) { + return getKey(result.SSID, result.BSSID, getSecurity(context, result)); } /** @@ -707,7 +723,42 @@ public class AccessPoint implements Comparable<AccessPoint> { * Determines if the other AccessPoint represents the same network as this AccessPoint */ public boolean matches(AccessPoint other) { - return getKey().equals(other.getKey()); + if (isPasspoint() || isPasspointConfig() || isOsuProvider()) { + return getKey().equals(other.getKey()); + } + + if (!isSameSsidOrBssid(other)) { + return false; + } + + final int otherApSecurity = other.getSecurity(); + if (mIsPskSaeTransitionMode) { + if (otherApSecurity == SECURITY_SAE && getWifiManager().isWpa3SaeSupported()) { + return true; + } else if (otherApSecurity == SECURITY_PSK) { + return true; + } + } else { + if ((security == SECURITY_SAE || security == SECURITY_PSK) + && other.isPskSaeTransitionMode()) { + return true; + } + } + + if (mIsOweTransitionMode) { + if (otherApSecurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) { + return true; + } else if (otherApSecurity == SECURITY_NONE) { + return true; + } + } else { + if ((security == SECURITY_OWE || security == SECURITY_NONE) + && other.isOweTransitionMode()) { + return true; + } + } + + return security == other.getSecurity(); } public boolean matches(WifiConfiguration config) { @@ -721,18 +772,77 @@ public class AccessPoint implements Comparable<AccessPoint> { } final int configSecurity = getSecurity(config); - final WifiManager wifiManager = getWifiManager(); - switch (security) { - case SECURITY_PSK_SAE_TRANSITION: - return configSecurity == SECURITY_PSK - || (wifiManager.isWpa3SaeSupported() && configSecurity == SECURITY_SAE); - case SECURITY_OWE_TRANSITION: - return configSecurity == SECURITY_NONE - || (wifiManager.isEnhancedOpenSupported() - && configSecurity == SECURITY_OWE); - default: - return security == configSecurity; + if (mIsPskSaeTransitionMode) { + if (configSecurity == SECURITY_SAE && getWifiManager().isWpa3SaeSupported()) { + return true; + } else if (configSecurity == SECURITY_PSK) { + return true; + } + } + + if (mIsOweTransitionMode) { + if (configSecurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) { + return true; + } else if (configSecurity == SECURITY_NONE) { + return true; + } + } + + return security == getSecurity(config); + } + + private boolean matches(WifiConfiguration config, WifiInfo wifiInfo) { + if (config == null || wifiInfo == null) { + return false; + } + if (!config.isPasspoint() && !isSameSsidOrBssid(wifiInfo)) { + return false; + } + return matches(config); + } + + @VisibleForTesting + boolean matches(ScanResult scanResult) { + if (scanResult == null) { + return false; + } + if (isPasspoint() || isOsuProvider()) { + throw new IllegalStateException("Should not matches a Passpoint by ScanResult"); + } + + if (!isSameSsidOrBssid(scanResult)) { + return false; + } + + if (mIsPskSaeTransitionMode) { + if (scanResult.capabilities.contains("SAE") + && getWifiManager().isWpa3SaeSupported()) { + return true; + } else if (scanResult.capabilities.contains("PSK")) { + return true; + } + } else { + if ((security == SECURITY_SAE || security == SECURITY_PSK) + && AccessPoint.isPskSaeTransitionMode(scanResult)) { + return true; + } + } + + if (mIsOweTransitionMode) { + final int scanResultSccurity = getSecurity(mContext, scanResult); + if (scanResultSccurity == SECURITY_OWE && getWifiManager().isEnhancedOpenSupported()) { + return true; + } else if (scanResultSccurity == SECURITY_NONE) { + return true; + } + } else { + if ((security == SECURITY_OWE || security == SECURITY_NONE) + && AccessPoint.isOweTransitionMode(scanResult)) { + return true; + } } + + return security == getSecurity(mContext, scanResult); } public WifiConfiguration getConfig() { @@ -819,14 +929,17 @@ public class AccessPoint implements Comparable<AccessPoint> { if (bestResult != null) { ssid = bestResult.SSID; bssid = bestResult.BSSID; - security = getSecurity(bestResult); - if (security == SECURITY_PSK || security == SECURITY_SAE - || security == SECURITY_PSK_SAE_TRANSITION) { + security = getSecurity(mContext, bestResult); + if (security == SECURITY_PSK || security == SECURITY_SAE) { pskType = getPskType(bestResult); } if (security == SECURITY_EAP) { mEapType = getEapType(bestResult); } + + mIsPskSaeTransitionMode = AccessPoint.isPskSaeTransitionMode(bestResult); + mIsOweTransitionMode = AccessPoint.isOweTransitionMode(bestResult); + mIsCarrierAp = bestResult.isCarrierAp; mCarrierApEapType = bestResult.carrierApEapType; mCarrierName = bestResult.carrierName; @@ -859,6 +972,12 @@ public class AccessPoint implements Comparable<AccessPoint> { return concise ? context.getString(R.string.wifi_security_short_eap) : context.getString(R.string.wifi_security_eap); } + + if (mIsPskSaeTransitionMode) { + return concise ? context.getString(R.string.wifi_security_short_psk_sae) : + context.getString(R.string.wifi_security_psk_sae); + } + switch(security) { case SECURITY_EAP: switch (mEapType) { @@ -898,20 +1017,8 @@ public class AccessPoint implements Comparable<AccessPoint> { return concise ? context.getString(R.string.wifi_security_short_wep) : context.getString(R.string.wifi_security_wep); case SECURITY_SAE: - case SECURITY_PSK_SAE_TRANSITION: - if (pskType == PSK_SAE) { - return concise ? context.getString(R.string.wifi_security_short_psk_sae) : - context.getString(R.string.wifi_security_psk_sae); - } else { - return concise ? context.getString(R.string.wifi_security_short_sae) : - context.getString(R.string.wifi_security_sae); - } - case SECURITY_OWE_TRANSITION: - if (mConfig != null && getSecurity(mConfig) == SECURITY_OWE) { - return concise ? context.getString(R.string.wifi_security_short_owe) : - context.getString(R.string.wifi_security_owe); - } - return concise ? "" : context.getString(R.string.wifi_security_none); + return concise ? context.getString(R.string.wifi_security_short_sae) : + context.getString(R.string.wifi_security_sae); case SECURITY_OWE: return concise ? context.getString(R.string.wifi_security_short_owe) : context.getString(R.string.wifi_security_owe); @@ -1195,7 +1302,7 @@ public class AccessPoint implements Comparable<AccessPoint> { if (networkId != WifiConfiguration.INVALID_NETWORK_ID) { return networkId == info.getNetworkId(); } else if (config != null) { - return isKeyEqual(getKey(config)); + return matches(config, info); } else { // Might be an ephemeral connection with no WifiConfiguration. Try matching on SSID. // (Note that we only do this if the WifiConfiguration explicitly equals INVALID). @@ -1221,8 +1328,7 @@ public class AccessPoint implements Comparable<AccessPoint> { * Can only be called for unsecured networks. */ public void generateOpenNetworkConfig() { - if ((security != SECURITY_NONE) && (security != SECURITY_OWE) - && (security != SECURITY_OWE_TRANSITION)) { + if ((security != SECURITY_NONE) && (security != SECURITY_OWE)) { throw new IllegalStateException(); } if (mConfig != null) @@ -1265,43 +1371,14 @@ public class AccessPoint implements Comparable<AccessPoint> { savedState.putBoolean(KEY_IS_CARRIER_AP, mIsCarrierAp); savedState.putInt(KEY_CARRIER_AP_EAP_TYPE, mCarrierApEapType); savedState.putString(KEY_CARRIER_NAME, mCarrierName); + savedState.putBoolean(KEY_IS_PSK_SAE_TRANSITION_MODE, mIsPskSaeTransitionMode); + savedState.putBoolean(KEY_IS_OWE_TRANSITION_MODE, mIsOweTransitionMode); } public void setListener(AccessPointListener listener) { mAccessPointListener = listener; } - private static final String sPskSuffix = "," + String.valueOf(SECURITY_PSK); - private static final String sSaeSuffix = "," + String.valueOf(SECURITY_SAE); - private static final String sPskSaeSuffix = "," + String.valueOf(SECURITY_PSK_SAE_TRANSITION); - private static final String sOweSuffix = "," + String.valueOf(SECURITY_OWE); - private static final String sOpenSuffix = "," + String.valueOf(SECURITY_NONE); - private static final String sOweTransSuffix = "," + String.valueOf(SECURITY_OWE_TRANSITION); - - private boolean isKeyEqual(String compareTo) { - if (mKey == null) { - return false; - } - - if (compareTo.endsWith(sPskSuffix) || compareTo.endsWith(sSaeSuffix)) { - if (mKey.endsWith(sPskSaeSuffix)) { - // Special handling for PSK-SAE transition mode. If the AP has advertised both, - // we compare the key with both PSK and SAE for a match. - return TextUtils.equals(mKey.substring(0, mKey.lastIndexOf(',')), - compareTo.substring(0, compareTo.lastIndexOf(','))); - } - } - if (compareTo.endsWith(sOpenSuffix) || compareTo.endsWith(sOweSuffix)) { - if (mKey.endsWith(sOweTransSuffix)) { - // Special handling for OWE/Open networks. If AP advertises OWE in transition mode - // and we have an Open network saved, allow this connection to be established. - return TextUtils.equals(mKey.substring(0, mKey.lastIndexOf(',')), - compareTo.substring(0, compareTo.lastIndexOf(','))); - } - } - return mKey.equals(compareTo); - } - /** * Sets {@link #mScanResults} to the given collection and updates info based on the best RSSI * scan result. @@ -1318,11 +1395,10 @@ public class AccessPoint implements Comparable<AccessPoint> { // Passpoint networks are not bound to a specific SSID/BSSID, so skip this for passpoint. if (mKey != null && !isPasspoint() && !isOsuProvider()) { for (ScanResult result : scanResults) { - String scanResultKey = AccessPoint.getKey(result); - if (!isKeyEqual(scanResultKey)) { + if (!matches(result)) { Log.d(TAG, String.format( - "ScanResult %s\nkey of %s did not match current AP key %s", - result, scanResultKey, mKey)); + "ScanResult %s\nkey of %s did not match current AP key %s", + result, getKey(mContext, result), mKey)); return; } } @@ -1601,11 +1677,8 @@ public class AccessPoint implements Comparable<AccessPoint> { private static int getPskType(ScanResult result) { boolean wpa = result.capabilities.contains("WPA-PSK"); boolean wpa2 = result.capabilities.contains("RSN-PSK"); - boolean wpa3TransitionMode = result.capabilities.contains("PSK+SAE"); boolean wpa3 = result.capabilities.contains("RSN-SAE"); - if (wpa3TransitionMode) { - return PSK_SAE; - } else if (wpa2 && wpa) { + if (wpa2 && wpa) { return PSK_WPA_WPA2; } else if (wpa2) { return PSK_WPA2; @@ -1632,22 +1705,37 @@ public class AccessPoint implements Comparable<AccessPoint> { return EAP_UNKNOWN; } - private static int getSecurity(ScanResult result) { - if (result.capabilities.contains("WEP")) { + private static int getSecurity(Context context, ScanResult result) { + final boolean isWep = result.capabilities.contains("WEP"); + final boolean isSae = result.capabilities.contains("SAE"); + final boolean isPsk = result.capabilities.contains("PSK"); + final boolean isEapSuiteB192 = result.capabilities.contains("EAP_SUITE_B_192"); + final boolean isEap = result.capabilities.contains("EAP"); + final boolean isOwe = result.capabilities.contains("OWE"); + final boolean isOweTransition = result.capabilities.contains("OWE_TRANSITION"); + + if (isSae && isPsk) { + final WifiManager wifiManager = (WifiManager) + context.getSystemService(Context.WIFI_SERVICE); + return wifiManager.isWpa3SaeSupported() ? SECURITY_SAE : SECURITY_PSK; + } + if (isOweTransition) { + final WifiManager wifiManager = (WifiManager) + context.getSystemService(Context.WIFI_SERVICE); + return wifiManager.isEnhancedOpenSupported() ? SECURITY_OWE : SECURITY_NONE; + } + + if (isWep) { return SECURITY_WEP; - } else if (result.capabilities.contains("PSK+SAE")) { - return SECURITY_PSK_SAE_TRANSITION; - } else if (result.capabilities.contains("SAE")) { + } else if (isSae) { return SECURITY_SAE; - } else if (result.capabilities.contains("PSK")) { + } else if (isPsk) { return SECURITY_PSK; - } else if (result.capabilities.contains("EAP_SUITE_B_192")) { + } else if (isEapSuiteB192) { return SECURITY_EAP_SUITE_B; - } else if (result.capabilities.contains("EAP")) { + } else if (isEap) { return SECURITY_EAP; - } else if (result.capabilities.contains("OWE_TRANSITION")) { - return SECURITY_OWE_TRANSITION; - } else if (result.capabilities.contains("OWE")) { + } else if (isOwe) { return SECURITY_OWE; } return SECURITY_NONE; @@ -1693,10 +1781,6 @@ public class AccessPoint implements Comparable<AccessPoint> { return "SUITE_B"; } else if (security == SECURITY_OWE) { return "OWE"; - } else if (security == SECURITY_PSK_SAE_TRANSITION) { - return "PSK+SAE"; - } else if (security == SECURITY_OWE_TRANSITION) { - return "OWE_TRANSITION"; } return "NONE"; } @@ -1866,4 +1950,61 @@ public class AccessPoint implements Comparable<AccessPoint> { } } } + + public boolean isPskSaeTransitionMode() { + return mIsPskSaeTransitionMode; + } + + public boolean isOweTransitionMode() { + return mIsOweTransitionMode; + } + + private static boolean isPskSaeTransitionMode(ScanResult scanResult) { + return scanResult.capabilities.contains("PSK") + && scanResult.capabilities.contains("SAE"); + } + + private static boolean isOweTransitionMode(ScanResult scanResult) { + return scanResult.capabilities.contains("OWE_TRANSITION"); + } + + private boolean isSameSsidOrBssid(ScanResult scanResult) { + if (scanResult == null) { + return false; + } + + if (TextUtils.equals(ssid, scanResult.SSID)) { + return true; + } else if (scanResult.BSSID != null && TextUtils.equals(bssid, scanResult.BSSID)) { + return true; + } + return false; + } + + private boolean isSameSsidOrBssid(WifiInfo wifiInfo) { + if (wifiInfo == null) { + return false; + } + + if (TextUtils.equals(ssid, removeDoubleQuotes(wifiInfo.getSSID()))) { + return true; + } else if (wifiInfo.getBSSID() != null && TextUtils.equals(bssid, wifiInfo.getBSSID())) { + return true; + } + return false; + } + + private boolean isSameSsidOrBssid(AccessPoint accessPoint) { + if (accessPoint == null) { + return false; + } + + if (TextUtils.equals(ssid, accessPoint.getSsid())) { + return true; + } else if (accessPoint.getBssid() != null + && TextUtils.equals(bssid, accessPoint.getBssid())) { + return true; + } + return false; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java index dae546497aba..6269a717b333 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java @@ -201,8 +201,7 @@ public class AccessPointPreference extends Preference { return; } if ((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) - && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE) - && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE_TRANSITION)) { + && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE)) { mFrictionSld.setState(STATE_SECURED); } else if (mAccessPoint.isMetered()) { mFrictionSld.setState(STATE_METERED); diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index b11585a73946..8bd5f57f9b71 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -90,7 +90,7 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { public void setListening(boolean listening) { if (listening) { mNetworkScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, - mWifiNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK); + mWifiNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK); mWifiNetworkScoreCache.registerListener(mCacheListener); mConnectivityManager.registerNetworkCallback( mNetworkRequest, mNetworkCallback, mHandler); diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 3e359d216234..d338bc090757 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -70,8 +70,10 @@ import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; /** * Tracks saved or available wifi networks and their state. @@ -359,7 +361,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro mNetworkScoreManager.registerNetworkScoreCache( NetworkKey.TYPE_WIFI, mScoreCache, - NetworkScoreManager.CACHE_FILTER_SCAN_RESULTS); + NetworkScoreManager.SCORE_FILTER_SCAN_RESULTS); } private void requestScoresForNetworkKeys(Collection<NetworkKey> keys) { @@ -475,7 +477,7 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro continue; } - String apKey = AccessPoint.getKey(result); + String apKey = AccessPoint.getKey(mContext, result); List<ScanResult> resultList; if (scanResultsByApKey.containsKey(apKey)) { resultList = scanResultsByApKey.get(apKey); @@ -548,14 +550,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private void updateAccessPoints(final List<ScanResult> newScanResults, List<WifiConfiguration> configs) { - // Map configs and scan results necessary to make AccessPoints - final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size()); - if (configs != null) { - for (WifiConfiguration config : configs) { - configsByKey.put(AccessPoint.getKey(config), config); - } - } - WifiConfiguration connectionConfig = null; if (mLastInfo != null) { connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId(), configs); @@ -587,7 +581,26 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro getCachedOrCreate(entry.getValue(), cachedAccessPoints); // Update the matching config if there is one, to populate saved network info - accessPoint.update(configsByKey.get(entry.getKey())); + final List<WifiConfiguration> matchedConfigs = configs.stream() + .filter(config -> accessPoint.matches(config)) + .collect(Collectors.toList()); + + final int matchedConfigCount = matchedConfigs.size(); + if (matchedConfigCount == 0) { + accessPoint.update(null); + } else if (matchedConfigCount == 1) { + accessPoint.update(matchedConfigs.get(0)); + } else { + // We may have 2 matched configured WifiCongiguration if the AccessPoint is + // of PSK/SAE transition mode or open/OWE transition mode. + Optional<WifiConfiguration> preferredConfig = matchedConfigs.stream() + .filter(config -> isSaeOrOwe(config)).findFirst(); + if (preferredConfig.isPresent()) { + accessPoint.update(preferredConfig.get()); + } else { + accessPoint.update(matchedConfigs.get(0)); + } + } accessPoints.add(accessPoint); } @@ -653,6 +666,11 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro conditionallyNotifyListeners(); } + private static boolean isSaeOrOwe(WifiConfiguration config) { + final int security = AccessPoint.getSecurity(config); + return security == AccessPoint.SECURITY_SAE || security == AccessPoint.SECURITY_OWE; + } + @VisibleForTesting List<AccessPoint> updatePasspointAccessPoints( List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> passpointConfigsAndScans, @@ -701,7 +719,8 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro private AccessPoint getCachedOrCreate( List<ScanResult> scanResults, List<AccessPoint> cache) { - AccessPoint accessPoint = getCachedByKey(cache, AccessPoint.getKey(scanResults.get(0))); + AccessPoint accessPoint = getCachedByKey(cache, + AccessPoint.getKey(mContext, scanResults.get(0))); if (accessPoint == null) { accessPoint = new AccessPoint(mContext, scanResults); } else { diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java index af4704c3618e..77473f52f4be 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java @@ -39,6 +39,7 @@ import android.net.ScoredNetwork; import android.net.WifiKey; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConfiguration.KeyMgmt; import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; @@ -1272,7 +1273,7 @@ public class AccessPointTest { @Test public void testGetKey_matchesKeysCorrectly() { AccessPoint ap = new AccessPoint(mContext, mScanResults); - assertThat(ap.getKey()).isEqualTo(AccessPoint.getKey(mScanResults.get(0))); + assertThat(ap.getKey()).isEqualTo(AccessPoint.getKey(mContext, mScanResults.get(0))); WifiConfiguration spyConfig = spy(new WifiConfiguration()); when(spyConfig.isPasspoint()).thenReturn(true); @@ -1294,6 +1295,44 @@ public class AccessPointTest { } /** + * Test that getKey returns a key of SAE type for a PSK/SAE transition mode ScanResult. + */ + @Test + public void testGetKey_supportSaeTransitionMode_shouldGetSaeKey() { + ScanResult scanResult = createScanResult(TEST_SSID, TEST_BSSID, DEFAULT_RSSI); + scanResult.capabilities = + "[WPA2-FT/PSK-CCMP][RSN-FT/PSK+PSK-SHA256+SAE+FT/SAE-CCMP][ESS][WPS]"; + when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true); + when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager); + StringBuilder key = new StringBuilder(); + key.append(AccessPoint.KEY_PREFIX_AP); + key.append(TEST_SSID); + key.append(','); + key.append(AccessPoint.SECURITY_SAE); + + assertThat(AccessPoint.getKey(mMockContext, scanResult)).isEqualTo(key.toString()); + } + + /** + * Test that getKey returns a key of PSK type for a PSK/SAE transition mode ScanResult. + */ + @Test + public void testGetKey_notSupportSaeTransitionMode_shouldGetPskKey() { + ScanResult scanResult = createScanResult(TEST_SSID, TEST_BSSID, DEFAULT_RSSI); + scanResult.capabilities = + "[WPA2-FT/PSK-CCMP][RSN-FT/PSK+PSK-SHA256+SAE+FT/SAE-CCMP][ESS][WPS]"; + when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false); + when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager); + StringBuilder key = new StringBuilder(); + key.append(AccessPoint.KEY_PREFIX_AP); + key.append(TEST_SSID); + key.append(','); + key.append(AccessPoint.SECURITY_PSK); + + assertThat(AccessPoint.getKey(mMockContext, scanResult)).isEqualTo(key.toString()); + } + + /** * Verifies that the Passpoint AccessPoint constructor creates AccessPoints whose isPasspoint() * returns true. */ @@ -1522,4 +1561,113 @@ public class AccessPointTest { verify(mMockConnectListener).onFailure(anyInt()); } + + /** + * Verifies that matches(AccessPoint other) matches a PSK/SAE transition mode AP to a PSK or a + * SAE AP. + */ + @Test + public void testMatches1_transitionModeApMatchesNotTransitionModeAp_shouldMatchCorrectly() { + when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager); + when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true); + AccessPoint pskSaeTransitionModeAp = getPskSaeTransitionModeAp(); + + // Transition mode AP matches a SAE AP. + AccessPoint saeAccessPoint = new TestAccessPointBuilder(mContext) + .setSsid(AccessPoint.removeDoubleQuotes(TEST_SSID)) + .setSecurity(AccessPoint.SECURITY_SAE) + .build(); + assertThat(pskSaeTransitionModeAp.matches(saeAccessPoint)).isTrue(); + + // Transition mode AP matches a PSK AP. + AccessPoint pskAccessPoint = new TestAccessPointBuilder(mContext) + .setSsid(AccessPoint.removeDoubleQuotes(TEST_SSID)) + .setSecurity(AccessPoint.SECURITY_PSK) + .build(); + + assertThat(pskSaeTransitionModeAp.matches(pskAccessPoint)).isTrue(); + + // Transition mode AP does not match a SAE AP if the device does not support SAE. + when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false); + pskSaeTransitionModeAp = getPskSaeTransitionModeAp(); + saeAccessPoint = new TestAccessPointBuilder(mContext) + .setSsid(AccessPoint.removeDoubleQuotes(TEST_SSID)) + .setSecurity(AccessPoint.SECURITY_SAE) + .build(); + + assertThat(pskSaeTransitionModeAp.matches(saeAccessPoint)).isFalse(); + } + + /** + * Verifies that matches(WifiConfiguration config) matches a PSK/SAE transition mode AP to a PSK + * or a SAE WifiConfiguration. + */ + @Test + public void testMatches2_transitionModeApMatchesNotTransitionModeAp_shouldMatchCorrectly() { + when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager); + when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true); + AccessPoint pskSaeTransitionModeAp = getPskSaeTransitionModeAp(); + + // Transition mode AP matches a SAE WifiConfiguration. + WifiConfiguration saeConfig = new WifiConfiguration(); + saeConfig.SSID = TEST_SSID; + saeConfig.allowedKeyManagement.set(KeyMgmt.SAE); + + assertThat(pskSaeTransitionModeAp.matches(saeConfig)).isTrue(); + + // Transition mode AP matches a PSK WifiConfiguration. + WifiConfiguration pskConfig = new WifiConfiguration(); + pskConfig.SSID = TEST_SSID; + pskConfig.allowedKeyManagement.set(KeyMgmt.WPA_PSK); + + assertThat(pskSaeTransitionModeAp.matches(pskConfig)).isTrue(); + + // Transition mode AP does not matches a SAE WifiConfiguration if the device does not + // support SAE. + when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false); + pskSaeTransitionModeAp = getPskSaeTransitionModeAp(); + + assertThat(pskSaeTransitionModeAp.matches(saeConfig)).isFalse(); + } + + /** + * Verifies that matches(ScanResult scanResult) matches a PSK/SAE transition mode AP to a PSK + * or a SAE ScanResult. + */ + @Test + public void testMatches3_transitionModeApMatchesNotTransitionModeAp_shouldMatchCorrectly() { + when(mMockContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager); + when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(true); + AccessPoint pskSaeTransitionModeAp = getPskSaeTransitionModeAp(); + + // Transition mode AP matches a SAE ScanResult. + ScanResult saeScanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID), + TEST_BSSID, DEFAULT_RSSI); + saeScanResult.capabilities = "[SAE-CCMP][ESS][WPS]"; + + assertThat(pskSaeTransitionModeAp.matches(saeScanResult)).isTrue(); + + // Transition mode AP matches a PSK ScanResult. + ScanResult pskScanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID), + TEST_BSSID, DEFAULT_RSSI); + pskScanResult.capabilities = "[RSN-PSK-CCMP][ESS][WPS]"; + + assertThat(pskSaeTransitionModeAp.matches(pskScanResult)).isTrue(); + + // Transition mode AP does not matches a SAE ScanResult if the device does not support SAE. + when(mMockWifiManager.isWpa3SaeSupported()).thenReturn(false); + pskSaeTransitionModeAp = getPskSaeTransitionModeAp(); + + assertThat(pskSaeTransitionModeAp.matches(saeScanResult)).isFalse(); + } + + private AccessPoint getPskSaeTransitionModeAp() { + ScanResult scanResult = createScanResult(AccessPoint.removeDoubleQuotes(TEST_SSID), + TEST_BSSID, DEFAULT_RSSI); + scanResult.capabilities = + "[WPA2-FT/PSK-CCMP][RSN-FT/PSK+PSK-SHA256+SAE+FT/SAE-CCMP][ESS][WPS]"; + return new TestAccessPointBuilder(mMockContext) + .setScanResults(new ArrayList<ScanResult>(Arrays.asList(scanResult))) + .build(); + } } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER new file mode 100644 index 000000000000..5c2a7b892f8f --- /dev/null +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/OWNER @@ -0,0 +1,4 @@ +# People who can approve changes for submission +arcwang@google.com +govenliu@google.com +qal@google.com diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java index 26db124c0041..5114b00d2711 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java @@ -50,7 +50,7 @@ public class ThreadUtilsTest { } @Test - public void testPostOnMainThread_shouldRunOnMainTread() { + public void testPostOnMainThread_shouldRunOnMainThread() { TestRunnable cr = new TestRunnable(); ShadowLooper.pauseMainLooper(); ThreadUtils.postOnMainThread(cr); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER new file mode 100644 index 000000000000..5c2a7b892f8f --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/OWNER @@ -0,0 +1,4 @@ +# People who can approve changes for submission +arcwang@google.com +govenliu@google.com +qal@google.com diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index f8c9bcce1b1c..21129f927873 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -641,6 +641,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS, GlobalSettingsProto.Euicc.FACTORY_RESET_TIMEOUT_MILLIS); + dumpSetting(s, p, + Settings.Global.EUICC_UNSUPPORTED_COUNTRIES, + GlobalSettingsProto.Euicc.UNSUPPORTED_COUNTRIES); p.end(euiccToken); dumpSetting(s, p, @@ -1969,6 +1972,12 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.SKIP_TOUCH_COUNT, SecureSettingsProto.Gesture.SKIP_TOUCH_COUNT); + dumpSetting(s, p, + Settings.Secure.AWARE_TAP_PAUSE_GESTURE_COUNT, + SecureSettingsProto.Gesture.AWARE_TAP_PAUSE_GESTURE_COUNT); + dumpSetting(s, p, + Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT, + SecureSettingsProto.Gesture.AWARE_TAP_PAUSE_TOUCH_COUNT); p.end(gestureToken); dumpSetting(s, p, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index c05c4cdf72d7..33085a14c65f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -64,6 +64,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -716,6 +718,23 @@ final class SettingsState { } } catch (Throwable t) { Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t); + if (t instanceof IOException) { + // we failed to create a directory, so log the permissions and existence + // state for the settings file and directory + logSettingsDirectoryInformation(destination.getBaseFile()); + if (t.getMessage().contains("Couldn't create directory")) { + // attempt to create the directory with Files.createDirectories, which + // throws more informative errors than File.mkdirs. + Path parentPath = destination.getBaseFile().getParentFile().toPath(); + try { + Files.createDirectories(parentPath); + Slog.i(LOG_TAG, "Successfully created " + parentPath); + } catch (Throwable t2) { + Slog.e(LOG_TAG, "Failed to write " + parentPath + + " with Files.writeDirectories", t2); + } + } + } destination.failWrite(out); } finally { IoUtils.closeQuietly(out); @@ -729,6 +748,33 @@ final class SettingsState { } } + private static void logSettingsDirectoryInformation(File settingsFile) { + File parent = settingsFile.getParentFile(); + Slog.i(LOG_TAG, "directory info for directory/file " + settingsFile + + " with stacktrace ", new Exception()); + File ancestorDir = parent; + while (ancestorDir != null) { + if (!ancestorDir.exists()) { + Slog.i(LOG_TAG, "ancestor directory " + ancestorDir + + " does not exist"); + ancestorDir = ancestorDir.getParentFile(); + } else { + Slog.i(LOG_TAG, "ancestor directory " + ancestorDir + + " exists"); + Slog.i(LOG_TAG, "ancestor directory " + ancestorDir + + " permissions: r: " + ancestorDir.canRead() + " w: " + + ancestorDir.canWrite() + " x: " + ancestorDir.canExecute()); + File ancestorParent = ancestorDir.getParentFile(); + if (ancestorParent != null) { + Slog.i(LOG_TAG, "ancestor's parent directory " + ancestorParent + + " permissions: r: " + ancestorParent.canRead() + " w: " + + ancestorParent.canWrite() + " x: " + ancestorParent.canExecute()); + } + break; + } + } + } + static void writeSingleSetting(int version, XmlSerializer serializer, String id, String name, String value, String defaultValue, String packageName, String tag, boolean defaultSysSet) throws IOException { @@ -803,6 +849,7 @@ final class SettingsState { in = new AtomicFile(mStatePersistFile).openRead(); } catch (FileNotFoundException fnfe) { Slog.i(LOG_TAG, "No settings state " + mStatePersistFile); + logSettingsDirectoryInformation(mStatePersistFile); addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null); return; } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 403e894a68e4..5a784e65db15 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -60,6 +60,9 @@ <uses-permission android:name="android.permission.GET_APP_OPS_STATS" /> <uses-permission android:name="android.permission.USE_RESERVED_DISK" /> + <!-- to invoke ContentSuggestionsService --> + <uses-permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS"/> + <!-- Networking and telephony --> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> @@ -72,7 +75,7 @@ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" /> <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> - <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> + <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" /> <uses-permission android:name="android.permission.NETWORK_SETTINGS" /> <uses-permission android:name="android.permission.TETHER_PRIVILEGED" /> <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /> @@ -366,6 +369,10 @@ <receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver" android:exported="false" /> + <!-- Callback for invoking a smart action from the screenshot notification. --> + <receiver android:name=".screenshot.GlobalScreenshot$SmartActionsReceiver" + android:exported="false"/> + <!-- started from UsbDeviceSettingsManager --> <activity android:name=".usb.UsbConfirmActivity" android:exported="true" diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 1bfc4c05c92c..c2788a8850d8 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -24,7 +24,35 @@ android:outlineProvider="none" android:elevation="5dp" > <!-- Put it above the status bar header --> - <include layout="@layout/keyguard_indication_area_overlay" /> + <LinearLayout + android:id="@+id/keyguard_indication_area" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom" + android:layout_gravity="bottom|center_horizontal" + android:orientation="vertical"> + + <com.android.systemui.statusbar.phone.KeyguardIndicationTextView + android:id="@+id/keyguard_indication_enterprise_disclosure" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:paddingStart="@dimen/keyguard_indication_text_padding" + android:paddingEnd="@dimen/keyguard_indication_text_padding" + android:textAppearance="@style/TextAppearance.Keyguard.BottomArea" + android:visibility="gone"/> + + <com.android.systemui.statusbar.phone.KeyguardIndicationTextView + android:id="@+id/keyguard_indication_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:paddingStart="@dimen/keyguard_indication_text_padding" + android:paddingEnd="@dimen/keyguard_indication_text_padding" + android:textAppearance="@style/TextAppearance.Keyguard.BottomArea" + android:accessibilityLiveRegion="polite"/> + + </LinearLayout> <FrameLayout android:id="@+id/preview_container" diff --git a/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml b/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml deleted file mode 100644 index cc30a682757c..000000000000 --- a/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/keyguard_indication_area" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom" - android:layout_gravity="bottom|center_horizontal" - android:orientation="vertical"> - - <include layout="@layout/keyguard_indication_text_view" /> - -</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml b/packages/SystemUI/res/layout/keyguard_indication_text_view.xml deleted file mode 100644 index 2b2100c850d8..000000000000 --- a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<merge xmlns:android="http://schemas.android.com/apk/res/android"> - - <com.android.systemui.statusbar.phone.KeyguardIndicationTextView - android:id="@+id/keyguard_indication_enterprise_disclosure" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:paddingStart="@dimen/keyguard_indication_text_padding" - android:paddingEnd="@dimen/keyguard_indication_text_padding" - android:textAppearance="@style/TextAppearance.Keyguard.BottomArea" - android:visibility="gone"/> - - <com.android.systemui.statusbar.phone.KeyguardIndicationTextView - android:id="@+id/keyguard_indication_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:paddingStart="@dimen/keyguard_indication_text_padding" - android:paddingEnd="@dimen/keyguard_indication_text_padding" - android:textAppearance="@style/TextAppearance.Keyguard.BottomArea" - android:accessibilityLiveRegion="polite"/> -</merge>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index d58f83fb8a3b..207fe26a28bd 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -892,8 +892,12 @@ <string name="quick_settings_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string> <!-- QuickSettings: Label for the toggle to activate dark theme (A.K.A Dark Mode). [CHAR LIMIT=20] --> <string name="quick_settings_ui_mode_night_label">Dark theme</string> - <!-- QuickSettings: Label for the dark theme tile when enabled by battery saver. [CHAR LIMIT=40] --> - <string name="quick_settings_ui_mode_night_label_battery_saver">Dark theme\nBattery saver</string> + <!-- QuickSettings: Secondary text for the dark theme tile when enabled by battery saver. [CHAR LIMIT=20] --> + <string name="quick_settings_dark_mode_secondary_label_battery_saver">Battery Saver</string> + <!-- QuickSettings: Secondary text for when the Dark Mode will be enabled at sunset. [CHAR LIMIT=20] --> + <string name="quick_settings_dark_mode_secondary_label_on_at_sunset">On at sunset</string> + <!-- QuickSettings: Secondary text for when the Dark Mode will be on until sunrise. [CHAR LIMIT=20] --> + <string name="quick_settings_dark_mode_secondary_label_until_sunrise">Until sunrise</string> <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] --> <string name="quick_settings_nfc_label">NFC</string> @@ -964,6 +968,12 @@ <!-- Message shown when face authentication fails and the pin pad is visible. [CHAR LIMIT=60] --> <string name="keyguard_retry">Swipe up to try again</string> + <!-- Indication when device is slow charging due to misalignment on the dock. [CHAR LIMIT=60] --> + <string name="dock_alignment_slow_charging" product="default">Realign phone for faster charging</string> + + <!-- Indication when device is not charging due to bad placement on the dock. [CHAR LIMIT=60] --> + <string name="dock_alignment_not_charging" product="default">Realign phone to charge wirelessly</string> + <!-- Text on keyguard screen and in Quick Settings footer indicating that the device is enterprise-managed by a Device Owner [CHAR LIMIT=60] --> <string name="do_disclosure_generic">This device is managed by your organization</string> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index ca7cd0d666ad..ace24a3b7ce1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -44,6 +44,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; @@ -615,6 +616,15 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS); mLockPatternUtils.reportSuccessfulPasswordAttempt(userId); + // Force a garbage collection in an attempt to erase any lockscreen password left in + // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard + // dismiss animation janky. + ThreadUtils.postOnBackgroundThread(() -> { + try { + Thread.sleep(5000); + } catch (InterruptedException ignored) { } + Runtime.getRuntime().gc(); + }); } else { StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE); diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index bd91333100bd..c0c14fb01222 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -20,6 +20,7 @@ import android.app.ActivityManager; import android.content.Context; import android.graphics.Rect; import android.os.HandlerThread; +import android.os.Trace; import android.service.wallpaper.WallpaperService; import android.util.Log; import android.util.Size; @@ -48,6 +49,7 @@ public class ImageWallpaper extends WallpaperService { private static final int DELAY_FINISH_RENDERING = 1000; private static final int INTERVAL_WAIT_FOR_RENDERING = 100; private static final int PATIENCE_WAIT_FOR_RENDERING = 10; + private static final boolean DEBUG = true; private HandlerThread mWorker; @Override @@ -125,6 +127,10 @@ public class ImageWallpaper extends WallpaperService { @Override public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) { if (!mNeedTransition) return; + if (DEBUG) { + Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode + + ", duration=" + animationDuration); + } mWorker.getThreadHandler().post( () -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration)); if (inAmbientMode && animationDuration == 0) { @@ -177,6 +183,10 @@ public class ImageWallpaper extends WallpaperService { @Override public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { mWorker.getThreadHandler().post(() -> { + if (DEBUG) { + Log.d(TAG, "onSurfaceChanged: w=" + width + ", h=" + height); + } + mRenderer.onSurfaceChanged(width, height); mNeedRedraw = true; }); @@ -185,16 +195,31 @@ public class ImageWallpaper extends WallpaperService { @Override public void onSurfaceRedrawNeeded(SurfaceHolder holder) { mWorker.getThreadHandler().post(() -> { + if (DEBUG) { + Log.d(TAG, "onSurfaceRedrawNeeded: mNeedRedraw=" + mNeedRedraw); + } + if (mNeedRedraw) { - preRender(); - requestRender(); - postRender(); + drawFrame(); mNeedRedraw = false; } }); } @Override + public void onVisibilityChanged(boolean visible) { + if (DEBUG) { + Log.d(TAG, "wallpaper visibility changes to: " + visible); + } + } + + private void drawFrame() { + preRender(); + requestRender(); + postRender(); + } + + @Override public void onStatePostChange() { // When back to home, we try to release EGL, which is preserved in lock screen or aod. if (mController.getState() == StatusBarState.SHADE) { @@ -204,8 +229,18 @@ public class ImageWallpaper extends WallpaperService { @Override public void preRender() { + if (DEBUG) { + Log.d(TAG, "preRender start"); + } + // This method should only be invoked from worker thread. + Trace.beginSection("ImageWallpaper#preRender"); preRenderInternal(); + Trace.endSection(); + + if (DEBUG) { + Log.d(TAG, "preRender end"); + } } private void preRenderInternal() { @@ -240,7 +275,9 @@ public class ImageWallpaper extends WallpaperService { @Override public void requestRender() { // This method should only be invoked from worker thread. + Trace.beginSection("ImageWallpaper#requestRender"); requestRenderInternal(); + Trace.endSection(); } private void requestRenderInternal() { @@ -262,9 +299,19 @@ public class ImageWallpaper extends WallpaperService { @Override public void postRender() { + if (DEBUG) { + Log.d(TAG, "postRender start"); + } + // This method should only be invoked from worker thread. + Trace.beginSection("ImageWallpaper#postRender"); notifyWaitingThread(); scheduleFinishRendering(); + Trace.endSection(); + + if (DEBUG) { + Log.d(TAG, "postRender end"); + } } private void notifyWaitingThread() { @@ -289,12 +336,18 @@ public class ImageWallpaper extends WallpaperService { } private void finishRendering() { + if (DEBUG) { + Log.d(TAG, "finishRendering, preserve=" + needPreserveEglContext()); + } + + Trace.beginSection("ImageWallpaper#finishRendering"); if (mEglHelper != null) { mEglHelper.destroyEglSurface(); if (!needPreserveEglContext()) { mEglHelper.destroyEglContext(); } } + Trace.endSection(); } private boolean needPreserveEglContext() { diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 48127a75b86e..9252e5767996 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -142,7 +142,7 @@ public class SystemUIApplication extends Application implements SysUiServiceProv public void startServicesIfNeeded() { String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents); - startServicesIfNeeded(names); + startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names); } /** @@ -154,10 +154,10 @@ public class SystemUIApplication extends Application implements SysUiServiceProv void startSecondaryUserServicesIfNeeded() { String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser); - startServicesIfNeeded(names); + startServicesIfNeeded(/* metricsPrefix= */ "StartSecondaryServices", names); } - private void startServicesIfNeeded(String[] services) { + private void startServicesIfNeeded(String metricsPrefix, String[] services) { if (mServicesStarted) { return; } @@ -176,14 +176,16 @@ public class SystemUIApplication extends Application implements SysUiServiceProv Log.v(TAG, "Starting SystemUI services for user " + Process.myUserHandle().getIdentifier() + "."); + TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming", Trace.TRACE_TAG_APP); - log.traceBegin("StartServices"); + log.traceBegin(metricsPrefix); + final int N = services.length; for (int i = 0; i < N; i++) { String clsName = services[i]; if (DEBUG) Log.d(TAG, "loading: " + clsName); - log.traceBegin("StartServices" + clsName); + log.traceBegin(metricsPrefix + clsName); long ti = System.currentTimeMillis(); Class cls; try { diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 0899d955a1ac..2531b6007c34 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -31,6 +31,7 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.ScrimView; @@ -49,6 +50,7 @@ import com.android.systemui.statusbar.phone.UnlockMethodCache; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.volume.VolumeDialogComponent; +import java.util.concurrent.Executor; import java.util.function.Consumer; import dagger.Module; @@ -110,6 +112,17 @@ public class SystemUIFactory { return new StatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils); } + /** + * Creates an instance of ScreenshotNotificationSmartActionsProvider. + * This method is overridden in vendor specific implementation of Sys UI. + */ + public ScreenshotNotificationSmartActionsProvider + createScreenshotNotificationSmartActionsProvider(Context context, + Executor executor, + Handler uiHandler) { + return new ScreenshotNotificationSmartActionsProvider(); + } + public KeyguardBouncer createKeyguardBouncer(Context context, ViewMediatorCallback callback, LockPatternUtils lockPatternUtils, ViewGroup container, DismissCallbackRegistry dismissCallbackRegistry, diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java index 1c5e80005d84..150a40abde7f 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java @@ -16,6 +16,7 @@ package com.android.systemui; +import android.annotation.NonNull; import android.app.Service; import android.content.Intent; import android.os.Build; @@ -66,7 +67,13 @@ public class SystemUIService extends Service { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (args != null && args.length > 0 && args[0].equals("--config")) { + dumpConfig(pw); + return; + } + dumpServices(((SystemUIApplication) getApplication()).getServices(), fd, pw, args); + dumpConfig(pw); } static void dumpServices( @@ -95,5 +102,29 @@ public class SystemUIService extends Service { } } } + + private void dumpConfig(@NonNull PrintWriter pw) { + pw.println("SystemUiServiceComponents configuration:"); + + pw.print("vendor component: "); + pw.println(getResources().getString(R.string.config_systemUIVendorServiceComponent)); + + dumpConfig(pw, "global", R.array.config_systemUIServiceComponents); + dumpConfig(pw, "per-user", R.array.config_systemUIServiceComponentsPerUser); + } + + private void dumpConfig(@NonNull PrintWriter pw, @NonNull String type, int resId) { + final String[] services = getResources().getStringArray(resId); + pw.print(type); pw.print(": "); + if (services == null) { + pw.println("N/A"); + return; + } + pw.print(services.length); + pw.println(" services"); + for (int i = 0; i < services.length; i++) { + pw.print(" "); pw.print(i); pw.print(": "); pw.println(services[i]); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java index 738ec80a40c4..8a8c6d1f1ec8 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java @@ -72,7 +72,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac private final Runnable mHideHandles = this::hideHandles; private final Runnable mShowAndGo = this::showAndGoInternal; private final Provider<ScreenDecorations> mScreenDecorations; - private final PhenotypeHelper mPhenotypeHelper; + private final DeviceConfigHelper mDeviceConfigHelper; private final Map<AssistHandleBehavior, BehaviorController> mBehaviorMap; private boolean mHandlesShowing = false; @@ -91,7 +91,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac AssistUtils assistUtils, @Named(ASSIST_HANDLE_THREAD_NAME) Handler handler, Provider<ScreenDecorations> screenDecorations, - PhenotypeHelper phenotypeHelper, + DeviceConfigHelper deviceConfigHelper, Map<AssistHandleBehavior, BehaviorController> behaviorMap, NavigationModeController navigationModeController, DumpController dumpController) { @@ -99,14 +99,14 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac mAssistUtils = assistUtils; mHandler = handler; mScreenDecorations = screenDecorations; - mPhenotypeHelper = phenotypeHelper; + mDeviceConfigHelper = deviceConfigHelper; mBehaviorMap = behaviorMap; mInGesturalMode = QuickStepContract.isGesturalMode( navigationModeController.addListener(this::handleNavigationModeChange)); setBehavior(getBehaviorMode()); - mPhenotypeHelper.addOnPropertiesChangedListener( + mDeviceConfigHelper.addOnPropertiesChangedListener( mHandler::post, (properties) -> { if (properties.getKeyset().contains( @@ -206,19 +206,19 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac } private long getShownFrequencyThreshold() { - return mPhenotypeHelper.getLong( + return mDeviceConfigHelper.getLong( SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS, DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS); } private long getShowAndGoDuration() { - return mPhenotypeHelper.getLong( + return mDeviceConfigHelper.getLong( SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS, DEFAULT_SHOW_AND_GO_DURATION_MS); } private String getBehaviorMode() { - return mPhenotypeHelper.getString( + return mDeviceConfigHelper.getString( SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE, DEFAULT_BEHAVIOR.toString()); } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java index 039404800fbb..b1fcd9d74980 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java @@ -156,7 +156,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { private final Clock mClock; private final Handler mHandler; - private final PhenotypeHelper mPhenotypeHelper; + private final DeviceConfigHelper mDeviceConfigHelper; private final Lazy<StatusBarStateController> mStatusBarStateController; private final Lazy<ActivityManagerWrapper> mActivityManagerWrapper; private final Lazy<OverviewProxyService> mOverviewProxyService; @@ -188,7 +188,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { AssistHandleReminderExpBehavior( @Named(UPTIME_NAME) Clock clock, @Named(ASSIST_HANDLE_THREAD_NAME) Handler handler, - PhenotypeHelper phenotypeHelper, + DeviceConfigHelper deviceConfigHelper, Lazy<StatusBarStateController> statusBarStateController, Lazy<ActivityManagerWrapper> activityManagerWrapper, Lazy<OverviewProxyService> overviewProxyService, @@ -196,7 +196,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { Lazy<PackageManagerWrapper> packageManagerWrapper) { mClock = clock; mHandler = handler; - mPhenotypeHelper = phenotypeHelper; + mDeviceConfigHelper = deviceConfigHelper; mStatusBarStateController = statusBarStateController; mActivityManagerWrapper = activityManagerWrapper; mOverviewProxyService = overviewProxyService; @@ -457,55 +457,55 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { } private long getLearningTimeMs() { - return mPhenotypeHelper.getLong( + return mDeviceConfigHelper.getLong( SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_TIME_MS, DEFAULT_LEARNING_TIME_MS); } private int getLearningCount() { - return mPhenotypeHelper.getInt( + return mDeviceConfigHelper.getInt( SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_COUNT, DEFAULT_LEARNING_COUNT); } private long getShowAndGoDelayedShortDelayMs() { - return mPhenotypeHelper.getLong( + return mDeviceConfigHelper.getLong( SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS, DEFAULT_SHOW_AND_GO_DELAYED_SHORT_DELAY_MS); } private long getShowAndGoDelayedLongDelayMs() { - return mPhenotypeHelper.getLong( + return mDeviceConfigHelper.getLong( SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAYED_LONG_DELAY_MS, DEFAULT_SHOW_AND_GO_DELAYED_LONG_DELAY_MS); } private long getShowAndGoDelayResetTimeoutMs() { - return mPhenotypeHelper.getLong( + return mDeviceConfigHelper.getLong( SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS, DEFAULT_SHOW_AND_GO_DELAY_RESET_TIMEOUT_MS); } private boolean getSuppressOnLockscreen() { - return mPhenotypeHelper.getBoolean( + return mDeviceConfigHelper.getBoolean( SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LOCKSCREEN, DEFAULT_SUPPRESS_ON_LOCKSCREEN); } private boolean getSuppressOnLauncher() { - return mPhenotypeHelper.getBoolean( + return mDeviceConfigHelper.getBoolean( SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_LAUNCHER, DEFAULT_SUPPRESS_ON_LAUNCHER); } private boolean getSuppressOnApps() { - return mPhenotypeHelper.getBoolean( + return mDeviceConfigHelper.getBoolean( SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_APPS, DEFAULT_SUPPRESS_ON_APPS); } private boolean getShowWhenTaught() { - return mPhenotypeHelper.getBoolean( + return mDeviceConfigHelper.getBoolean( SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_WHEN_TAUGHT, DEFAULT_SHOW_WHEN_TAUGHT); } diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java b/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java index ff76adfd5b6a..3005c97b46f0 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/PhenotypeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/assist/DeviceConfigHelper.java @@ -26,15 +26,15 @@ import javax.inject.Inject; import javax.inject.Singleton; /** - * Wrapper class for retrieving phenotype flag values. + * Wrapper class for retrieving System UI device configuration values. * * Can be mocked in tests for ease of testing the effects of particular values. */ @Singleton -public class PhenotypeHelper { +public class DeviceConfigHelper { @Inject - public PhenotypeHelper() {} + public DeviceConfigHelper() {} public long getLong(String name, long defaultValue) { return DeviceConfig.getLong(DeviceConfig.NAMESPACE_SYSTEMUI, name, defaultValue); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java index ce67577ea483..cbbd3a0b0562 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java @@ -499,6 +499,8 @@ public abstract class BiometricDialogView extends LinearLayout { if (newState == STATE_PENDING_CONFIRMATION || newState == STATE_AUTHENTICATED) { mNegativeButton.setText(R.string.cancel); mNegativeButton.setContentDescription(getResources().getString(R.string.cancel)); + } else { + mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT)); } updateIcon(mState, newState); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index eb826e54d340..d43e030ed9eb 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -184,6 +184,8 @@ public class BubbleData { Log.d(TAG, "notificationEntryUpdated: " + entry); } Bubble bubble = getBubbleWithKey(entry.key); + suppressFlyout = !entry.isVisuallyInterruptive || suppressFlyout; + if (bubble == null) { // Create a new bubble bubble = new Bubble(mContext, entry); @@ -193,8 +195,10 @@ public class BubbleData { } else { // Updates an existing bubble bubble.updateEntry(entry); + bubble.setSuppressFlyout(suppressFlyout); doUpdate(bubble); } + if (bubble.shouldAutoExpand()) { setSelectedBubbleInternal(bubble); if (!mExpanded) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index b68b7627fe8c..31cf853dce04 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -169,7 +169,7 @@ public class BubbleStackView extends FrameLayout { * Callback to run after the flyout hides. Also called if a new flyout is shown before the * previous one animates out. */ - private Runnable mAfterFlyoutHides; + private Runnable mFlyoutOnHide; /** Layout change listener that moves the stack to the nearest valid position on rotation. */ private OnLayoutChangeListener mOrientationChangedListener; @@ -1401,111 +1401,106 @@ public class BubbleStackView extends FrameLayout { @VisibleForTesting void animateInFlyoutForBubble(Bubble bubble) { final CharSequence updateMessage = bubble.getUpdateMessage(getContext()); - if (!bubble.showFlyoutForBubble()) { // In case flyout was suppressed for this update, reset now. bubble.setSuppressFlyout(false); return; } - if (updateMessage == null || isExpanded() || mIsExpansionAnimating || mIsGestureInProgress - || mBubbleToExpandAfterFlyoutCollapse != null) { + || mBubbleToExpandAfterFlyoutCollapse != null + || bubble.getIconView() == null) { // Skip the message if none exists, we're expanded or animating expansion, or we're - // about to expand a bubble from the previous tapped flyout. + // about to expand a bubble from the previous tapped flyout, or if bubble view is null. return; } - - if (bubble.getIconView() != null) { - // Temporarily suppress the dot while the flyout is visible. - bubble.getIconView().setSuppressDot( - true /* suppressDot */, false /* animate */); - - mFlyout.removeCallbacks(mAnimateInFlyout); - mFlyoutDragDeltaX = 0f; - - if (mAfterFlyoutHides != null) { - mAfterFlyoutHides.run(); + mFlyoutDragDeltaX = 0f; + clearFlyoutOnHide(); + mFlyoutOnHide = () -> { + resetDot(bubble); + if (mBubbleToExpandAfterFlyoutCollapse == null) { + return; } - - mAfterFlyoutHides = () -> { - final boolean suppressDot = !bubble.showBubbleDot(); - // If we're going to suppress the dot, make it visible first so it'll - // visibly animate away. - if (suppressDot) { - bubble.getIconView().setSuppressDot( - false /* suppressDot */, false /* animate */); - } - // Reset dot suppression. If we're not suppressing due to DND, then - // stop suppressing it with no animation (since the flyout has - // transformed into the dot). If we are suppressing due to DND, animate - // it away. - bubble.getIconView().setSuppressDot( - suppressDot /* suppressDot */, - suppressDot /* animate */); - - if (mBubbleToExpandAfterFlyoutCollapse != null) { - mBubbleData.setSelectedBubble(mBubbleToExpandAfterFlyoutCollapse); - mBubbleData.setExpanded(true); - mBubbleToExpandAfterFlyoutCollapse = null; - } - }; - - mFlyout.setVisibility(INVISIBLE); - - // Post in case layout isn't complete and getWidth returns 0. - post(() -> { - // An auto-expanding bubble could have been posted during the time it takes to - // layout. - if (isExpanded()) { - return; - } - - final Runnable afterShow = () -> { - mAnimateInFlyout = () -> { - mFlyout.setVisibility(VISIBLE); - bubble.getIconView().setSuppressDot( - true /* suppressDot */, false /* animate */); - mFlyoutDragDeltaX = - mStackAnimationController.isStackOnLeftSide() - ? -mFlyout.getWidth() - : mFlyout.getWidth(); - animateFlyoutCollapsed(false /* collapsed */, 0 /* velX */); - mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER); - }; - - mFlyout.postDelayed(mAnimateInFlyout, 200); + mBubbleData.setSelectedBubble(mBubbleToExpandAfterFlyoutCollapse); + mBubbleData.setExpanded(true); + mBubbleToExpandAfterFlyoutCollapse = null; + }; + mFlyout.setVisibility(INVISIBLE); + + // Temporarily suppress the dot while the flyout is visible. + bubble.getIconView().setSuppressDot( + true /* suppressDot */, false /* animate */); + + // Start flyout expansion. Post in case layout isn't complete and getWidth returns 0. + post(() -> { + // An auto-expanding bubble could have been posted during the time it takes to + // layout. + if (isExpanded()) { + return; + } + final Runnable expandFlyoutAfterDelay = () -> { + mAnimateInFlyout = () -> { + mFlyout.setVisibility(VISIBLE); + mFlyoutDragDeltaX = + mStackAnimationController.isStackOnLeftSide() + ? -mFlyout.getWidth() + : mFlyout.getWidth(); + animateFlyoutCollapsed(false /* collapsed */, 0 /* velX */); + mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER); }; - - mFlyout.setupFlyoutStartingAsDot( - updateMessage, mStackAnimationController.getStackPosition(), getWidth(), - mStackAnimationController.isStackOnLeftSide(), - bubble.getIconView().getBadgeColor(), - afterShow, - mAfterFlyoutHides, - bubble.getIconView().getDotCenter()); - mFlyout.bringToFront(); - }); - } - + mFlyout.postDelayed(mAnimateInFlyout, 200); + }; + mFlyout.setupFlyoutStartingAsDot( + updateMessage, mStackAnimationController.getStackPosition(), getWidth(), + mStackAnimationController.isStackOnLeftSide(), + bubble.getIconView().getBadgeColor() /* dotColor */, + expandFlyoutAfterDelay /* onLayoutComplete */, + mFlyoutOnHide, + bubble.getIconView().getDotCenter()); + mFlyout.bringToFront(); + }); mFlyout.removeCallbacks(mHideFlyout); mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER); logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT); } - /** Hide the flyout immediately and cancel any pending hide runnables. */ - private void hideFlyoutImmediate() { - if (mAfterFlyoutHides != null) { - mAfterFlyoutHides.run(); + private void resetDot(Bubble bubble) { + final boolean suppressDot = !bubble.showBubbleDot(); + // If we're going to suppress the dot, make it visible first so it'll + // visibly animate away. + + if (suppressDot) { + bubble.getIconView().setSuppressDot( + false /* suppressDot */, false /* animate */); } + // Reset dot suppression. If we're not suppressing due to DND, then + // stop suppressing it with no animation (since the flyout has + // transformed into the dot). If we are suppressing due to DND, animate + // it away. + bubble.getIconView().setSuppressDot( + suppressDot /* suppressDot */, + suppressDot /* animate */); + } + /** Hide the flyout immediately and cancel any pending hide runnables. */ + private void hideFlyoutImmediate() { + clearFlyoutOnHide(); mFlyout.removeCallbacks(mAnimateInFlyout); mFlyout.removeCallbacks(mHideFlyout); mFlyout.hideFlyout(); } + private void clearFlyoutOnHide() { + mFlyout.removeCallbacks(mAnimateInFlyout); + if (mFlyoutOnHide == null) { + return; + } + mFlyoutOnHide.run(); + mFlyoutOnHide = null; + } + @Override public void getBoundsOnScreen(Rect outRect) { if (!mIsExpanded) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java index 603c4169c169..4512aa822e3b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java @@ -61,7 +61,7 @@ public class BubbleView extends FrameLayout { // mBubbleIconFactory cannot be static because it depends on Context. private BubbleIconFactory mBubbleIconFactory; - private boolean mSuppressDot = false; + private boolean mSuppressDot; private Bubble mBubble; @@ -140,6 +140,7 @@ public class BubbleView extends FrameLayout { public void setAppIcon(Drawable appIcon) { mUserBadgedAppIcon = appIcon; } + /** * @return the {@link ExpandableNotificationRow} view to display notification content when the * bubble is expanded. @@ -154,7 +155,6 @@ public class BubbleView extends FrameLayout { updateDotVisibility(animate, null /* after */); } - /** * Sets whether or not to hide the dot even if we'd otherwise show it. This is used while the * flyout is visible or animating, to hide the dot until the flyout visually transforms into it. @@ -166,7 +166,7 @@ public class BubbleView extends FrameLayout { /** Sets the position of the 'new' dot, animating it out and back in if requested. */ void setDotPosition(boolean onLeft, boolean animate) { - if (animate && onLeft != mBadgedImageView.getDotOnLeft() && !mSuppressDot) { + if (animate && onLeft != mBadgedImageView.getDotOnLeft() && shouldShowDot()) { animateDot(false /* showDot */, () -> { mBadgedImageView.setDotOnLeft(onLeft); animateDot(true /* showDot */, null); @@ -190,12 +190,12 @@ public class BubbleView extends FrameLayout { * after animation if requested. */ private void updateDotVisibility(boolean animate, Runnable after) { - boolean showDot = mBubble.showBubbleDot() && !mSuppressDot; - + final boolean showDot = shouldShowDot(); if (animate) { animateDot(showDot, after); } else { mBadgedImageView.setShowDot(showDot); + mBadgedImageView.setDotScale(showDot ? 1f : 0f); } } @@ -203,27 +203,25 @@ public class BubbleView extends FrameLayout { * Animates the badge to show or hide. */ private void animateDot(boolean showDot, Runnable after) { - if (mBadgedImageView.isShowingDot() != showDot) { - if (showDot) { - mBadgedImageView.setShowDot(true); - } - mBadgedImageView.clearAnimation(); - mBadgedImageView.animate().setDuration(200) - .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) - .setUpdateListener((valueAnimator) -> { - float fraction = valueAnimator.getAnimatedFraction(); - fraction = showDot ? fraction : 1f - fraction; - mBadgedImageView.setDotScale(fraction); - }).withEndAction(() -> { - if (!showDot) { - mBadgedImageView.setShowDot(false); - } - - if (after != null) { - after.run(); - } - }).start(); + if (mBadgedImageView.isShowingDot() == showDot) { + return; } + // Do NOT wait until after animation ends to setShowDot + // to avoid overriding more recent showDot states. + mBadgedImageView.setShowDot(showDot); + mBadgedImageView.clearAnimation(); + mBadgedImageView.animate().setDuration(200) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .setUpdateListener((valueAnimator) -> { + float fraction = valueAnimator.getAnimatedFraction(); + fraction = showDot ? fraction : 1f - fraction; + mBadgedImageView.setDotScale(fraction); + }).withEndAction(() -> { + mBadgedImageView.setDotScale(showDot ? 1f : 0f); + if (after != null) { + after.run(); + } + }).start(); } void updateViews() { @@ -273,7 +271,11 @@ public class BubbleView extends FrameLayout { iconPath.transform(matrix); mBadgedImageView.drawDot(iconPath); - animateDot(mBubble.showBubbleDot() /* showDot */, null /* after */); + animateDot(shouldShowDot(), null /* after */); + } + + boolean shouldShowDot() { + return mBubble.showBubbleDot() && !mSuppressDot; } int getBadgeColor() { diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java index d3e8b3d3236e..3ca1f59fd793 100644 --- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java +++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java @@ -68,9 +68,11 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable, mBackdropColors.setMainColor(Color.BLACK); // Listen to all users instead of only the current one. - wallpaperManager.removeOnColorsChangedListener(this); - wallpaperManager.addOnColorsChangedListener(this, null /* handler */, - UserHandle.USER_ALL); + if (wallpaperManager.isWallpaperSupported()) { + wallpaperManager.removeOnColorsChangedListener(this); + wallpaperManager.addOnColorsChangedListener(this, null /* handler */, + UserHandle.USER_ALL); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/dock/DockManager.java b/packages/SystemUI/src/com/android/systemui/dock/DockManager.java index d332f59a4500..4ee2ed3e34c0 100644 --- a/packages/SystemUI/src/com/android/systemui/dock/DockManager.java +++ b/packages/SystemUI/src/com/android/systemui/dock/DockManager.java @@ -17,12 +17,12 @@ package com.android.systemui.dock; /** - * Allows an app to handle dock events + * Allows an app to handle dock events. */ public interface DockManager { /** - * Uninitialized / undocking dock states + * Uninitialized / undocking dock states. */ int STATE_NONE = 0; /** @@ -30,34 +30,81 @@ public interface DockManager { */ int STATE_DOCKED = 1; /** - * The state for docking without showing UI + * The state for docking without showing UI. */ int STATE_DOCKED_HIDE = 2; /** - * Add a dock event listener into manager + * Indicates there's no alignment info. This could happen when the device is unable to decide + * its alignment condition. + */ + int ALIGN_STATE_UNKNOWN = -1; + + /** + * Indicates there's no alignment issue. + */ + int ALIGN_STATE_GOOD = 0; + + /** + * Indicates it's slightly not aligned with dock. Normally combines with slow charging issue. + */ + int ALIGN_STATE_POOR = 1; + + /** + * Indicates it's not aligned with dock. Normally combines with not charging issue. + */ + int ALIGN_STATE_TERRIBLE = 2; + + /** + * Adds a dock event listener into manager. * * @param callback A {@link DockEventListener} which want to add */ void addListener(DockEventListener callback); /** - * Remove the added listener from dock manager + * Removes the added listener from dock manager * * @param callback A {@link DockEventListener} which want to remove */ void removeListener(DockEventListener callback); /** + * Adds a alignment listener into manager. + * + * @param listener A {@link AlignmentStateListener} which want to add + */ + void addAlignmentStateListener(AlignmentStateListener listener); + + /** + * Removes the added alignment listener from dock manager. + * + * @param listener A {@link AlignmentStateListener} which want to remove + */ + void removeAlignmentStateListener(AlignmentStateListener listener); + + /** * Returns true if the device is in docking state. */ boolean isDocked(); - /** Callback for receiving dock events */ + /** + * Listens to dock events. + */ interface DockEventListener { /** - * Override to handle dock events + * Override to handle dock events. */ void onEvent(int event); } + + /** + * Listens to dock alignment state changed. + */ + interface AlignmentStateListener { + /** + * Override to handle alignment state changes. + */ + void onAlignmentStateChanged(int alignState); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java b/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java index fa7f5032ca16..f6d24e8a35c7 100644 --- a/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/dock/DockManagerImpl.java @@ -35,6 +35,14 @@ public class DockManagerImpl implements DockManager { } @Override + public void addAlignmentStateListener(AlignmentStateListener listener) { + } + + @Override + public void removeAlignmentStateListener(AlignmentStateListener listener) { + } + + @Override public boolean isDocked() { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 90cb05a8e6ee..0e124e48f165 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -33,6 +33,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.AsyncSensorManager; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.util.wakelock.WakeLock; @@ -64,7 +65,7 @@ public class DozeFactory { params); DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock, - wakefulnessLifecycle); + wakefulnessLifecycle, Dependency.get(BatteryController.class)); machine.setParts(new DozeMachine.Part[]{ new DozePauser(handler, machine, alarmManager, params.getPolicy()), new DozeFalsingManagerAdapter(falsingManager), diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index 93a51cc20db2..7f2d52780a23 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -27,6 +27,7 @@ import com.android.internal.util.Preconditions; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.Assert; import com.android.systemui.util.wakelock.WakeLock; @@ -121,6 +122,7 @@ public class DozeMachine { private final WakeLock mWakeLock; private final AmbientDisplayConfiguration mConfig; private final WakefulnessLifecycle mWakefulnessLifecycle; + private final BatteryController mBatteryController; private Part[] mParts; private final ArrayList<State> mQueuedRequests = new ArrayList<>(); @@ -129,11 +131,13 @@ public class DozeMachine { private boolean mWakeLockHeldForCurrentState = false; public DozeMachine(Service service, AmbientDisplayConfiguration config, - WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle) { + WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle, + BatteryController batteryController) { mDozeService = service; mConfig = config; mWakefulnessLifecycle = wakefulnessLifecycle; mWakeLock = wakeLock; + mBatteryController = batteryController; } /** Initializes the set of {@link Part}s. Must be called exactly once after construction. */ @@ -316,6 +320,9 @@ public class DozeMachine { Log.i(TAG, "Dropping pulse done because current state is already done: " + mState); return mState; } + if (requestedState == State.DOZE_AOD && mBatteryController.isAodPowerSave()) { + return State.DOZE; + } if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) { Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState); return mState; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index bab64db4519c..6199a0deb31f 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -345,7 +345,6 @@ public class DozeTriggers implements DozeMachine.Part { private void checkTriggersAtInit() { if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR - || mDozeHost.isPowerSaveActive() || mDozeHost.isBlockingDoze() || !mDozeHost.isProvisioned()) { mMachine.requestState(DozeMachine.State.FINISH); @@ -574,8 +573,8 @@ public class DozeTriggers implements DozeMachine.Part { @Override public void onPowerSaveChanged(boolean active) { - if (active) { - mMachine.requestState(DozeMachine.State.FINISH); + if (mDozeHost.isPowerSaveActive()) { + mMachine.requestState(DozeMachine.State.DOZE); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 1f33af8c3f55..3869e77294e7 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -83,7 +83,8 @@ public class DozeUi implements DozeMachine.Part { */ private void updateAnimateScreenOff() { if (mCanAnimateTransition) { - final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing; + final boolean controlScreenOff = mDozeParameters.getAlwaysOn() && mKeyguardShowing + && !mHost.isPowerSaveActive(); mDozeParameters.setControlScreenOffAnimation(controlScreenOff); mHost.setAnimateScreenOff(controlScreenOff); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java index 35c8b741381c..b409129048a6 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java @@ -16,6 +16,7 @@ package com.android.systemui.doze; +import android.annotation.Nullable; import android.app.IWallpaperManager; import android.content.Context; import android.os.RemoteException; @@ -37,6 +38,7 @@ public class DozeWallpaperState implements DozeMachine.Part { private static final String TAG = "DozeWallpaperState"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + @Nullable private final IWallpaperManager mWallpaperManagerService; private final DozeParameters mDozeParameters; private final BiometricUnlockController mBiometricUnlockController; @@ -88,16 +90,18 @@ public class DozeWallpaperState implements DozeMachine.Part { if (isAmbientMode != mIsAmbientMode) { mIsAmbientMode = isAmbientMode; - try { - long duration = animated ? StackStateAnimator.ANIMATION_DURATION_WAKEUP : 0L; - if (DEBUG) { - Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode + if (mWallpaperManagerService != null) { + try { + long duration = animated ? StackStateAnimator.ANIMATION_DURATION_WAKEUP : 0L; + if (DEBUG) { + Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode + ", animationDuration: " + duration); + } + mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, duration); + } catch (RemoteException e) { + // Cannot notify wallpaper manager service, but it's fine, let's just skip it. + Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode); } - mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, duration); - } catch (RemoteException e) { - // Cannot notify wallpaper manager service, but it's fine, let's just skip it. - Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode); } } } @@ -106,5 +110,6 @@ public class DozeWallpaperState implements DozeMachine.Part { public void dump(PrintWriter pw) { pw.println("DozeWallpaperState:"); pw.println(" isAmbientMode: " + mIsAmbientMode); + pw.println(" hasWallpaperService: " + (mWallpaperManagerService != null)); } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 589b24a04f8c..41a3a95bd724 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -86,6 +86,7 @@ import com.android.internal.util.ScreenRecordHelper; import com.android.internal.util.ScreenshotHelper; import com.android.internal.view.RotationPolicy; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.MultiListLayout; @@ -213,11 +214,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, Dependency.get(ConfigurationController.class).addCallback(this); mActivityStarter = Dependency.get(ActivityStarter.class); + KeyguardUpdateMonitor keyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context); UnlockMethodCache unlockMethodCache = UnlockMethodCache.getInstance(context); unlockMethodCache.addListener( () -> { if (mDialog != null && mDialog.mPanelController != null) { - boolean locked = !unlockMethodCache.canSkipBouncer(); + boolean locked = !unlockMethodCache.canSkipBouncer() + && keyguardUpdateMonitor.isKeyguardVisible(); mDialog.mPanelController.onDeviceLockStateChanged(locked); } }); @@ -414,9 +417,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, new GlobalActionsPanelPlugin.Callbacks() { @Override public void dismissGlobalActionsMenu() { - if (mDialog != null) { - mDialog.dismiss(); - } + dismissDialog(); } @Override @@ -912,6 +913,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, /** {@inheritDoc} */ public void onDismiss(DialogInterface dialog) { + if (mDialog == dialog) { + mDialog = null; + } mWindowManagerFuncs.onGlobalActionsHidden(); if (mShowSilentToggle) { try { diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java index b154e66a846e..6474b390f5f8 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java @@ -19,6 +19,7 @@ package com.android.systemui.glwallpaper; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.util.Log; import com.android.systemui.Interpolators; @@ -30,6 +31,7 @@ class ImageRevealHelper { private static final String TAG = ImageRevealHelper.class.getSimpleName(); private static final float MAX_REVEAL = 0f; private static final float MIN_REVEAL = 1f; + private static final boolean DEBUG = true; private final ValueAnimator mAnimator; private final RevealStateListener mRevealListener; @@ -56,8 +58,13 @@ class ImageRevealHelper { @Override public void onAnimationEnd(Animator animation) { - if (!mIsCanceled && mRevealListener != null) { - mRevealListener.onRevealEnd(); + if (mRevealListener != null) { + if (DEBUG) { + Log.d(TAG, "transition end, cancel=" + mIsCanceled + ", reveal=" + mReveal); + } + if (!mIsCanceled) { + mRevealListener.onRevealEnd(); + } } mIsCanceled = false; } @@ -65,25 +72,25 @@ class ImageRevealHelper { @Override public void onAnimationStart(Animator animation) { if (mRevealListener != null) { + if (DEBUG) { + Log.d(TAG, "transition start"); + } mRevealListener.onRevealStart(true /* animate */); } } }); } - private void animate() { - mAnimator.cancel(); - mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL); - mAnimator.start(); - } - public float getReveal() { return mReveal; } void updateAwake(boolean awake, long duration) { + if (DEBUG) { + Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration); + } + mAnimator.cancel(); mAwake = awake; - mAnimator.setDuration(duration); if (duration == 0) { // We are transiting from home to aod or aod to home directly, // we don't need to do transition in these cases. @@ -92,7 +99,9 @@ class ImageRevealHelper { mRevealListener.onRevealStateChanged(); mRevealListener.onRevealEnd(); } else { - animate(); + mAnimator.setDuration(duration); + mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL); + mAnimator.start(); } } diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java index 29606347f009..be6f7bfe2587 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -46,6 +46,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, private static final String TAG = ImageWallpaperRenderer.class.getSimpleName(); private static final float SCALE_VIEWPORT_MIN = 1f; private static final float SCALE_VIEWPORT_MAX = 1.1f; + private static final boolean DEBUG = true; private final WallpaperManager mWallpaperManager; private final ImageGLProgram mProgram; @@ -107,6 +108,9 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, } private boolean loadBitmap() { + if (DEBUG) { + Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap); + } if (mWallpaperManager != null && mBitmap == null) { mBitmap = mWallpaperManager.getBitmap(); mWallpaperManager.forgetLoadedWallpaper(); @@ -119,6 +123,9 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, mSurfaceSize.set(0, 0, surfaceWidth, surfaceHeight); } } + if (DEBUG) { + Log.d(TAG, "loadBitmap done, surface size=" + mSurfaceSize); + } return mBitmap != null; } @@ -193,16 +200,28 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, @Override public void onRevealStart(boolean animate) { + if (DEBUG) { + Log.v(TAG, "onRevealStart: start, anim=" + animate); + } + if (animate) { mScissorMode = true; // Use current display area of texture. mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset); } mProxy.preRender(); + + if (DEBUG) { + Log.v(TAG, "onRevealStart: done"); + } } @Override public void onRevealEnd() { + if (DEBUG) { + Log.v(TAG, "onRevealEnd: start, mScissorMode=" + mScissorMode); + } + if (mScissorMode) { mScissorMode = false; // reset texture coordinates to use full texture. @@ -211,6 +230,10 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, mProxy.requestRender(); } mProxy.postRender(); + + if (DEBUG) { + Log.v(TAG, "onRevealEnd: done"); + } } @Override @@ -223,6 +246,7 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, out.print(prefix); out.print("mXOffset="); out.print(mXOffset); out.print(prefix); out.print("mYOffset="); out.print(mYOffset); out.print(prefix); out.print("threshold="); out.print(mImageProcessHelper.getThreshold()); + out.print(prefix); out.print("mReveal="); out.print(mImageRevealHelper.getReveal()); mWallpaper.dump(prefix, fd, out, args); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 48f32cf04fb2..e408745699ed 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -103,8 +103,8 @@ public class KeyguardSliceProvider extends SliceProvider implements protected final Uri mMediaUri; private final Date mCurrentTime = new Date(); private final Handler mHandler; + private final Handler mMediaHandler; private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm; - private final Object mMediaToken = new Object(); private DozeParameters mDozeParameters; @VisibleForTesting protected SettableWakeLock mMediaWakeLock; @@ -169,17 +169,13 @@ public class KeyguardSliceProvider extends SliceProvider implements } }; - public KeyguardSliceProvider() { - this(new Handler()); - } - public static KeyguardSliceProvider getAttachedInstance() { return KeyguardSliceProvider.sInstance; } - @VisibleForTesting - KeyguardSliceProvider(Handler handler) { - mHandler = handler; + public KeyguardSliceProvider() { + mHandler = new Handler(); + mMediaHandler = new Handler(); mSliceUri = Uri.parse(KEYGUARD_SLICE_URI); mHeaderUri = Uri.parse(KEYGUARD_HEADER_URI); mDateUri = Uri.parse(KEYGUARD_DATE_URI); @@ -462,16 +458,18 @@ public class KeyguardSliceProvider extends SliceProvider implements public void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state) { synchronized (this) { boolean nextVisible = NotificationMediaManager.isPlayingState(state); - mHandler.removeCallbacksAndMessages(mMediaToken); + mMediaHandler.removeCallbacksAndMessages(null); if (mMediaIsVisible && !nextVisible && mStatusBarState != StatusBarState.SHADE) { // We need to delay this event for a few millis when stopping to avoid jank in the // animation. The media app might not send its update when buffering, and the slice // would end up without a header for 0.5 second. mMediaWakeLock.setAcquired(true); - mHandler.postDelayed(() -> { - updateMediaStateLocked(metadata, state); - mMediaWakeLock.setAcquired(false); - }, mMediaToken, 2000); + mMediaHandler.postDelayed(() -> { + synchronized (this) { + updateMediaStateLocked(metadata, state); + mMediaWakeLock.setAcquired(false); + } + }, 2000); } else { mMediaWakeLock.setAcquired(false); updateMediaStateLocked(metadata, state); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java index dd0ea5ef17f4..107958f49b91 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java @@ -21,7 +21,7 @@ import android.content.Intent; import android.content.res.Configuration; import android.provider.Settings; import android.service.quicksettings.Tile; -import android.widget.Switch; +import android.text.TextUtils; import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.R; @@ -79,24 +79,34 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements return; } boolean newState = !mState.value; - mUiModeManager.setNightMode(newState ? UiModeManager.MODE_NIGHT_YES - : UiModeManager.MODE_NIGHT_NO); + mUiModeManager.setNightModeActivated(newState); refreshState(newState); } @Override protected void handleUpdateState(BooleanState state, Object arg) { + int uiMode = mUiModeManager.getNightMode(); boolean powerSave = mBatteryController.isPowerSave(); + boolean isAuto = uiMode == UiModeManager.MODE_NIGHT_AUTO; boolean nightMode = (mContext.getResources().getConfiguration().uiMode - & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; + & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; + if (powerSave) { + state.secondaryLabel = mContext.getResources().getString( + R.string.quick_settings_dark_mode_secondary_label_battery_saver); + } else if (isAuto) { + state.secondaryLabel = mContext.getResources().getString(nightMode + ? R.string.quick_settings_dark_mode_secondary_label_until_sunrise + : R.string.quick_settings_dark_mode_secondary_label_on_at_sunset); + } else { + state.secondaryLabel = null; + } state.value = nightMode; - state.label = mContext.getString(powerSave - ? R.string.quick_settings_ui_mode_night_label_battery_saver - : R.string.quick_settings_ui_mode_night_label); - state.contentDescription = state.label; + state.label = mContext.getString(R.string.quick_settings_ui_mode_night_label); state.icon = mIcon; - state.expandedAccessibilityClassName = Switch.class.getName(); + state.contentDescription = TextUtils.isEmpty(state.secondaryLabel) + ? state.label + : TextUtils.concat(state.label, ", ", state.secondaryLabel); if (powerSave) { state.state = Tile.STATE_UNAVAILABLE; } else { @@ -112,7 +122,7 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements @Override public Intent getLongClickIntent() { - return new Intent(Settings.ACTION_DISPLAY_SETTINGS); + return new Intent(Settings.ACTION_DARK_THEME_SETTINGS); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 11ca94f6f4e6..9b30a77003cd 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -17,6 +17,7 @@ package com.android.systemui.screenshot; import static android.content.Context.NOTIFICATION_SERVICE; +import static android.os.AsyncTask.THREAD_POOL_EXECUTOR; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT; @@ -29,7 +30,9 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.app.ActivityManager; import android.app.ActivityOptions; +import android.app.ActivityTaskManager; import android.app.Notification; import android.app.Notification.BigPictureStyle; import android.app.NotificationManager; @@ -42,6 +45,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; @@ -58,10 +62,16 @@ import android.graphics.Rect; import android.media.MediaActionSound; import android.net.Uri; import android.os.AsyncTask; +import android.os.Bundle; import android.os.Environment; +import android.os.Handler; import android.os.PowerManager; import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; +import android.provider.DeviceConfig; import android.provider.MediaStore; import android.text.TextUtils; import android.util.DisplayMetrics; @@ -77,10 +87,13 @@ import android.view.animation.Interpolator; import android.widget.ImageView; import android.widget.Toast; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.SystemUI; +import com.android.systemui.SystemUIFactory; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.NotificationChannels; @@ -91,11 +104,17 @@ import java.io.IOException; import java.io.OutputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.Date; +import java.util.List; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; - +import java.util.function.Consumer; /** @@ -105,7 +124,7 @@ class SaveImageInBackgroundData { Context context; Bitmap image; Uri imageUri; - Runnable finisher; + Consumer<Uri> finisher; int iconSize; int previewWidth; int previewheight; @@ -128,6 +147,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { private static final String TAG = "SaveImageInBackgroundTask"; private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png"; + private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s"; private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)"; private final SaveImageInBackgroundData mParams; @@ -138,6 +158,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { private final BigPictureStyle mNotificationStyle; private final int mImageWidth; private final int mImageHeight; + private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider; + private final String mScreenshotId; + private final boolean mSmartActionsEnabled; + private final Random mRandom = new Random(); SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data, NotificationManager nManager) { @@ -148,6 +172,20 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mImageTime = System.currentTimeMillis(); String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime)); mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate); + mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, UUID.randomUUID()); + + // Initialize screenshot notification smart actions provider. + mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, false); + if (mSmartActionsEnabled) { + mSmartActionsProvider = + SystemUIFactory.getInstance() + .createScreenshotNotificationSmartActionsProvider( + context, THREAD_POOL_EXECUTOR, new Handler()); + } else { + // If smart actions is not enabled use empty implementation. + mSmartActionsProvider = new ScreenshotNotificationSmartActionsProvider(); + } // Create the large notification icon mImageWidth = data.image.getWidth(); @@ -222,6 +260,55 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mNotificationStyle.bigLargeIcon((Bitmap) null); } + private List<Notification.Action> buildSmartActions( + List<Notification.Action> actions, Context context) { + List<Notification.Action> broadcastActions = new ArrayList<>(); + for (Notification.Action action : actions) { + // Proxy smart actions through {@link GlobalScreenshot.SmartActionsReceiver} + // for logging smart actions. + Bundle extras = action.getExtras(); + String actionType = extras.getString( + ScreenshotNotificationSmartActionsProvider.ACTION_TYPE, + ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE); + Intent intent = new Intent(context, + GlobalScreenshot.SmartActionsReceiver.class).putExtra( + GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent); + addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled); + PendingIntent broadcastIntent = PendingIntent.getBroadcast(context, + mRandom.nextInt(), + intent, + PendingIntent.FLAG_CANCEL_CURRENT); + broadcastActions.add(new Notification.Action.Builder(action.getIcon(), action.title, + broadcastIntent).setContextual(true).addExtras(extras).build()); + } + return broadcastActions; + } + + private static void addIntentExtras(String screenshotId, Intent intent, String actionType, + boolean smartActionsEnabled) { + intent + .putExtra(GlobalScreenshot.EXTRA_ACTION_TYPE, actionType) + .putExtra(GlobalScreenshot.EXTRA_ID, screenshotId) + .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled); + } + + private int getUserHandleOfForegroundApplication(Context context) { + // This logic matches + // com.android.systemui.statusbar.phone.PhoneStatusBarPolicy#updateManagedProfile + try { + return ActivityTaskManager.getService().getLastResumedActivityUserId(); + } catch (RemoteException e) { + Slog.w(TAG, "getUserHandleOfForegroundApplication: ", e); + return context.getUserId(); + } + } + + private boolean isManagedProfile(Context context) { + UserManager manager = UserManager.get(context); + UserInfo info = manager.getUserInfo(getUserHandleOfForegroundApplication(context)); + return info.isManagedProfile(); + } + /** * Generates a new hardware bitmap with specified values, copying the content from the passed * in bitmap. @@ -251,6 +338,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { Resources r = context.getResources(); try { + CompletableFuture<List<Notification.Action>> smartActionsFuture = + GlobalScreenshot.getSmartActionsFuture(mScreenshotId, image, + mSmartActionsProvider, mSmartActionsEnabled, isManagedProfile(context)); + // Save the screenshot to the MediaStore final MediaStore.PendingParams params = new MediaStore.PendingParams( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mImageFileName, "image/png"); @@ -273,77 +364,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { IoUtils.closeQuietly(session); } - // Note: Both the share and edit actions are proxied through ActionProxyReceiver in - // order to do some common work like dismissing the keyguard and sending - // closeSystemWindows - - // Create a share intent, this will always go through the chooser activity first which - // should not trigger auto-enter PiP - String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime)); - String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate); - Intent sharingIntent = new Intent(Intent.ACTION_SEND); - sharingIntent.setType("image/png"); - sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); - // Include URI in ClipData also, so that grantPermission picks it up. - // We don't use setData here because some apps interpret this as "to:". - ClipData clipdata = new ClipData(new ClipDescription("content", - new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}), - new ClipData.Item(uri)); - sharingIntent.setClipData(clipdata); - sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject); - sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - - PendingIntent chooserAction = PendingIntent.getBroadcast(context, 0, - new Intent(context, GlobalScreenshot.TargetChosenReceiver.class), - PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); - Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null, - chooserAction.getIntentSender()) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - - // Create a share action for the notification - PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, 0, - new Intent(context, GlobalScreenshot.ActionProxyReceiver.class) - .putExtra(EXTRA_ACTION_INTENT, sharingChooserIntent) - .putExtra(EXTRA_DISALLOW_ENTER_PIP, true), - PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM); - Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder( - R.drawable.ic_screenshot_share, - r.getString(com.android.internal.R.string.share), shareAction); - mNotificationBuilder.addAction(shareActionBuilder.build()); - - // Create an edit intent, if a specific package is provided as the editor, then launch - // that directly - String editorPackage = context.getString(R.string.config_screenshotEditor); - Intent editIntent = new Intent(Intent.ACTION_EDIT); - if (!TextUtils.isEmpty(editorPackage)) { - editIntent.setComponent(ComponentName.unflattenFromString(editorPackage)); - } - editIntent.setType("image/png"); - editIntent.setData(uri); - editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - - // Create a edit action - PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, 1, - new Intent(context, GlobalScreenshot.ActionProxyReceiver.class) - .putExtra(EXTRA_ACTION_INTENT, editIntent) - .putExtra(EXTRA_CANCEL_NOTIFICATION, editIntent.getComponent() != null), - PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM); - Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( - R.drawable.ic_screenshot_edit, - r.getString(com.android.internal.R.string.screenshot_edit), editAction); - mNotificationBuilder.addAction(editActionBuilder.build()); - - // Create a delete action for the notification - PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0, - new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class) - .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()), - PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); - Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder( - R.drawable.ic_screenshot_delete, - r.getString(com.android.internal.R.string.delete), deleteAction); - mNotificationBuilder.addAction(deleteActionBuilder.build()); + populateNotificationActions(context, r, uri, smartActionsFuture, mNotificationBuilder); mParams.imageUri = uri; mParams.image = null; @@ -364,6 +385,105 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { return null; } + @VisibleForTesting + void populateNotificationActions(Context context, Resources r, Uri uri, + CompletableFuture<List<Notification.Action>> smartActionsFuture, + Notification.Builder notificationBuilder) { + // Note: Both the share and edit actions are proxied through ActionProxyReceiver in + // order to do some common work like dismissing the keyguard and sending + // closeSystemWindows + + // Create a share intent, this will always go through the chooser activity first which + // should not trigger auto-enter PiP + String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime)); + String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate); + Intent sharingIntent = new Intent(Intent.ACTION_SEND); + sharingIntent.setType("image/png"); + sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); + // Include URI in ClipData also, so that grantPermission picks it up. + // We don't use setData here because some apps interpret this as "to:". + ClipData clipdata = new ClipData(new ClipDescription("content", + new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}), + new ClipData.Item(uri)); + sharingIntent.setClipData(clipdata); + sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject); + sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + PendingIntent chooserAction = PendingIntent.getBroadcast(context, 0, + new Intent(context, GlobalScreenshot.TargetChosenReceiver.class), + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); + Intent sharingChooserIntent = Intent.createChooser(sharingIntent, null, + chooserAction.getIntentSender()) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + // Create a share action for the notification + PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, 0, + new Intent(context, GlobalScreenshot.ActionProxyReceiver.class) + .putExtra(EXTRA_ACTION_INTENT, sharingChooserIntent) + .putExtra(EXTRA_DISALLOW_ENTER_PIP, true) + .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId) + .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, + mSmartActionsEnabled), + PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM); + Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder( + R.drawable.ic_screenshot_share, + r.getString(com.android.internal.R.string.share), shareAction); + notificationBuilder.addAction(shareActionBuilder.build()); + + // Create an edit intent, if a specific package is provided as the editor, then launch + // that directly + String editorPackage = context.getString(R.string.config_screenshotEditor); + Intent editIntent = new Intent(Intent.ACTION_EDIT); + if (!TextUtils.isEmpty(editorPackage)) { + editIntent.setComponent(ComponentName.unflattenFromString(editorPackage)); + } + editIntent.setType("image/png"); + editIntent.setData(uri); + editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + + // Create a edit action + PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, 1, + new Intent(context, GlobalScreenshot.ActionProxyReceiver.class) + .putExtra(EXTRA_ACTION_INTENT, editIntent) + .putExtra(EXTRA_CANCEL_NOTIFICATION, editIntent.getComponent() != null) + .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId) + .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, + mSmartActionsEnabled), + PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM); + Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( + R.drawable.ic_screenshot_edit, + r.getString(com.android.internal.R.string.screenshot_edit), editAction); + notificationBuilder.addAction(editActionBuilder.build()); + + // Create a delete action for the notification + PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0, + new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class) + .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()) + .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId) + .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, + mSmartActionsEnabled), + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); + Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder( + R.drawable.ic_screenshot_delete, + r.getString(com.android.internal.R.string.delete), deleteAction); + notificationBuilder.addAction(deleteActionBuilder.build()); + + if (mSmartActionsEnabled) { + int timeoutMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags + .SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS, + 1000); + List<Notification.Action> smartActions = GlobalScreenshot.getSmartActions(mScreenshotId, + smartActionsFuture, timeoutMs, mSmartActionsProvider); + smartActions = buildSmartActions(smartActions, context); + for (Notification.Action action : smartActions) { + notificationBuilder.addAction(action); + } + } + } + @Override protected void onPostExecute(Void params) { if (mParams.errorMsgResId != 0) { @@ -406,7 +526,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT, mNotificationBuilder.build()); } - mParams.finisher.run(); + mParams.finisher.accept(mParams.imageUri); mParams.clearContext(); } @@ -415,7 +535,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { // If we are cancelled while the task is running in the background, we may get null params. // The finisher is expected to always be called back, so just use the baked-in params from // the ctor in any case. - mParams.finisher.run(); + mParams.finisher.accept(null); mParams.clearImage(); mParams.clearContext(); @@ -446,6 +566,15 @@ class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> { } class GlobalScreenshot { + // These strings are used for communicating the action invoked to + // ScreenshotNotificationSmartActionsProvider. + static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type"; + static final String EXTRA_ID = "android:screenshot_id"; + static final String ACTION_TYPE_DELETE = "Delete"; + static final String ACTION_TYPE_SHARE = "Share"; + static final String ACTION_TYPE_EDIT = "Edit"; + static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled"; + static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id"; static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent"; static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification"; @@ -566,7 +695,7 @@ class GlobalScreenshot { /** * Creates a new worker thread and saves the screenshot to the media store. */ - private void saveScreenshotInWorkerThread(Runnable finisher) { + private void saveScreenshotInWorkerThread(Consumer<Uri> finisher) { SaveImageInBackgroundData data = new SaveImageInBackgroundData(); data.context = mContext; data.image = mScreenBitmap; @@ -584,8 +713,8 @@ class GlobalScreenshot { /** * Takes a screenshot of the current display and shows an animation. */ - private void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible, - Rect crop) { + private void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, + boolean navBarVisible, Rect crop) { int rot = mDisplay.getRotation(); int width = crop.width(); int height = crop.height(); @@ -595,7 +724,7 @@ class GlobalScreenshot { if (mScreenBitmap == null) { notifyScreenshotError(mContext, mNotificationManager, R.string.screenshot_failed_to_capture_text); - finisher.run(); + finisher.accept(null); return; } @@ -608,7 +737,7 @@ class GlobalScreenshot { statusBarVisible, navBarVisible); } - void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) { + void takeScreenshot(Consumer<Uri> finisher, boolean statusBarVisible, boolean navBarVisible) { mDisplay.getRealMetrics(mDisplayMetrics); takeScreenshot(finisher, statusBarVisible, navBarVisible, new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); @@ -617,7 +746,7 @@ class GlobalScreenshot { /** * Displays a screenshot selector */ - void takeScreenshotPartial(final Runnable finisher, final boolean statusBarVisible, + void takeScreenshotPartial(final Consumer<Uri> finisher, final boolean statusBarVisible, final boolean navBarVisible) { mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() { @@ -677,8 +806,8 @@ class GlobalScreenshot { /** * Starts the animation after taking the screenshot */ - private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible, - boolean navBarVisible) { + private void startAnimation(final Consumer<Uri> finisher, int w, int h, + boolean statusBarVisible, boolean navBarVisible) { // If power save is on, show a toast so there is some visual indication that a screenshot // has been taken. PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -904,6 +1033,100 @@ class GlobalScreenshot { nManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT, n); } + @VisibleForTesting + static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(String screenshotId, + Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider, + boolean smartActionsEnabled, boolean isManagedProfile) { + if (!smartActionsEnabled) { + Slog.i(TAG, "Screenshot Intelligence not enabled, returning empty list."); + return CompletableFuture.completedFuture(Collections.emptyList()); + } + if (image.getConfig() != Bitmap.Config.HARDWARE) { + Slog.w(TAG, String.format( + "Bitmap expected: Hardware, Bitmap found: %s. Returning empty list.", + image.getConfig())); + return CompletableFuture.completedFuture(Collections.emptyList()); + } + + Slog.d(TAG, "Screenshot from a managed profile: " + isManagedProfile); + CompletableFuture<List<Notification.Action>> smartActionsFuture; + long startTimeMs = SystemClock.uptimeMillis(); + try { + ActivityManager.RunningTaskInfo runningTask = + ActivityManagerWrapper.getInstance().getRunningTask(); + ComponentName componentName = + (runningTask != null && runningTask.topActivity != null) + ? runningTask.topActivity + : new ComponentName("", ""); + smartActionsFuture = smartActionsProvider.getActions(screenshotId, image, + componentName, + isManagedProfile); + } catch (Throwable e) { + long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; + smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList()); + Slog.e(TAG, "Failed to get future for screenshot notification smart actions.", e); + notifyScreenshotOp(screenshotId, smartActionsProvider, + ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS, + ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR, + waitTimeMs); + } + return smartActionsFuture; + } + + @VisibleForTesting + static List<Notification.Action> getSmartActions(String screenshotId, + CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs, + ScreenshotNotificationSmartActionsProvider smartActionsProvider) { + long startTimeMs = SystemClock.uptimeMillis(); + try { + List<Notification.Action> actions = smartActionsFuture.get(timeoutMs, + TimeUnit.MILLISECONDS); + long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; + Slog.d(TAG, String.format("Got %d smart actions. Wait time: %d ms", + actions.size(), waitTimeMs)); + notifyScreenshotOp(screenshotId, smartActionsProvider, + ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS, + ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS, + waitTimeMs); + return actions; + } catch (Throwable e) { + long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; + Slog.e(TAG, String.format("Error getting smart actions. Wait time: %d ms", waitTimeMs), + e); + ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status = + (e instanceof TimeoutException) + ? ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT + : ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR; + notifyScreenshotOp(screenshotId, smartActionsProvider, + ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS, + status, waitTimeMs); + return Collections.emptyList(); + } + } + + static void notifyScreenshotOp(String screenshotId, + ScreenshotNotificationSmartActionsProvider smartActionsProvider, + ScreenshotNotificationSmartActionsProvider.ScreenshotOp op, + ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) { + try { + smartActionsProvider.notifyOp(screenshotId, op, status, durationMs); + } catch (Throwable e) { + Slog.e(TAG, "Error in notifyScreenshotOp: ", e); + } + } + + static void notifyScreenshotAction(Context context, String screenshotId, String action, + boolean isSmartAction) { + try { + ScreenshotNotificationSmartActionsProvider provider = + SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider( + context, THREAD_POOL_EXECUTOR, new Handler()); + provider.notifyAction(screenshotId, action, isSmartAction); + } catch (Throwable e) { + Slog.e(TAG, "Error in notifyScreenshotAction: ", e); + } + } + /** * Receiver to proxy the share or edit intent, used to clean up the notification and send * appropriate signals to the system (ie. to dismiss the keyguard if necessary). @@ -913,6 +1136,7 @@ class GlobalScreenshot { @Override public void onReceive(Context context, final Intent intent) { + Intent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT); Runnable startActivityRunnable = () -> { try { ActivityManagerWrapper.getInstance().closeSystemWindows( @@ -923,7 +1147,6 @@ class GlobalScreenshot { return; } - Intent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT); if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) { cancelScreenshotNotification(context); } @@ -935,6 +1158,14 @@ class GlobalScreenshot { StatusBar statusBar = SysUiServiceProvider.getComponent(context, StatusBar.class); statusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null, true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */); + + if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) { + String actionType = Intent.ACTION_EDIT.equals(actionIntent.getAction()) + ? ACTION_TYPE_EDIT + : ACTION_TYPE_SHARE; + notifyScreenshotAction(context, intent.getStringExtra(EXTRA_ID), + actionType, false); + } } } @@ -965,6 +1196,31 @@ class GlobalScreenshot { // And delete the image from the media store final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID)); new DeleteImageInBackgroundTask(context).execute(uri); + if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) { + notifyScreenshotAction(context, intent.getStringExtra(EXTRA_ID), + ACTION_TYPE_DELETE, + false); + } + } + } + + /** + * Executes the smart action tapped by the user in the notification. + */ + public static class SmartActionsReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT); + Intent actionIntent = pendingIntent.getIntent(); + String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE); + Slog.d(TAG, "Executing smart action [" + actionType + "]:" + actionIntent); + ActivityOptions opts = ActivityOptions.makeBasic(); + context.startActivityAsUser(actionIntent, opts.toBundle(), + UserHandle.CURRENT); + + notifyScreenshotAction(context, intent.getStringExtra(EXTRA_ID), + actionType, + true); } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java new file mode 100644 index 000000000000..b6f5447d2867 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.screenshot; + +import android.app.Notification; +import android.content.ComponentName; +import android.graphics.Bitmap; +import android.util.Log; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * This class can be overridden by a vendor-specific sys UI implementation, + * in order to provide smart actions in the screenshot notification. + */ +public class ScreenshotNotificationSmartActionsProvider { + /* Key provided in the notification action to get the type of smart action. */ + public static final String ACTION_TYPE = "action_type"; + public static final String DEFAULT_ACTION_TYPE = "Smart Action"; + + /* Define phases of screenshot execution. */ + protected enum ScreenshotOp { + OP_UNKNOWN, + RETRIEVE_SMART_ACTIONS, + REQUEST_SMART_ACTIONS, + WAIT_FOR_SMART_ACTIONS + } + + /* Enum to report success or failure for screenshot execution phases. */ + protected enum ScreenshotOpStatus { + OP_STATUS_UNKNOWN, + SUCCESS, + ERROR, + TIMEOUT + } + + private static final String TAG = "ScreenshotActions"; + + /** + * Default implementation that returns an empty list. + * This method is overridden in vendor-specific Sys UI implementation. + * + * @param screenshotId A generated random unique id for the screenshot. + * @param bitmap The bitmap of the screenshot. The bitmap config must be {@link + * HARDWARE}. + * @param componentName Contains package and activity class names where the screenshot was + * taken. This is used as an additional signal to generate and rank more + * relevant actions. + * @param isManagedProfile The screenshot was taken for a work profile app. + */ + public CompletableFuture<List<Notification.Action>> getActions( + String screenshotId, + Bitmap bitmap, + ComponentName componentName, + boolean isManagedProfile) { + Log.d(TAG, "Returning empty smart action list."); + return CompletableFuture.completedFuture(Collections.emptyList()); + } + + /** + * Notify exceptions and latency encountered during generating smart actions. + * This method is overridden in vendor-specific Sys UI implementation. + * + * @param screenshotId Unique id of the screenshot. + * @param op screenshot execution phase defined in {@link ScreenshotOp} + * @param status {@link ScreenshotOpStatus} to report success or failure. + * @param durationMs latency experienced in different phases of screenshots. + */ + public void notifyOp(String screenshotId, ScreenshotOp op, ScreenshotOpStatus status, + long durationMs) { + Log.d(TAG, "Return without notify."); + } + + /** + * Notify screenshot notification action invoked. + * This method is overridden in vendor-specific Sys UI implementation. + * + * @param screenshotId Unique id of the screenshot. + * @param action type of notification action invoked. + * @param isSmartAction whether action invoked was a smart action. + */ + public void notifyAction(String screenshotId, String action, boolean isSmartAction) { + Log.d(TAG, "Return without notify."); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 34b8bfe59e7e..330a6b541098 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -18,6 +18,7 @@ package com.android.systemui.screenshot; import android.app.Service; import android.content.Intent; +import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -27,6 +28,8 @@ import android.os.UserManager; import android.util.Log; import android.view.WindowManager; +import java.util.function.Consumer; + public class TakeScreenshotService extends Service { private static final String TAG = "TakeScreenshotService"; @@ -36,10 +39,10 @@ public class TakeScreenshotService extends Service { @Override public void handleMessage(Message msg) { final Messenger callback = msg.replyTo; - Runnable finisher = new Runnable() { + Consumer<Uri> finisher = new Consumer<Uri>() { @Override - public void run() { - Message reply = Message.obtain(null, 1); + public void accept(Uri uri) { + Message reply = Message.obtain(null, 1, uri); try { callback.send(reply); } catch (RemoteException e) { @@ -52,7 +55,7 @@ public class TakeScreenshotService extends Service { // animation and error notification. if (!getSystemService(UserManager.class).isUserUnlocked()) { Log.w(TAG, "Skipping screenshot because storage is locked!"); - post(finisher); + post(() -> finisher.accept(null)); return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 6f87b2939cfb..8ee7305d99c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -50,6 +50,7 @@ import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; @@ -96,6 +97,7 @@ public class KeyguardIndicationController implements StateListener, private final IBatteryStats mBatteryInfo; private final SettableWakeLock mWakeLock; private final LockPatternUtils mLockPatternUtils; + private final DockManager mDockManager; private final int mSlowThreshold; private final int mFastThreshold; @@ -104,10 +106,12 @@ public class KeyguardIndicationController implements StateListener, private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); private String mRestingIndication; + private String mAlignmentIndication = ""; private CharSequence mTransientIndication; private ColorStateList mTransientTextColorState; private ColorStateList mInitialTextColorState; private boolean mVisible; + private boolean mHideTransientMessageOnScreenOff; private boolean mPowerPluggedIn; private boolean mPowerPluggedInWired; @@ -140,7 +144,8 @@ public class KeyguardIndicationController implements StateListener, Dependency.get(AccessibilityController.class), UnlockMethodCache.getInstance(context), Dependency.get(StatusBarStateController.class), - KeyguardUpdateMonitor.getInstance(context)); + KeyguardUpdateMonitor.getInstance(context), + Dependency.get(DockManager.class)); } /** @@ -151,7 +156,8 @@ public class KeyguardIndicationController implements StateListener, LockPatternUtils lockPatternUtils, WakeLock wakeLock, ShadeController shadeController, AccessibilityController accessibilityController, UnlockMethodCache unlockMethodCache, StatusBarStateController statusBarStateController, - KeyguardUpdateMonitor keyguardUpdateMonitor) { + KeyguardUpdateMonitor keyguardUpdateMonitor, + DockManager dockManager) { mContext = context; mLockIcon = lockIcon; mShadeController = shadeController; @@ -159,6 +165,8 @@ public class KeyguardIndicationController implements StateListener, mUnlockMethodCache = unlockMethodCache; mStatusBarStateController = statusBarStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mDockManager = dockManager; + mDockManager.addAlignmentStateListener(this::handleAlignStateChanged); // lock icon is not used on all form factors. if (mLockIcon != null) { mLockIcon.setOnLongClickListener(this::handleLockLongClick); @@ -212,6 +220,21 @@ public class KeyguardIndicationController implements StateListener, mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); } + private void handleAlignStateChanged(int alignState) { + String alignmentIndication = ""; + if (alignState == DockManager.ALIGN_STATE_POOR) { + alignmentIndication = + mContext.getResources().getString(R.string.dock_alignment_slow_charging); + } else if (alignState == DockManager.ALIGN_STATE_TERRIBLE) { + alignmentIndication = + mContext.getResources().getString(R.string.dock_alignment_not_charging); + } + if (!alignmentIndication.equals(mAlignmentIndication)) { + mAlignmentIndication = alignmentIndication; + updateIndication(false); + } + } + /** * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this * {@link KeyguardIndicationController}. @@ -255,7 +278,7 @@ public class KeyguardIndicationController implements StateListener, if (visible) { // If this is called after an error message was already shown, we should not clear it. // Otherwise the error message won't be shown - if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) { + if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) { hideTransientIndication(); } updateIndication(false); @@ -317,15 +340,17 @@ public class KeyguardIndicationController implements StateListener, * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. */ public void showTransientIndication(CharSequence transientIndication) { - showTransientIndication(transientIndication, mInitialTextColorState); + showTransientIndication(transientIndication, mInitialTextColorState, + false /* hideOnScreenOff */); } /** * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. */ - public void showTransientIndication(CharSequence transientIndication, - ColorStateList textColorState) { + private void showTransientIndication(CharSequence transientIndication, + ColorStateList textColorState, boolean hideOnScreenOff) { mTransientIndication = transientIndication; + mHideTransientMessageOnScreenOff = hideOnScreenOff && transientIndication != null; mTransientTextColorState = textColorState; mHandler.removeMessages(MSG_HIDE_TRANSIENT); mHandler.removeMessages(MSG_SWIPE_UP_TO_UNLOCK); @@ -344,6 +369,7 @@ public class KeyguardIndicationController implements StateListener, public void hideTransientIndication() { if (mTransientIndication != null) { mTransientIndication = null; + mHideTransientMessageOnScreenOff = false; mHandler.removeMessages(MSG_HIDE_TRANSIENT); updateIndication(false); } @@ -363,6 +389,9 @@ public class KeyguardIndicationController implements StateListener, mTextView.setTextColor(Color.WHITE); if (!TextUtils.isEmpty(mTransientIndication)) { mTextView.switchIndication(mTransientIndication); + } else if (!TextUtils.isEmpty(mAlignmentIndication)) { + mTextView.switchIndication(mAlignmentIndication); + mTextView.setTextColor(Utils.getColorError(mContext)); } else if (mPowerPluggedIn) { String indication = computePowerIndication(); if (animate) { @@ -391,6 +420,9 @@ public class KeyguardIndicationController implements StateListener, && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { mTextView.switchIndication(trustGrantedIndication); mTextView.setTextColor(mInitialTextColorState); + } else if (!TextUtils.isEmpty(mAlignmentIndication)) { + mTextView.switchIndication(mAlignmentIndication); + mTextView.setTextColor(Utils.getColorError(mContext)); } else if (mPowerPluggedIn) { String indication = computePowerIndication(); if (DEBUG_CHARGING_SPEED) { @@ -566,7 +598,8 @@ public class KeyguardIndicationController implements StateListener, String message = mContext.getString(R.string.keyguard_retry); mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState); } else if (mKeyguardUpdateMonitor.isScreenOn()) { - showTransientIndication(mContext.getString(R.string.keyguard_unlock)); + showTransientIndication(mContext.getString(R.string.keyguard_unlock), + mInitialTextColorState, true /* hideOnScreenOff */); hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS); } } @@ -576,7 +609,11 @@ public class KeyguardIndicationController implements StateListener, return; } mDozing = dozing; - updateIndication(false); + if (mHideTransientMessageOnScreenOff && mDozing) { + hideTransientIndication(); + } else { + updateIndication(false); + } updateDisclosure(); } @@ -646,8 +683,7 @@ public class KeyguardIndicationController implements StateListener, @Override public void onBiometricHelp(int msgId, String helpString, BiometricSourceType biometricSourceType) { - KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); - if (!updateMonitor.isUnlockingWithBiometricAllowed()) { + if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) { return; } boolean showSwipeToUnlock = @@ -655,8 +691,8 @@ public class KeyguardIndicationController implements StateListener, if (mStatusBarKeyguardViewManager.isBouncerShowing()) { mStatusBarKeyguardViewManager.showBouncerMessage(helpString, mInitialTextColorState); - } else if (updateMonitor.isScreenOn()) { - showTransientIndication(helpString); + } else if (mKeyguardUpdateMonitor.isScreenOn()) { + showTransientIndication(helpString, mInitialTextColorState, showSwipeToUnlock); if (!showSwipeToUnlock) { hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT); } @@ -670,8 +706,7 @@ public class KeyguardIndicationController implements StateListener, @Override public void onBiometricError(int msgId, String errString, BiometricSourceType biometricSourceType) { - KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); - if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) { + if (shouldSuppressBiometricError(msgId, biometricSourceType, mKeyguardUpdateMonitor)) { return; } animatePadlockError(); @@ -681,7 +716,7 @@ public class KeyguardIndicationController implements StateListener, showSwipeUpToUnlock(); } else if (mStatusBarKeyguardViewManager.isBouncerShowing()) { mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState); - } else if (updateMonitor.isScreenOn()) { + } else if (mKeyguardUpdateMonitor.isScreenOn()) { showTransientIndication(errString); // We want to keep this message around in case the screen was off hideTransientIndicationDelayed(HIDE_DELAY_MS); @@ -721,13 +756,15 @@ public class KeyguardIndicationController implements StateListener, @Override public void onTrustAgentErrorMessage(CharSequence message) { - showTransientIndication(message, Utils.getColorError(mContext)); + showTransientIndication(message, Utils.getColorError(mContext), + false /* hideOnScreenOff */); } @Override public void onScreenTurnedOn() { if (mMessageToShowOnScreenOn != null) { - showTransientIndication(mMessageToShowOnScreenOn, Utils.getColorError(mContext)); + showTransientIndication(mMessageToShowOnScreenOn, Utils.getColorError(mContext), + false /* hideOnScreenOff */); // We want to keep this message around in case the screen was off hideTransientIndicationDelayed(HIDE_DELAY_MS); mMessageToShowOnScreenOn = null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index a37367e4bb25..da0f83da5a71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -89,7 +89,6 @@ public class NotificationEntryManager implements private NotificationRowBinder mNotificationRowBinder; private NotificationPresenter mPresenter; - private NotificationListenerService.RankingMap mLatestRankingMap; @VisibleForTesting protected NotificationData mNotificationData; @@ -168,8 +167,7 @@ public class NotificationEntryManager implements /** Adds a {@link NotificationLifetimeExtender}. */ public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) { mNotificationLifetimeExtenders.add(extender); - extender.setCallback(key -> removeNotification(key, mLatestRankingMap, - UNDEFINED_DISMISS_REASON)); + extender.setCallback(key -> removeNotification(key, null, UNDEFINED_DISMISS_REASON)); } public NotificationData getNotificationData() { @@ -307,7 +305,6 @@ public class NotificationEntryManager implements if (!forceRemove && !entryDismissed) { for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) { if (extender.shouldExtendLifetime(entry)) { - mLatestRankingMap = ranking; extendLifetime(entry, extender); lifetimeExtended = true; break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java index 150667b86828..ef09434aa395 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java @@ -39,6 +39,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.HeadsUpManager; import javax.inject.Inject; @@ -63,6 +64,7 @@ public class NotificationInterruptionStateProvider { private final Context mContext; private final PowerManager mPowerManager; private final IDreamManager mDreamManager; + private final BatteryController mBatteryController; private NotificationPresenter mPresenter; private HeadsUpManager mHeadsUpManager; @@ -75,13 +77,14 @@ public class NotificationInterruptionStateProvider { @Inject public NotificationInterruptionStateProvider(Context context, NotificationFilter filter, - StatusBarStateController stateController) { + StatusBarStateController stateController, BatteryController batteryController) { this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE), IDreamManager.Stub.asInterface( ServiceManager.checkService(DreamService.DREAM_SERVICE)), new AmbientDisplayConfiguration(context), filter, + batteryController, stateController); } @@ -92,10 +95,12 @@ public class NotificationInterruptionStateProvider { IDreamManager dreamManager, AmbientDisplayConfiguration ambientDisplayConfiguration, NotificationFilter notificationFilter, + BatteryController batteryController, StatusBarStateController statusBarStateController) { mContext = context; mPowerManager = powerManager; mDreamManager = dreamManager; + mBatteryController = batteryController; mAmbientDisplayConfiguration = ambientDisplayConfiguration; mNotificationFilter = notificationFilter; mStatusBarStateController = statusBarStateController; @@ -293,6 +298,13 @@ public class NotificationInterruptionStateProvider { return false; } + if (mBatteryController.isAodPowerSave()) { + if (DEBUG_HEADS_UP) { + Log.d(TAG, "No pulsing: disabled by battery saver: " + sbn.getKey()); + } + return false; + } + if (!canAlertCommon(entry)) { if (DEBUG_HEADS_UP) { Log.d(TAG, "No pulsing: notification shouldn't alert: " + sbn.getKey()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java index 00092929fd49..7b4ed3f8a7d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java @@ -57,7 +57,6 @@ public class NotificationData { private final ArrayMap<String, NotificationEntry> mEntries = new ArrayMap<>(); private final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>(); - private final ArrayList<NotificationEntry> mFilteredForUser = new ArrayList<>(); private final NotificationGroupManager mGroupManager = Dependency.get(NotificationGroupManager.class); @@ -166,20 +165,20 @@ public class NotificationData { } public ArrayList<NotificationEntry> getNotificationsForCurrentUser() { - mFilteredForUser.clear(); - synchronized (mEntries) { final int len = mEntries.size(); + ArrayList<NotificationEntry> filteredForUser = new ArrayList<>(len); + for (int i = 0; i < len; i++) { NotificationEntry entry = mEntries.valueAt(i); final StatusBarNotification sbn = entry.notification; if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) { continue; } - mFilteredForUser.add(entry); + filteredForUser.add(entry); } + return filteredForUser; } - return mFilteredForUser; } public NotificationEntry get(String key) { @@ -201,6 +200,9 @@ public class NotificationData { removed = mEntries.remove(key); } if (removed == null) return null; + // NEM may pass us a null ranking map if removing a lifetime-extended notification, + // so use the most recent ranking + if (ranking == null) ranking = mRankingMap; mGroupManager.onEntryRemoved(removed); updateRankingAndSort(ranking); return removed; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 027e8e426c4b..121508b877d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -155,6 +155,12 @@ public final class NotificationEntry { public boolean canBubble; /** + * Whether this notification has changed in visual appearance since the previous post. + * New notifications are interruptive by default. + */ + public boolean isVisuallyInterruptive; + + /** * Whether this notification is shown to the user as a high priority notification: visible on * the lock screen/status bar and in the top section in the shade. */ @@ -196,6 +202,7 @@ public final class NotificationEntry { suppressedVisualEffects = ranking.getSuppressedVisualEffects(); suspended = ranking.isSuspended(); canBubble = ranking.canBubble(); + isVisuallyInterruptive = ranking.visuallyInterruptive(); } public void setInterruption() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 179375e31dd3..4e91e4c84e99 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -240,7 +240,7 @@ public class KeyguardClockPositionAlgorithm { * @return Alpha from 0 to 1. */ private float getClockAlpha(int y) { - float alphaKeyguard = Math.max(0, y / Math.max(1f, getExpandedPreferredClockY())); + float alphaKeyguard = Math.max(0, y / Math.max(1f, getClockY(1f))); alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard); return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java index d2023ec49ebc..dcb349ba9c8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java @@ -79,10 +79,13 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen IWallpaperManager service = IWallpaperManager.Stub.asInterface( ServiceManager.getService(Context.WALLPAPER_SERVICE)); - try { - service.setLockWallpaperCallback(this); - } catch (RemoteException e) { - Log.e(TAG, "System dead?" + e); + if (service != null) { + // Service is disabled on some devices like Automotive + try { + service.setLockWallpaperCallback(this); + } catch (RemoteException e) { + Log.e(TAG, "System dead?" + e); + } } } @@ -108,6 +111,11 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen public LoaderResult loadBitmap(int currentUserId, UserHandle selectedUser) { // May be called on any thread - only use thread safe operations. + if (!mWallpaperManager.isWallpaperSupported()) { + // When wallpaper is not supported, show the system wallpaper + return LoaderResult.success(null); + } + // Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK // wallpaper. final int lockWallpaperUserId = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java index bfd17b9abc72..68ea81dfce55 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java @@ -107,6 +107,11 @@ public class NavBarTintController implements View.OnAttachStateChangeListener, requestUpdateSamplingListener(); } + void stopAndDestroy() { + stop(); + mSamplingListener.destroy(); + } + @Override public void onViewAttachedToWindow(View view) { requestUpdateSamplingListener(); @@ -114,8 +119,7 @@ public class NavBarTintController implements View.OnAttachStateChangeListener, @Override public void onViewDetachedFromWindow(View view) { - // Defer calling updateSamplingListener the attach info has not yet been reset - requestUpdateSamplingListener(); + stopAndDestroy(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index f5f2dd9d2438..d3a56cad8d96 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -137,6 +137,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback private final MetricsLogger mMetricsLogger; private final DeviceProvisionedController mDeviceProvisionedController; private final StatusBarStateController mStatusBarStateController; + private final NavigationModeController mNavigationModeController; protected NavigationBarView mNavigationBarView = null; @@ -253,6 +254,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mAssistManager = assistManager; mAssistantAvailable = mAssistManager.getAssistInfoForUser(UserHandle.USER_CURRENT) != null; mOverviewProxyService = overviewProxyService; + mNavigationModeController = navigationModeController; mNavBarMode = navigationModeController.addListener(this); } @@ -292,6 +294,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback @Override public void onDestroy() { super.onDestroy(); + mNavigationModeController.removeListener(this); mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener); mContentResolver.unregisterContentObserver(mMagnificationObserver); mContentResolver.unregisterContentObserver(mAssistContentObserver); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 17c200eaef6a..a0e7d0246d73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -379,8 +379,11 @@ public class PhoneStatusBarPolicy mContext.getString(R.string.accessibility_quick_settings_bluetooth_on); boolean bluetoothVisible = false; if (mBluetooth != null) { - if (mBluetooth.isBluetoothConnected()) { - contentDescription = mContext.getString(R.string.accessibility_bluetooth_connected); + if (mBluetooth.isBluetoothConnected() + && (mBluetooth.isBluetoothAudioActive() + || !mBluetooth.isBluetoothAudioProfileOnly())) { + contentDescription = mContext.getString( + R.string.accessibility_bluetooth_connected); bluetoothVisible = mBluetooth.isBluetoothEnabled(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java index 2b0bb21c6560..26cc74d16ccf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java @@ -127,6 +127,11 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, updateSamplingListener(); } + void stopAndDestroy() { + stop(); + mSamplingListener.destroy(); + } + @Override public void onViewAttachedToWindow(View view) { updateSamplingListener(); @@ -134,9 +139,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, @Override public void onViewDetachedFromWindow(View view) { - // isAttachedToWindow is only changed after this call to the listeners, so let's post it - // instead - postUpdateSamplingListener(); + stopAndDestroy(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 559df18ef478..c6dee5e9bdf5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -229,7 +229,6 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.tuner.TunerService; import com.android.systemui.util.InjectionInflationController; import com.android.systemui.volume.VolumeComponent; @@ -344,7 +343,8 @@ public class StatusBar extends SystemUI implements DemoMode, private BrightnessMirrorController mBrightnessMirrorController; private boolean mBrightnessMirrorVisible; protected BiometricUnlockController mBiometricUnlockController; - private LightBarController mLightBarController; + protected LightBarController mLightBarController; + @Nullable protected LockscreenWallpaper mLockscreenWallpaper; @VisibleForTesting protected AutoHideController mAutoHideController; @@ -410,17 +410,17 @@ public class StatusBar extends SystemUI implements DemoMode, private final int[] mAbsPos = new int[2]; private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>(); - private NotificationGutsManager mGutsManager; + protected NotificationGutsManager mGutsManager; protected NotificationLogger mNotificationLogger; protected NotificationEntryManager mEntryManager; - private NotificationListController mNotificationListController; - private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; + protected NotificationListController mNotificationListController; + protected NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; protected NotificationViewHierarchyManager mViewHierarchyManager; protected ForegroundServiceController mForegroundServiceController; protected AppOpsController mAppOpsController; protected KeyguardViewMediator mKeyguardViewMediator; - private ZenModeController mZenController; - private final NotificationAlertingManager mNotificationAlertingManager = + protected ZenModeController mZenController; + protected final NotificationAlertingManager mNotificationAlertingManager = Dependency.get(NotificationAlertingManager.class); // for disabling the status bar @@ -483,18 +483,20 @@ public class StatusBar extends SystemUI implements DemoMode, protected boolean mDozing; private boolean mDozingRequested; - private NotificationMediaManager mMediaManager; + protected NotificationMediaManager mMediaManager; protected NotificationLockscreenUserManager mLockscreenUserManager; protected NotificationRemoteInputManager mRemoteInputManager; + private boolean mWallpaperSupported; private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class); - if (wallpaperManager == null) { - Log.w(TAG, "WallpaperManager not available"); + if (!mWallpaperSupported) { + // Receiver should not have been registered at all... + Log.wtf(TAG, "WallpaperManager not supported"); return; } + WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class); WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT); final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean( com.android.internal.R.bool.config_dozeSupportsAodWallpaper); @@ -555,18 +557,19 @@ public class StatusBar extends SystemUI implements DemoMode, private KeyguardUserSwitcher mKeyguardUserSwitcher; protected UserSwitcherController mUserSwitcherController; - private NetworkController mNetworkController; - private KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); - private BatteryController mBatteryController; + protected NetworkController mNetworkController; + protected KeyguardMonitor mKeyguardMonitor; + protected BatteryController mBatteryController; protected boolean mPanelExpanded; private UiModeManager mUiModeManager; protected boolean mIsKeyguard; private LogMaker mStatusBarStateLog; protected NotificationIconAreaController mNotificationIconAreaController; @Nullable private View mAmbientIndicationContainer; - private SysuiColorExtractor mColorExtractor; - private ScreenLifecycle mScreenLifecycle; - @VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle; + protected SysuiColorExtractor mColorExtractor; + protected ScreenLifecycle mScreenLifecycle; + @VisibleForTesting + protected WakefulnessLifecycle mWakefulnessLifecycle; private final View.OnClickListener mGoToLockedShadeListener = v -> { if (mState == StatusBarState.KEYGUARD) { @@ -598,7 +601,7 @@ public class StatusBar extends SystemUI implements DemoMode, private HeadsUpAppearanceController mHeadsUpAppearanceController; private boolean mVibrateOnOpening; - private VibratorHelper mVibratorHelper; + protected VibratorHelper mVibratorHelper; private ActivityLaunchAnimator mActivityLaunchAnimator; protected StatusBarNotificationPresenter mPresenter; private NotificationActivityStarter mNotificationActivityStarter; @@ -628,40 +631,24 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void start() { - mGroupManager = Dependency.get(NotificationGroupManager.class); - mGroupAlertTransferHelper = Dependency.get(NotificationGroupAlertTransferHelper.class); - mVisualStabilityManager = Dependency.get(VisualStabilityManager.class); - mNotificationLogger = Dependency.get(NotificationLogger.class); - mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); - mNotificationListener = Dependency.get(NotificationListener.class); + getDependencies(); + if (mScreenLifecycle != null && mScreenObserver != null) { + mScreenLifecycle.addObserver(mScreenObserver); + } + + if (mWakefulnessLifecycle != null && mWakefulnessObserver != null) { + mWakefulnessLifecycle.addObserver(mWakefulnessObserver); + } + mNotificationListener.registerAsSystemService(); - mNetworkController = Dependency.get(NetworkController.class); - mUserSwitcherController = Dependency.get(UserSwitcherController.class); - mScreenLifecycle = Dependency.get(ScreenLifecycle.class); - mScreenLifecycle.addObserver(mScreenObserver); - mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); - mWakefulnessLifecycle.addObserver(mWakefulnessObserver); - mBatteryController = Dependency.get(BatteryController.class); - mAssistManager = Dependency.get(AssistManager.class); + if (mBubbleController != null) { + mBubbleController.setExpandListener(mBubbleExpandListener); + } + mUiModeManager = mContext.getSystemService(UiModeManager.class); - mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class); - mGutsManager = Dependency.get(NotificationGutsManager.class); - mMediaManager = Dependency.get(NotificationMediaManager.class); - mEntryManager = Dependency.get(NotificationEntryManager.class); - mBypassHeadsUpNotifier.setUp(mEntryManager); - mNotificationInterruptionStateProvider = - Dependency.get(NotificationInterruptionStateProvider.class); - mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); - mForegroundServiceController = Dependency.get(ForegroundServiceController.class); - mAppOpsController = Dependency.get(AppOpsController.class); - mZenController = Dependency.get(ZenModeController.class); mKeyguardViewMediator = getComponent(KeyguardViewMediator.class); - mColorExtractor = Dependency.get(SysuiColorExtractor.class); - mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); - mNavigationBarController = Dependency.get(NavigationBarController.class); - mBubbleController = Dependency.get(BubbleController.class); - mBubbleController.setExpandListener(mBubbleExpandListener); mActivityIntentHelper = new ActivityIntentHelper(mContext); + KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance(); if (sliceProvider != null) { sliceProvider.initDependencies(mMediaManager, mStatusBarStateController, @@ -707,6 +694,8 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); mFalsingManager = Dependency.get(FalsingManager.class); + mWallpaperSupported = + mContext.getSystemService(WallpaperManager.class).isWallpaperSupported(); // Connect in to the status bar manager service mCommandQueue = getComponent(CommandQueue.class); @@ -721,11 +710,15 @@ public class StatusBar extends SystemUI implements DemoMode, createAndAddWindows(result); - // Make sure we always have the most current wallpaper info. - IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); - mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL, - wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */); - mWallpaperChangedReceiver.onReceive(mContext, null); + if (mWallpaperSupported) { + // Make sure we always have the most current wallpaper info. + IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); + mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL, + wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */); + mWallpaperChangedReceiver.onReceive(mContext, null); + } else if (DEBUG) { + Log.v(TAG, "start(): no wallpaper service "); + } // Set up the initial notification state. This needs to happen before CommandQueue.disable() setUpPresenter(); @@ -760,12 +753,14 @@ public class StatusBar extends SystemUI implements DemoMode, mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF, null); - IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface( - ServiceManager.getService(Context.WALLPAPER_SERVICE)); - try { - wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */); - } catch (RemoteException e) { - // Just pass, nothing critical. + if (mWallpaperSupported) { + IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface( + ServiceManager.getService(Context.WALLPAPER_SERVICE)); + try { + wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */); + } catch (RemoteException e) { + // Just pass, nothing critical. + } } // end old BaseStatusBar.start(). @@ -896,7 +891,7 @@ public class StatusBar extends SystemUI implements DemoMode, createNavigationBar(result); - if (ENABLE_LOCKSCREEN_WALLPAPER) { + if (ENABLE_LOCKSCREEN_WALLPAPER && mWallpaperSupported) { mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler); } @@ -910,20 +905,22 @@ public class StatusBar extends SystemUI implements DemoMode, R.id.ambient_indication_container); // TODO: Find better place for this callback. - mBatteryController.addCallback(new BatteryStateChangeCallback() { - @Override - public void onPowerSaveChanged(boolean isPowerSave) { - mHandler.post(mCheckBarModes); - if (mDozeServiceHost != null) { - mDozeServiceHost.firePowerSaveChanged(isPowerSave); + if (mBatteryController != null) { + mBatteryController.addCallback(new BatteryStateChangeCallback() { + @Override + public void onPowerSaveChanged(boolean isPowerSave) { + mHandler.post(mCheckBarModes); + if (mDozeServiceHost != null) { + mDozeServiceHost.firePowerSaveChanged(isPowerSave); + } } - } - @Override - public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { - // noop - } - }); + @Override + public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { + // noop + } + }); + } mAutoHideController = Dependency.get(AutoHideController.class); mAutoHideController.setStatusBar(this); @@ -965,28 +962,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarWindow::onShowingLaunchAffordanceChanged); // Set up the quick settings tile panel - View container = mStatusBarWindow.findViewById(R.id.qs_frame); - if (container != null) { - FragmentHostManager fragmentHostManager = FragmentHostManager.get(container); - ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame, - Dependency.get(ExtensionController.class) - .newExtension(QS.class) - .withPlugin(QS.class) - .withDefault(this::createDefaultQSFragment) - .build()); - mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow, - (visible) -> { - mBrightnessMirrorVisible = visible; - updateScrimController(); - }); - fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> { - QS qs = (QS) f; - if (qs instanceof QSFragment) { - mQSPanel = ((QSFragment) qs).getQsPanel(); - mQSPanel.setBrightnessMirror(mBrightnessMirrorController); - } - }); - } + setUpQuickSettingsTilePanel(); mReportRejectedTouch = mStatusBarWindow.findViewById(R.id.report_rejected_touch); if (mReportRejectedTouch != null) { @@ -1086,7 +1062,9 @@ public class StatusBar extends SystemUI implements DemoMode, mForegroundServiceController, mDeviceProvisionedController); - mAppOpsController.addCallback(APP_OPS, this); + if (mAppOpsController != null) { + mAppOpsController.addCallback(APP_OPS, this); + } mNotificationShelf.setOnActivatedListener(mPresenter); mRemoteInputManager.getController().addCallback(mStatusBarWindowController); @@ -1116,6 +1094,73 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationListController.bind(); } + protected void getDependencies() { + // Icons + mIconController = Dependency.get(StatusBarIconController.class); + mLightBarController = Dependency.get(LightBarController.class); + + // Keyguard + mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); + mScreenLifecycle = Dependency.get(ScreenLifecycle.class); + mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); + + // Notifications + mEntryManager = Dependency.get(NotificationEntryManager.class); + mForegroundServiceController = Dependency.get(ForegroundServiceController.class); + mGroupAlertTransferHelper = Dependency.get(NotificationGroupAlertTransferHelper.class); + mGroupManager = Dependency.get(NotificationGroupManager.class); + mGutsManager = Dependency.get(NotificationGutsManager.class); + mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class); + mMediaManager = Dependency.get(NotificationMediaManager.class); + mNotificationInterruptionStateProvider = + Dependency.get(NotificationInterruptionStateProvider.class); + mNotificationListener = Dependency.get(NotificationListener.class); + mNotificationLogger = Dependency.get(NotificationLogger.class); + mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); + mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); + mVisualStabilityManager = Dependency.get(VisualStabilityManager.class); + + // Policy + mBatteryController = Dependency.get(BatteryController.class); + mNetworkController = Dependency.get(NetworkController.class); + mZenController = Dependency.get(ZenModeController.class); + + // Others + mAppOpsController = Dependency.get(AppOpsController.class); + mAssistManager = Dependency.get(AssistManager.class); + mBubbleController = Dependency.get(BubbleController.class); + mColorExtractor = Dependency.get(SysuiColorExtractor.class); + mNavigationBarController = Dependency.get(NavigationBarController.class); + mUserSwitcherController = Dependency.get(UserSwitcherController.class); + mVibratorHelper = Dependency.get(VibratorHelper.class); + } + + protected void setUpQuickSettingsTilePanel() { + View container = mStatusBarWindow.findViewById(R.id.qs_frame); + if (container != null) { + FragmentHostManager fragmentHostManager = FragmentHostManager.get(container); + ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame, + Dependency.get(ExtensionController.class) + .newExtension(QS.class) + .withPlugin(QS.class) + .withDefault(this::createDefaultQSFragment) + .build()); + mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow, + (visible) -> { + mBrightnessMirrorVisible = visible; + updateScrimController(); + }); + fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> { + QS qs = (QS) f; + if (qs instanceof QSFragment) { + mQSPanel = ((QSFragment) qs).getQsPanel(); + mQSPanel.setBrightnessMirror(mBrightnessMirrorController); + } + }); + } + } + + /** * Post-init task of {@link #start()} * @param state1 disable1 flags @@ -1936,7 +1981,9 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarWindow.cancelExpandHelper(); mStatusBarView.collapsePanel(true /* animate */, delayed, speedUpFactor); } else { - mBubbleController.collapseStack(); + if (mBubbleController != null) { + mBubbleController.collapseStack(); + } } } @@ -2318,6 +2365,7 @@ public class StatusBar extends SystemUI implements DemoMode, pw.println(Settings.Global.zenModeToString(Settings.Global.getInt( mContext.getContentResolver(), Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF))); + pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported); if (mStatusBarView != null) { dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); @@ -2590,7 +2638,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (mRemoteInputManager.getController() != null) { mRemoteInputManager.getController().closeRemoteInputs(); } - if (mBubbleController.isStackExpanded()) { + if (mBubbleController != null && mBubbleController.isStackExpanded()) { mBubbleController.collapseStack(); } if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) { @@ -2606,7 +2654,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (mStatusBarWindowController != null) { mStatusBarWindowController.setNotTouchable(false); } - if (mBubbleController.isStackExpanded()) { + if (mBubbleController != null && mBubbleController.isStackExpanded()) { mBubbleController.collapseStack(); } finishBarAnimations(); @@ -2700,9 +2748,13 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void setLockscreenUser(int newUserId) { - mLockscreenWallpaper.setCurrentUser(newUserId); + if (mLockscreenWallpaper != null) { + mLockscreenWallpaper.setCurrentUser(newUserId); + } mScrimController.setCurrentUser(newUserId); - mWallpaperChangedReceiver.onReceive(mContext, null); + if (mWallpaperSupported) { + mWallpaperChangedReceiver.onReceive(mContext, null); + } } /** @@ -2984,7 +3036,9 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); mPendingRemoteInputView = null; updateIsKeyguard(); - mAssistManager.onLockscreenShown(); + if (mAssistManager != null) { + mAssistManager.onLockscreenShown(); + } } public boolean hideKeyguard() { @@ -3000,10 +3054,21 @@ public class StatusBar extends SystemUI implements DemoMode, return mState == StatusBarState.FULLSCREEN_USER_SWITCHER; } + private boolean isAutomotive() { + return mContext != null + && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); + } + private boolean updateIsKeyguard() { boolean wakeAndUnlocking = mBiometricUnlockController.getMode() == BiometricUnlockController.MODE_WAKE_AND_UNLOCK; + if (mScreenLifecycle == null && isAutomotive()) { + // TODO(b/146144370): workaround to avoid NPE when device goes into STR (Suspend to RAM) + Log.w(TAG, "updateIsKeyguard(): mScreenLifeCycle not set yet"); + mScreenLifecycle = Dependency.get(ScreenLifecycle.class); + } + // For dozing, keyguard needs to be shown whenever the device is non-interactive. Otherwise // there's no surface we can show to the user. Note that the device goes fully interactive // late in the transition, so we also allow the device to start dozing once the screen has @@ -3030,7 +3095,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void showKeyguardImpl() { mIsKeyguard = true; - if (mKeyguardMonitor.isLaunchTransitionFadingAway()) { + if (mKeyguardMonitor != null && mKeyguardMonitor.isLaunchTransitionFadingAway()) { mNotificationPanel.animate().cancel(); onLaunchTransitionFadingEnded(); } @@ -3334,7 +3399,9 @@ public class StatusBar extends SystemUI implements DemoMode, if (mNotificationPanel.canPanelBeCollapsed()) { animateCollapsePanels(); } else { - mBubbleController.performBackPressIfNeeded(); + if (mBubbleController != null) { + mBubbleController.performBackPressIfNeeded(); + } } return true; } @@ -3495,9 +3562,11 @@ public class StatusBar extends SystemUI implements DemoMode, } private void updateKeyguardState() { - mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), - mUnlockMethodCache.isMethodSecure(), - mStatusBarKeyguardViewManager.isOccluded()); + if (mKeyguardMonitor != null) { + mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), + mUnlockMethodCache.isMethodSecure(), + mStatusBarKeyguardViewManager.isOccluded()); + } } public void onActivationReset() { @@ -3886,7 +3955,7 @@ public class StatusBar extends SystemUI implements DemoMode, // We don't want to end up in KEYGUARD state when we're unlocking with // fingerprint from doze. We should cross fade directly from black. boolean unlocking = mBiometricUnlockController.isWakeAndUnlock() - || mKeyguardMonitor.isKeyguardFadingAway(); + || (mKeyguardMonitor != null && mKeyguardMonitor.isKeyguardFadingAway()); // Do not animate the scrim expansion when triggered by the fingerprint sensor. mScrimController.setExpansionAffectsAlpha( @@ -3915,7 +3984,7 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController.transitionTo(ScrimState.AOD); } else if (mIsKeyguard && !unlocking) { mScrimController.transitionTo(ScrimState.KEYGUARD); - } else if (mBubbleController.isStackExpanded()) { + } else if (mBubbleController != null && mBubbleController.isStackExpanded()) { mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED); } else { mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); @@ -4217,8 +4286,8 @@ public class StatusBar extends SystemUI implements DemoMode, protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; protected KeyguardManager mKeyguardManager; - private DeviceProvisionedController mDeviceProvisionedController - = Dependency.get(DeviceProvisionedController.class); + protected DeviceProvisionedController mDeviceProvisionedController = + Dependency.get(DeviceProvisionedController.class); protected NavigationBarController mNavigationBarController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index 111cdd2cc32a..738d076e13c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -45,9 +45,7 @@ public interface BatteryController extends DemoMode, Dumpable, /** * Returns {@code true} if AOD was disabled by power saving policies. */ - default boolean isAodPowerSave() { - return isPowerSave(); - } + boolean isAodPowerSave(); /** * A listener that will be notified whenever a change in battery level or power save mode has diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java index 42e02d5846bc..0c5b8515071d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java @@ -31,6 +31,8 @@ public interface BluetoothController extends CallbackController<Callback>, Dumpa boolean isBluetoothConnected(); boolean isBluetoothConnecting(); + boolean isBluetoothAudioProfileOnly(); + boolean isBluetoothAudioActive(); String getConnectedDeviceName(); void setBluetoothEnabled(boolean enabled); Collection<CachedBluetoothDevice> getDevices(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index 78e845a68445..351579a95710 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -34,6 +34,7 @@ import android.util.Log; import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfile; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import java.io.FileDescriptor; @@ -66,6 +67,8 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa private boolean mEnabled; private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; + private boolean mAudioProfileOnly; + private boolean mIsActive; private final H mHandler = new H(Looper.getMainLooper()); private int mState; @@ -103,6 +106,8 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa } pw.print(" mEnabled="); pw.println(mEnabled); pw.print(" mConnectionState="); pw.println(stateToString(mConnectionState)); + pw.print(" mAudioProfileOnly="); pw.println(mAudioProfileOnly); + pw.print(" mIsActive="); pw.println(mIsActive); pw.print(" mConnectedDevices="); pw.println(mConnectedDevices); pw.print(" mCallbacks.size="); pw.println(mHandler.mCallbacks.size()); pw.println(" Bluetooth Devices:"); @@ -176,6 +181,16 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa } @Override + public boolean isBluetoothAudioProfileOnly() { + return mAudioProfileOnly; + } + + @Override + public boolean isBluetoothAudioActive() { + return mIsActive; + } + + @Override public void setBluetoothEnabled(boolean enabled) { if (mLocalBluetoothManager != null) { mLocalBluetoothManager.getBluetoothAdapter().setBluetoothEnabled(enabled); @@ -239,6 +254,48 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa mConnectionState = state; mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED); } + updateAudioProfile(); + } + + private void updateActive() { + boolean isActive = false; + + for (CachedBluetoothDevice device : getDevices()) { + isActive |= device.isActiveDevice(BluetoothProfile.HEADSET) + || device.isActiveDevice(BluetoothProfile.A2DP) + || device.isActiveDevice(BluetoothProfile.HEARING_AID); + } + + if (mIsActive != isActive) { + mIsActive = isActive; + mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED); + } + } + + private void updateAudioProfile() { + boolean audioProfileConnected = false; + boolean otherProfileConnected = false; + + for (CachedBluetoothDevice device : getDevices()) { + for (LocalBluetoothProfile profile : device.getProfiles()) { + int profileId = profile.getProfileId(); + boolean isConnected = device.isConnectedProfile(profile); + if (profileId == BluetoothProfile.HEADSET + || profileId == BluetoothProfile.A2DP + || profileId == BluetoothProfile.HEARING_AID) { + audioProfileConnected |= isConnected; + } else { + otherProfileConnected |= isConnected; + } + } + } + + boolean audioProfileOnly = (audioProfileConnected && !otherProfileConnected); + if (audioProfileOnly != mAudioProfileOnly) { + mAudioProfileOnly = audioProfileOnly; + mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED); + } + } @Override @@ -294,6 +351,16 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa } @Override + public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { + if (DEBUG) { + Log.d(TAG, "ActiveDeviceChanged=" + activeDevice.getAddress() + + " profileId=" + bluetoothProfile); + } + updateActive(); + mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED); + } + + @Override public void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { if (DEBUG) { Log.d(TAG, "ACLConnectionStateChanged=" + cachedDevice.getAddress() + " " diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 5020744e6982..1c646b2d4ee2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -15,6 +15,9 @@ */ package com.android.systemui.statusbar.policy; +import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; + import android.content.Context; import android.content.Intent; import android.database.ContentObserver; @@ -708,7 +711,11 @@ public class MobileSignalController extends SignalController< } mServiceState = state; if (mServiceState != null) { - updateDataNetType(mServiceState.getDataNetworkType()); + NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo( + DOMAIN_PS, TRANSPORT_TYPE_WWAN); + if (regInfo != null) { + updateDataNetType(regInfo.getAccessNetworkTechnology()); + } } updateTelephony(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index ed0b9d929466..919ca12648ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -22,6 +22,7 @@ import android.text.TextPaint; import android.text.method.TransformationMethod; import android.util.AttributeSet; import android.util.Log; +import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -238,13 +239,15 @@ public class SmartReplyView extends ViewGroup { public List<Button> inflateSmartActions(Context packageContext, @NonNull SmartActions smartActions, SmartReplyController smartReplyController, NotificationEntry entry, HeadsUpManager headsUpManager, boolean delayOnClickListener) { + Context themedPackageContext = new ContextThemeWrapper(packageContext, mContext.getTheme()); List<Button> buttons = new ArrayList<>(); int numSmartActions = smartActions.actions.size(); for (int n = 0; n < numSmartActions; n++) { Notification.Action action = smartActions.actions.get(n); if (action.actionIntent != null) { buttons.add(inflateActionButton( - this, getContext(), packageContext, n, smartActions, smartReplyController, + this, getContext(), themedPackageContext, n, smartActions, + smartReplyController, entry, headsUpManager, delayOnClickListener)); } } diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index efb4ff008b23..a897b0098bb7 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -35,7 +35,6 @@ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> - <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> <uses-permission android:name="android.permission.REQUEST_NETWORK_SCORES" /> <uses-permission android:name="android.permission.CONTROL_VPN" /> diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java index 20983fc16080..caca6631f85b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleBehaviorControllerTest.java @@ -67,7 +67,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase { @Mock private ScreenDecorations mMockScreenDecorations; @Mock private AssistUtils mMockAssistUtils; @Mock private Handler mMockHandler; - @Mock private PhenotypeHelper mMockPhenotypeHelper; + @Mock private DeviceConfigHelper mMockDeviceConfigHelper; @Mock private AssistHandleOffBehavior mMockOffBehavior; @Mock private AssistHandleLikeHomeBehavior mMockLikeHomeBehavior; @Mock private AssistHandleReminderExpBehavior mMockReminderExpBehavior; @@ -97,7 +97,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase { mMockAssistUtils, mMockHandler, () -> mMockScreenDecorations, - mMockPhenotypeHelper, + mMockDeviceConfigHelper, behaviorMap, mMockNavigationModeController, mMockDumpController); @@ -216,7 +216,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase { public void showAndGo_doesNothingIfRecentlyHidden() { // Arrange when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME); - when(mMockPhenotypeHelper.getLong( + when(mMockDeviceConfigHelper.getLong( eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS), anyLong())).thenReturn(10000L); mAssistHandleBehaviorController.showAndGo(); @@ -297,7 +297,7 @@ public class AssistHandleBehaviorControllerTest extends SysuiTestCase { public void showAndGoDelayed_doesNothingIfRecentlyHidden() { // Arrange when(mMockAssistUtils.getAssistComponentForUser(anyInt())).thenReturn(COMPONENT_NAME); - when(mMockPhenotypeHelper.getLong( + when(mMockDeviceConfigHelper.getLong( eq(SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS), anyLong())).thenReturn(10000L); mAssistHandleBehaviorController.showAndGo(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index ba434d4fd0bd..5a4e6c903650 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -74,6 +74,7 @@ import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBarWindowController; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.ZenModeController; @@ -173,7 +174,8 @@ public class BubbleControllerTest extends SysuiTestCase { TestableNotificationInterruptionStateProvider interruptionStateProvider = new TestableNotificationInterruptionStateProvider(mContext, mock(NotificationFilter.class), - mock(StatusBarStateController.class)); + mock(StatusBarStateController.class), + mock(BatteryController.class)); interruptionStateProvider.setUpWithPresenter( mock(NotificationPresenter.class), mock(HeadsUpManager.class), @@ -659,8 +661,9 @@ public class BubbleControllerTest extends SysuiTestCase { NotificationInterruptionStateProvider { TestableNotificationInterruptionStateProvider(Context context, - NotificationFilter filter, StatusBarStateController controller) { - super(context, filter, controller); + NotificationFilter filter, StatusBarStateController controller, + BatteryController batteryController) { + super(context, filter, controller, batteryController); mUseHeadsUp = true; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java index 839b5e4472c6..fd48d34ebea6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java @@ -21,6 +21,7 @@ package com.android.systemui.dock; */ public class DockManagerFake implements DockManager { DockEventListener mCallback; + AlignmentStateListener mAlignmentListener; @Override public void addListener(DockEventListener callback) { @@ -33,6 +34,16 @@ public class DockManagerFake implements DockManager { } @Override + public void addAlignmentStateListener(AlignmentStateListener listener) { + mAlignmentListener = listener; + } + + @Override + public void removeAlignmentStateListener(AlignmentStateListener listener) { + mAlignmentListener = listener; + } + + @Override public boolean isDocked() { return false; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java index 1e18e51bc079..1eb75aaacbfb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java @@ -46,6 +46,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.wakelock.WakeLockFake; import org.junit.Before; @@ -76,8 +77,8 @@ public class DozeMachineTest extends SysuiTestCase { mConfigMock = mock(AmbientDisplayConfiguration.class); mPartMock = mock(DozeMachine.Part.class); - mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake, mWakefulnessLifecycle); - + mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake, + mWakefulnessLifecycle, mock(BatteryController.class)); mMachine.setParts(new DozeMachine.Part[]{mPartMock}); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java new file mode 100644 index 000000000000..d46d7a271e0a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.screenshot; + +import static android.content.Context.NOTIFICATION_SERVICE; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SystemUIFactory; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.NotificationChannels; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +/** + * Tests for exception handling and bitmap configuration in adding smart actions to Screenshot + * Notification. + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { + private ScreenshotNotificationSmartActionsProvider mSmartActionsProvider; + private Handler mHandler; + + @Before + public void setup() { + mSmartActionsProvider = mock( + ScreenshotNotificationSmartActionsProvider.class); + mHandler = mock(Handler.class); + } + + // Tests any exception thrown in getting smart actions future does not affect regular + // screenshot flow. + @Test + public void testExceptionHandlingInGetSmartActionsFuture() + throws Exception { + Bitmap bitmap = mock(Bitmap.class); + when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE); + ScreenshotNotificationSmartActionsProvider smartActionsProvider = mock( + ScreenshotNotificationSmartActionsProvider.class); + when(smartActionsProvider.getActions(any(), any(), any(), + eq(false))).thenThrow(RuntimeException.class); + CompletableFuture<List<Notification.Action>> smartActionsFuture = + GlobalScreenshot.getSmartActionsFuture("", bitmap, + smartActionsProvider, true, false); + Assert.assertNotNull(smartActionsFuture); + List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); + Assert.assertEquals(Collections.emptyList(), smartActions); + } + + // Tests any exception thrown in waiting for smart actions future to complete does + // not affect regular screenshot flow. + @Test + public void testExceptionHandlingInGetSmartActions() + throws Exception { + CompletableFuture<List<Notification.Action>> smartActionsFuture = mock( + CompletableFuture.class); + int timeoutMs = 1000; + when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow( + RuntimeException.class); + List<Notification.Action> actions = GlobalScreenshot.getSmartActions( + "", smartActionsFuture, timeoutMs, mSmartActionsProvider); + Assert.assertEquals(Collections.emptyList(), actions); + } + + // Tests any exception thrown in notifying feedback does not affect regular screenshot flow. + @Test + public void testExceptionHandlingInNotifyingFeedback() { + doThrow(RuntimeException.class).when(mSmartActionsProvider).notifyOp(any(), any(), any(), + anyLong()); + GlobalScreenshot.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1); + } + + // Tests for a non-hardware bitmap, ScreenshotNotificationSmartActionsProvider is never invoked + // and a completed future is returned. + @Test + public void testUnsupportedBitmapConfiguration() + throws Exception { + Bitmap bitmap = mock(Bitmap.class); + when(bitmap.getConfig()).thenReturn(Bitmap.Config.RGB_565); + CompletableFuture<List<Notification.Action>> smartActionsFuture = + GlobalScreenshot.getSmartActionsFuture("", bitmap, + mSmartActionsProvider, true, true); + verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), + eq(false)); + Assert.assertNotNull(smartActionsFuture); + List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); + Assert.assertEquals(Collections.emptyList(), smartActions); + } + + // Tests for a hardware bitmap, ScreenshotNotificationSmartActionsProvider is invoked once. + @Test + public void testScreenshotNotificationSmartActionsProviderInvokedOnce() { + Bitmap bitmap = mock(Bitmap.class); + when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE); + GlobalScreenshot.getSmartActionsFuture("", bitmap, mSmartActionsProvider, + true, true); + verify(mSmartActionsProvider, times(1)) + .getActions(any(), any(), any(), eq(true)); + } + + // Tests for a hardware bitmap, a completed future is returned. + @Test + public void testSupportedBitmapConfiguration() + throws Exception { + Bitmap bitmap = mock(Bitmap.class); + when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE); + ScreenshotNotificationSmartActionsProvider actionsProvider = + SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider( + mContext, null, mHandler); + CompletableFuture<List<Notification.Action>> smartActionsFuture = + GlobalScreenshot.getSmartActionsFuture("", bitmap, + actionsProvider, + true, true); + Assert.assertNotNull(smartActionsFuture); + List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); + Assert.assertEquals(smartActions.size(), 0); + } + + // Tests for notification action extras. + @Test + public void testNotificationActionExtras() { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + NotificationManager notificationManager = + (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE); + SaveImageInBackgroundData data = new SaveImageInBackgroundData(); + data.context = mContext; + data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); + data.iconSize = 10; + data.finisher = null; + data.previewWidth = 10; + data.previewheight = 10; + SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data, + notificationManager); + Uri uri = Uri.parse("Screenshot_123.png"); + Notification.Builder notificationBuilder = new Notification.Builder(mContext, + NotificationChannels.SCREENSHOTS_HEADSUP); + task.populateNotificationActions(mContext, mContext.getResources(), + uri, + CompletableFuture.completedFuture(Collections.emptyList()), notificationBuilder); + + Notification notification = notificationBuilder.build(); + Assert.assertEquals(notification.actions.length, 3); + boolean isShareFound = false; + boolean isEditFound = false; + boolean isDeleteFound = false; + for (Notification.Action action : notification.actions) { + Intent intent = action.actionIntent.getIntent(); + Intent actionIntent = intent.getParcelableExtra(GlobalScreenshot.EXTRA_ACTION_INTENT); + Assert.assertNotNull(intent); + Bundle bundle = intent.getExtras(); + Assert.assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID)); + Assert.assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED)); + if (uri.toString().equals(bundle.getString(GlobalScreenshot.SCREENSHOT_URI_ID))) { + isDeleteFound = true; + } else if (Intent.ACTION_EDIT.equals(actionIntent.getAction())) { + isEditFound = true; + } else if (Intent.ACTION_CHOOSER.equals(actionIntent.getAction())) { + isShareFound = true; + } + } + + Assert.assertTrue(isEditFound); + Assert.assertTrue(isShareFound); + Assert.assertTrue(isDeleteFound); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index daee55bd3d61..2fe51d35c490 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -35,8 +35,11 @@ import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.content.Context; import android.graphics.Color; +import android.hardware.biometrics.BiometricSourceType; +import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Looper; +import android.os.UserManager; import android.view.View; import android.view.ViewGroup; @@ -47,12 +50,15 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import com.android.systemui.statusbar.phone.LockIcon; import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.UnlockMethodCache; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.util.wakelock.WakeLockFake; @@ -61,6 +67,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -92,6 +99,14 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { private StatusBarStateController mStatusBarStateController; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock + private UserManager mUserManager; + @Mock + private DockManager mDockManager; + @Captor + private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener; private KeyguardIndicationTextView mTextView; private KeyguardIndicationController mController; @@ -105,14 +120,18 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mTextView = new KeyguardIndicationTextView(mContext); mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager); + mContext.addMockSystemService(UserManager.class, mUserManager); mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class)); mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class)); mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name, ORGANIZATION_NAME); + when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true); when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure)) .thenReturn(mDisclosure); when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView); + when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true); mWakeLock = new WakeLockFake(); } @@ -123,7 +142,9 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } mController = new KeyguardIndicationController(mContext, mIndicationArea, mLockIcon, mLockPatternUtils, mWakeLock, mShadeController, mAccessibilityController, - mUnlockMethodCache, mStatusBarStateController, mKeyguardUpdateMonitor); + mUnlockMethodCache, mStatusBarStateController, mKeyguardUpdateMonitor, + mDockManager); + mController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); } @Test @@ -193,6 +214,74 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test + public void createController_addsAlignmentListener() { + createController(); + + verify(mDockManager).addAlignmentStateListener( + any(DockManager.AlignmentStateListener.class)); + } + + @Test + public void onAlignmentStateChanged_showsSlowChargingIndication() { + createController(); + verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture()); + mController.setVisible(true); + + mAlignmentListener.getValue().onAlignmentStateChanged( + DockManager.ALIGN_STATE_POOR); + + assertThat(mTextView.getText()).isEqualTo( + mContext.getResources().getString(R.string.dock_alignment_slow_charging)); + assertThat(mTextView.getCurrentTextColor()).isEqualTo( + Utils.getColorError(mContext).getDefaultColor()); + } + + @Test + public void onAlignmentStateChanged_showsNotChargingIndication() { + createController(); + verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture()); + mController.setVisible(true); + + mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE); + + assertThat(mTextView.getText()).isEqualTo( + mContext.getResources().getString(R.string.dock_alignment_not_charging)); + assertThat(mTextView.getCurrentTextColor()).isEqualTo( + Utils.getColorError(mContext).getDefaultColor()); + } + + @Test + public void onAlignmentStateChanged_whileDozing_showsSlowChargingIndication() { + createController(); + verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture()); + mController.setVisible(true); + mController.setDozing(true); + + mAlignmentListener.getValue().onAlignmentStateChanged( + DockManager.ALIGN_STATE_POOR); + + assertThat(mTextView.getText()).isEqualTo( + mContext.getResources().getString(R.string.dock_alignment_slow_charging)); + assertThat(mTextView.getCurrentTextColor()).isEqualTo( + Utils.getColorError(mContext).getDefaultColor()); + } + + @Test + public void onAlignmentStateChanged_whileDozing_showsNotChargingIndication() { + createController(); + verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture()); + mController.setVisible(true); + mController.setDozing(true); + + mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE); + + assertThat(mTextView.getText()).isEqualTo( + mContext.getResources().getString(R.string.dock_alignment_not_charging)); + assertThat(mTextView.getCurrentTextColor()).isEqualTo( + Utils.getColorError(mContext).getDefaultColor()); + } + + @Test public void transientIndication_holdsWakeLock_whenDozing() { createController(); @@ -245,6 +334,49 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test + public void transientIndication_visibleWhenDozing_unlessSwipeUp_fromHelp() { + createController(); + String message = "A message"; + + mController.setVisible(true); + mController.getKeyguardCallback().onBiometricHelp( + KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message, + BiometricSourceType.FACE); + assertThat(mTextView.getText()).isEqualTo(message); + mController.setDozing(true); + + assertThat(mTextView.getText()).isNotEqualTo(message); + } + + @Test + public void transientIndication_visibleWhenDozing_unlessSwipeUp_fromError() { + createController(); + String message = mContext.getString(R.string.keyguard_unlock); + + mController.setVisible(true); + mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT, + "A message", BiometricSourceType.FACE); + + assertThat(mTextView.getText()).isEqualTo(message); + mController.setDozing(true); + + assertThat(mTextView.getText()).isNotEqualTo(message); + } + + @Test + public void transientIndication_swipeUpToRetry() { + createController(); + String message = mContext.getString(R.string.keyguard_retry); + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true); + + mController.setVisible(true); + mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT, + "A message", BiometricSourceType.FACE); + + verify(mStatusBarKeyguardViewManager).showBouncerMessage(eq(message), any()); + } + + @Test public void lockIcon_click() { createController(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java index a66cf843bbc3..0db1f681a7ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInterruptionStateProviderTest.java @@ -53,6 +53,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.HeadsUpManager; import org.junit.Before; @@ -85,6 +86,8 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { HeadsUpManager mHeadsUpManager; @Mock NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor; + @Mock + BatteryController mBatteryController; private NotificationInterruptionStateProvider mNotifInterruptionStateProvider; @@ -98,7 +101,8 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter, - mStatusBarStateController); + mStatusBarStateController, + mBatteryController); mNotifInterruptionStateProvider.setUpWithPresenter( mPresenter, @@ -573,17 +577,17 @@ public class NotificationInterruptionStateProviderTest extends SysuiTestCase { /** * Testable class overriding constructor. */ - public class TestableNotificationInterruptionStateProvider extends + public static class TestableNotificationInterruptionStateProvider extends NotificationInterruptionStateProvider { TestableNotificationInterruptionStateProvider(Context context, PowerManager powerManager, IDreamManager dreamManager, AmbientDisplayConfiguration ambientDisplayConfiguration, NotificationFilter notificationFilter, - StatusBarStateController statusBarStateController) { + StatusBarStateController statusBarStateController, + BatteryController batteryController) { super(context, powerManager, dreamManager, ambientDisplayConfiguration, - notificationFilter, - statusBarStateController); + notificationFilter, batteryController, statusBarStateController); } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 2ca1b0611cd6..b07ac5ff7ea8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -182,7 +182,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { 0, NotificationManager.IMPORTANCE_DEFAULT, null, null, - null, null, null, true, sentiment, false, -1, false, null, null, false); + null, null, null, true, sentiment, false, -1, false, null, null, false, false); return true; }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class)); } @@ -201,7 +201,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { null, null, null, null, null, true, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, -1, - false, smartActions, null, false); + false, smartActions, null, false, false); return true; }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java index e2d8e5698daf..cf0c7185f040 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java @@ -623,6 +623,7 @@ public class NotificationDataTest extends SysuiTestCase { public static final String OVERRIDE_SMART_ACTIONS = "sa"; public static final String OVERRIDE_SMART_REPLIES = "sr"; public static final String OVERRIDE_BUBBLE = "cb"; + public static final String OVERRIDE_VISUALLY_INTERRUPTIVE = "vi"; public Map<String, Bundle> rankingOverrides = new HashMap<>(); @@ -683,7 +684,9 @@ public class NotificationDataTest extends SysuiTestCase { overrides.containsKey(OVERRIDE_SMART_REPLIES) ? overrides.getCharSequenceArrayList(OVERRIDE_SMART_REPLIES) : currentReplies, - overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble())); + overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble()), + overrides.getBoolean(OVERRIDE_VISUALLY_INTERRUPTIVE, + outRanking.visuallyInterruptive())); } return true; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 178ff22eede2..02215a984203 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -110,6 +110,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.UserSwitcherController; @@ -156,6 +157,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private RemoteInputController mRemoteInputController; @Mock private StatusBarStateControllerImpl mStatusBarStateController; + @Mock private BatteryController mBatteryController; @Mock private DeviceProvisionedController mDeviceProvisionedController; @Mock private StatusBarNotificationPresenter mNotificationPresenter; @Mock @@ -209,7 +211,7 @@ public class StatusBarTest extends SysuiTestCase { mNotificationInterruptionStateProvider = new TestableNotificationInterruptionStateProvider(mContext, mPowerManager, mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter, - mStatusBarStateController); + mStatusBarStateController, mBatteryController); mDependency.injectTestDependency(NotificationInterruptionStateProvider.class, mNotificationInterruptionStateProvider); mDependency.injectMockDependency(NavigationBarController.class); @@ -873,9 +875,10 @@ public class StatusBarTest extends SysuiTestCase { IDreamManager dreamManager, AmbientDisplayConfiguration ambientDisplayConfiguration, NotificationFilter filter, - StatusBarStateController controller) { + StatusBarStateController controller, + BatteryController batteryController) { super(context, powerManager, dreamManager, ambientDisplayConfiguration, filter, - controller); + batteryController, controller); mUseHeadsUp = true; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java index 766ad978f475..7fa094b6b1af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java @@ -39,6 +39,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.LocalBluetoothAdapter; import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfile; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.systemui.SysuiTestCase; @@ -229,4 +230,28 @@ public class BluetoothControllerImplTest extends SysuiTestCase { assertTrue(mBluetoothControllerImpl.isBluetoothConnected()); verify(callback, atLeastOnce()).onBluetoothStateChange(anyBoolean()); } + + @Test + public void testOnActiveDeviceChanged_updatesAudioActive() { + assertFalse(mBluetoothControllerImpl.isBluetoothAudioActive()); + assertFalse(mBluetoothControllerImpl.isBluetoothAudioProfileOnly()); + + CachedBluetoothDevice device = mock(CachedBluetoothDevice.class); + mDevices.add(device); + when(device.isActiveDevice(BluetoothProfile.HEADSET)).thenReturn(true); + + List<LocalBluetoothProfile> profiles = new ArrayList<>(); + LocalBluetoothProfile profile = mock(LocalBluetoothProfile.class); + profiles.add(profile); + when(profile.getProfileId()).thenReturn(BluetoothProfile.HEADSET); + when(device.getProfiles()).thenReturn(profiles); + when(device.isConnectedProfile(profile)).thenReturn(true); + + mBluetoothControllerImpl.onAclConnectionStateChanged(device, + BluetoothProfile.STATE_CONNECTED); + mBluetoothControllerImpl.onActiveDeviceChanged(device, BluetoothProfile.HEADSET); + + assertTrue(mBluetoothControllerImpl.isBluetoothAudioActive()); + assertTrue(mBluetoothControllerImpl.isBluetoothAudioProfileOnly()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index b43527ca2ca6..59270d86844b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.policy; +import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; @@ -39,6 +42,7 @@ import android.net.wifi.WifiManager; import android.os.Handler; import android.provider.Settings; import android.provider.Settings.Global; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -347,7 +351,13 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } public void updateDataConnectionState(int dataState, int dataNetType) { - when(mServiceState.getDataNetworkType()).thenReturn(dataNetType); + NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder() + .setTransportType(TRANSPORT_TYPE_WWAN) + .setDomain(DOMAIN_PS) + .setAccessNetworkTechnology(dataNetType) + .build(); + when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN)) + .thenReturn(fakeRegInfo); mPhoneStateListener.onDataConnectionStateChanged(dataState, dataNetType); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index 99e5a76b3a11..47e0c3c1600d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -1,5 +1,8 @@ package com.android.systemui.statusbar.policy; +import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyInt; @@ -418,7 +421,13 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { // State from NR_5G to NONE NR_STATE_RESTRICTED, showing corresponding icon doReturn(NetworkRegistrationInfo.NR_STATE_RESTRICTED).when(mServiceState).getNrState(); - doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType(); + NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder() + .setTransportType(TRANSPORT_TYPE_WWAN) + .setDomain(DOMAIN_PS) + .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE) + .build(); + doReturn(fakeRegInfo).when(mServiceState) + .getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN); mPhoneStateListener.onDataConnectionStateChanged(TelephonyManager.DATA_CONNECTED, TelephonyManager.NETWORK_TYPE_LTE); @@ -480,8 +489,13 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { verifyDataIndicators(TelephonyIcons.ICON_LTE); - when(mServiceState.getDataNetworkType()) - .thenReturn(TelephonyManager.NETWORK_TYPE_HSPA); + NetworkRegistrationInfo fakeRegInfo = new NetworkRegistrationInfo.Builder() + .setTransportType(TRANSPORT_TYPE_WWAN) + .setDomain(DOMAIN_PS) + .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_HSPA) + .build(); + when(mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN)) + .thenReturn(fakeRegInfo); updateServiceState(); verifyDataIndicators(TelephonyIcons.ICON_H); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java index a843cca498a0..df76f01494f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java @@ -48,4 +48,9 @@ public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCal public boolean isPowerSave() { return false; } + + @Override + public boolean isAodPowerSave() { + return false; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java index cac6bf7ca3f0..6cbd175c1084 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java @@ -57,6 +57,16 @@ public class FakeBluetoothController extends BaseLeakChecker<Callback> implement } @Override + public boolean isBluetoothAudioProfileOnly() { + return false; + } + + @Override + public boolean isBluetoothAudioActive() { + return false; + } + + @Override public String getConnectedDeviceName() { return null; } diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml index 5a71eb23abad..c71d0d7bc543 100644 --- a/packages/Tethering/AndroidManifest.xml +++ b/packages/Tethering/AndroidManifest.xml @@ -36,6 +36,7 @@ <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /> <uses-permission android:name="android.permission.TETHER_PRIVILEGED" /> <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> + <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <application diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml index ca290c637773..6fa1f77bdd38 100644 --- a/packages/Tethering/res/values/config.xml +++ b/packages/Tethering/res/values/config.xml @@ -100,7 +100,7 @@ <!-- If the mobile hotspot feature requires provisioning, a package name and class name can be provided to launch a supported application that provisions the devices. - EntitlementManager will send an inent to Settings with the specified package name and + EntitlementManager will send an intent to Settings with the specified package name and class name in extras to launch provision app. TODO: note what extras here. diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java index 6ac467e39a9d..4306cf0bd5f3 100644 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ b/packages/Tethering/src/android/net/ip/IpServer.java @@ -26,7 +26,6 @@ import static android.net.util.TetheringMessageBase.BASE_IPSERVER; import android.net.INetd; import android.net.INetworkStackStatusCallback; -import android.net.INetworkStatsService; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; @@ -176,7 +175,6 @@ public class IpServer extends StateMachine { private final SharedLog mLog; private final INetd mNetd; - private final INetworkStatsService mStatsService; private final Callback mCallback; private final InterfaceController mInterfaceCtrl; @@ -208,12 +206,10 @@ public class IpServer extends StateMachine { public IpServer( String ifaceName, Looper looper, int interfaceType, SharedLog log, - INetd netd, INetworkStatsService statsService, Callback callback, - boolean usingLegacyDhcp, Dependencies deps) { + INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) { super(ifaceName, looper); mLog = log.forSubComponent(ifaceName); mNetd = netd; - mStatsService = statsService; mCallback = callback; mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog); mIfaceName = ifaceName; @@ -882,12 +878,6 @@ public class IpServer extends StateMachine { // to remove their rules, which generates errors. // Just do the best we can. try { - // About to tear down NAT; gather remaining statistics. - mStatsService.forceUpdate(); - } catch (Exception e) { - mLog.e("Exception in forceUpdate: " + e.toString()); - } - try { mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface); } catch (RemoteException | ServiceSpecificException e) { mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString()); diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java index 4e2a2c1c7af7..1cabc8d0b5b7 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java @@ -27,8 +27,6 @@ import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED; -import static com.android.internal.R.string.config_wifi_tether_enable; - import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -36,7 +34,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Resources; import android.net.util.SharedLog; import android.os.Bundle; import android.os.Handler; @@ -54,6 +51,7 @@ import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.StateMachine; +import com.android.networkstack.tethering.R; import java.io.PrintWriter; @@ -75,9 +73,7 @@ public class EntitlementManager { "com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM"; private static final String EXTRA_SUBID = "subId"; - // {@link ComponentName} of the Service used to run tether provisioning. - private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString( - Resources.getSystem().getString(config_wifi_tether_enable)); + private final ComponentName mSilentProvisioningService; private static final int MS_PER_HOUR = 60 * 60 * 1000; private static final int EVENT_START_PROVISIONING = 0; private static final int EVENT_STOP_PROVISIONING = 1; @@ -122,6 +118,8 @@ public class EntitlementManager { mHandler = new EntitlementHandler(masterHandler.getLooper()); mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM), null, mHandler); + mSilentProvisioningService = ComponentName.unflattenFromString( + mContext.getResources().getString(R.string.config_wifi_tether_enable)); } public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) { @@ -377,7 +375,7 @@ public class EntitlementManager { intent.putExtra(EXTRA_RUN_PROVISION, true); intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); intent.putExtra(EXTRA_SUBID, subId); - intent.setComponent(TETHER_SERVICE); + intent.setComponent(mSilentProvisioningService); // Only admin user can change tethering and SilentTetherProvisioning don't need to // show UI, it is fine to always start setting's background service as system user. mContext.startService(intent); diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java index ce7c2a669f0a..cc36f4a9c516 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java @@ -16,35 +16,40 @@ package com.android.server.connectivity.tethering; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; -import static android.net.TrafficStats.UID_TETHERING; +import static android.net.NetworkStats.UID_TETHERING; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.usage.NetworkStatsManager; import android.content.ContentResolver; -import android.net.ITetheringStatsProvider; import android.net.InetAddresses; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkStats; +import android.net.NetworkStats.Entry; import android.net.RouteInfo; import android.net.netlink.ConntrackMessage; import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkSocket; +import android.net.netstats.provider.AbstractNetworkStatsProvider; +import android.net.netstats.provider.NetworkStatsProviderCallback; import android.net.util.SharedLog; import android.os.Handler; -import android.os.INetworkManagementService; -import android.os.Looper; -import android.os.RemoteException; -import android.os.SystemClock; import android.provider.Settings; import android.system.ErrnoException; import android.system.OsConstants; import android.text.TextUtils; +import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; @@ -73,13 +78,19 @@ public class OffloadController { private static final String ANYIP = "0.0.0.0"; private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); + @VisibleForTesting + enum StatsType { + STATS_PER_IFACE, + STATS_PER_UID, + } + private enum UpdateType { IF_NEEDED, FORCE }; private final Handler mHandler; private final OffloadHardwareInterface mHwInterface; private final ContentResolver mContentResolver; - private final INetworkManagementService mNms; - private final ITetheringStatsProvider mStatsProvider; + private final @NonNull OffloadTetheringStatsProvider mStatsProvider; + private final @Nullable NetworkStatsProviderCallback mStatsProviderCb; private final SharedLog mLog; private final HashMap<String, LinkProperties> mDownstreams; private boolean mConfigInitialized; @@ -109,22 +120,23 @@ public class OffloadController { private int mNatUpdateNetlinkErrors; public OffloadController(Handler h, OffloadHardwareInterface hwi, - ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) { + ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log) { mHandler = h; mHwInterface = hwi; mContentResolver = contentResolver; - mNms = nms; mStatsProvider = new OffloadTetheringStatsProvider(); mLog = log.forSubComponent(TAG); mDownstreams = new HashMap<>(); mExemptPrefixes = new HashSet<>(); mLastLocalPrefixStrs = new HashSet<>(); - + NetworkStatsProviderCallback providerCallback = null; try { - mNms.registerTetheringStatsProvider(mStatsProvider, getClass().getSimpleName()); - } catch (RemoteException e) { - mLog.e("Cannot register offload stats provider: " + e); + providerCallback = nsm.registerNetworkStatsProvider( + getClass().getSimpleName(), mStatsProvider); + } catch (RuntimeException e) { + Log.wtf(TAG, "Cannot register offload stats provider: " + e); } + mStatsProviderCb = providerCallback; } /** Start hardware offload. */ @@ -173,7 +185,7 @@ public class OffloadController { // and we need to synchronize stats and limits between // software and hardware forwarding. updateStatsForAllUpstreams(); - forceTetherStatsPoll(); + mStatsProvider.pushTetherStats(); } @Override @@ -186,7 +198,7 @@ public class OffloadController { // limits set take into account any software tethering // traffic that has been happening in the meantime. updateStatsForAllUpstreams(); - forceTetherStatsPoll(); + mStatsProvider.pushTetherStats(); // [2] (Re)Push all state. computeAndPushLocalPrefixes(UpdateType.FORCE); pushAllDownstreamState(); @@ -204,14 +216,11 @@ public class OffloadController { // the HAL queued the callback. // TODO: rev the HAL so that it provides an interface name. - // Fetch current stats, so that when our notification reaches - // NetworkStatsService and triggers a poll, we will respond with - // current data (which will be above the limit that was reached). - // Note that if we just changed upstream, this is unnecessary but harmless. - // The stats for the previous upstream were already updated on this thread - // just after the upstream was changed, so they are also up-to-date. updateStatsForCurrentUpstream(); - forceTetherStatsPoll(); + mStatsProvider.pushTetherStats(); + // Push stats to service does not cause the service react to it immediately. + // Inform the service about limit reached. + if (mStatsProviderCb != null) mStatsProviderCb.onLimitReached(); } @Override @@ -253,42 +262,37 @@ public class OffloadController { return mConfigInitialized && mControlInitialized; } - private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub { - @Override - public NetworkStats getTetherStats(int how) { - // getTetherStats() is the only function in OffloadController that can be called from - // a different thread. Do not attempt to update stats by querying the offload HAL - // synchronously from a different thread than our Handler thread. http://b/64771555. - Runnable updateStats = () -> { - updateStatsForCurrentUpstream(); - }; - if (Looper.myLooper() == mHandler.getLooper()) { - updateStats.run(); - } else { - mHandler.post(updateStats); - } - - NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0); - NetworkStats.Entry entry = new NetworkStats.Entry(); - entry.set = SET_DEFAULT; - entry.tag = TAG_NONE; - entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL; - - for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) { - ForwardedStats value = kv.getValue(); - entry.iface = kv.getKey(); - entry.rxBytes = value.rxBytes; - entry.txBytes = value.txBytes; - stats.addEntry(entry); + @VisibleForTesting + class OffloadTetheringStatsProvider extends AbstractNetworkStatsProvider { + // These stats must only ever be touched on the handler thread. + @NonNull + private NetworkStats mIfaceStats = new NetworkStats(0L, 0); + @NonNull + private NetworkStats mUidStats = new NetworkStats(0L, 0); + + @VisibleForTesting + @NonNull + NetworkStats getTetherStats(@NonNull StatsType how) { + NetworkStats stats = new NetworkStats(0L, 0); + final int uid = (how == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL; + + for (final Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) { + final ForwardedStats value = kv.getValue(); + final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO, + ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L); + stats = stats.addValues(entry); } return stats; } @Override - public void setInterfaceQuota(String iface, long quotaBytes) { + public void setLimit(String iface, long quotaBytes) { + mLog.i("setLimit: " + iface + "," + quotaBytes); + // Listen for all iface is necessary since upstream might be changed after limit + // is set. mHandler.post(() -> { - if (quotaBytes == ITetheringStatsProvider.QUOTA_UNLIMITED) { + if (quotaBytes == QUOTA_UNLIMITED) { mInterfaceQuotas.remove(iface); } else { mInterfaceQuotas.put(iface, quotaBytes); @@ -296,6 +300,42 @@ public class OffloadController { maybeUpdateDataLimit(iface); }); } + + /** + * Push stats to service, but does not cause a force polling. Note that this can only be + * called on the handler thread. + */ + public void pushTetherStats() { + // TODO: remove the accumulated stats and report the diff from HAL directly. + if (null == mStatsProviderCb) return; + final NetworkStats ifaceDiff = + getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats); + final NetworkStats uidDiff = + getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats); + try { + mStatsProviderCb.onStatsUpdated(0 /* token */, ifaceDiff, uidDiff); + mIfaceStats = mIfaceStats.add(ifaceDiff); + mUidStats = mUidStats.add(uidDiff); + } catch (RuntimeException e) { + mLog.e("Cannot report network stats: ", e); + } + } + + @Override + public void requestStatsUpdate(int token) { + mLog.i("requestStatsUpdate: " + token); + // Do not attempt to update stats by querying the offload HAL + // synchronously from a different thread than the Handler thread. http://b/64771555. + mHandler.post(() -> { + updateStatsForCurrentUpstream(); + pushTetherStats(); + }); + } + + @Override + public void setAlert(long quotaBytes) { + // TODO: Ask offload HAL to notify alert without stopping traffic. + } } private String currentUpstreamInterface() { @@ -353,14 +393,6 @@ public class OffloadController { } } - private void forceTetherStatsPoll() { - try { - mNms.tetherLimitReached(mStatsProvider); - } catch (RemoteException e) { - mLog.e("Cannot report data limit reached: " + e); - } - } - /** Set current tethering upstream LinkProperties. */ public void setUpstreamLinkProperties(LinkProperties lp) { if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return; diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java index 4a8ef1f92754..90b9d3f148dc 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java @@ -29,6 +29,8 @@ import android.os.Handler; import android.os.RemoteException; import android.system.OsConstants; +import com.android.internal.annotations.VisibleForTesting; + import java.util.ArrayList; @@ -91,6 +93,12 @@ public class OffloadHardwareInterface { txBytes = 0; } + @VisibleForTesting + public ForwardedStats(long rxBytes, long txBytes) { + this.rxBytes = rxBytes; + this.txBytes = txBytes; + } + /** Add Tx/Rx bytes. */ public void add(ForwardedStats other) { rxBytes += other.rxBytes; diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 5b97f50f1208..26875b1d51ae 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; +import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; @@ -53,6 +54,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothProfile; @@ -63,9 +65,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.hardware.usb.UsbManager; +import android.net.ConnectivityManager; import android.net.INetd; -import android.net.INetworkPolicyManager; -import android.net.INetworkStatsService; import android.net.ITetheringEventCallback; import android.net.IpPrefix; import android.net.LinkAddress; @@ -176,8 +177,6 @@ public class Tethering { private final Context mContext; private final ArrayMap<String, TetherState> mTetherStates; private final BroadcastReceiver mStateReceiver; - private final INetworkStatsService mStatsService; - private final INetworkPolicyManager mPolicyManager; private final Looper mLooper; private final StateMachine mTetherMasterSM; private final OffloadController mOffloadController; @@ -207,13 +206,12 @@ public class Tethering { private boolean mWifiTetherRequested; private Network mTetherUpstream; private TetherStatesParcel mTetherStatesParcel; + private boolean mDataSaverEnabled = false; public Tethering(TetheringDependencies deps) { mLog.mark("Tethering.constructed"); mDeps = deps; mContext = mDeps.getContext(); - mStatsService = mDeps.getINetworkStatsService(); - mPolicyManager = mDeps.getINetworkPolicyManager(); mNetd = mDeps.getINetd(mContext); mLooper = mDeps.getTetheringLooper(); @@ -224,10 +222,12 @@ public class Tethering { mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps); mTetherMasterSM.start(); + final NetworkStatsManager statsManager = + (NetworkStatsManager) mContext.getSystemService(Context.NETWORK_STATS_SERVICE); mHandler = mTetherMasterSM.getHandler(); mOffloadController = new OffloadController(mHandler, mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(), - mDeps.getINetworkManagementService(), mLog); + statsManager, mLog); mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new HashSet<>(); @@ -264,7 +264,7 @@ public class Tethering { } final UserManager userManager = (UserManager) mContext.getSystemService( - Context.USER_SERVICE); + Context.USER_SERVICE); mTetheringRestriction = new UserRestrictionActionListener(userManager, this); final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(executor); @@ -288,6 +288,7 @@ public class Tethering { filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED); + filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED); mContext.registerReceiver(mStateReceiver, filter, null, handler); } @@ -484,7 +485,7 @@ public class Tethering { } private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) { - final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + final BluetoothAdapter adapter = mDeps.getBluetoothAdapter(); if (adapter == null || !adapter.isEnabled()) { Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " + (adapter == null)); @@ -740,8 +741,7 @@ public class Tethering { .setContentIntent(pi); mLastNotificationId = id; - notificationManager.notify(null, mLastNotificationId, - mTetheredNotificationBuilder.buildInto(new Notification())); + notificationManager.notify(null, mLastNotificationId, mTetheredNotificationBuilder.build()); } @VisibleForTesting @@ -775,6 +775,9 @@ public class Tethering { } else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) { mLog.log("OBSERVED user restrictions changed"); handleUserRestrictionAction(); + } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) { + mLog.log("OBSERVED data saver changed"); + handleDataSaverChanged(); } } @@ -885,6 +888,20 @@ public class Tethering { private void handleUserRestrictionAction() { mTetheringRestriction.onUserRestrictionsChanged(); } + + private void handleDataSaverChanged() { + final ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + final boolean isDataSaverEnabled = connMgr.getRestrictBackgroundStatus() + != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; + + if (mDataSaverEnabled == isDataSaverEnabled) return; + + mDataSaverEnabled = isDataSaverEnabled; + if (mDataSaverEnabled) { + untetherAll(); + } + } } @VisibleForTesting @@ -1982,15 +1999,6 @@ public class Tethering { mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error)); - try { - // Notify that we're tethering (or not) this interface. - // This is how data saver for instance knows if the user explicitly - // turned on tethering (thus keeping us from being in data saver mode). - mPolicyManager.onTetheringChanged(iface, state == IpServer.STATE_TETHERED); - } catch (RemoteException e) { - // Not really very much we can do here. - } - // If TetherMasterSM is in ErrorState, TetherMasterSM stays there. // Thus we give a chance for TetherMasterSM to recover to InitialState // by sending CMD_CLEAR_ERROR @@ -2054,7 +2062,7 @@ public class Tethering { mLog.log("adding TetheringInterfaceStateMachine for: " + iface); final TetherState tetherState = new TetherState( - new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mStatsService, + new IpServer(iface, mLooper, interfaceType, mLog, mNetd, makeControlCallback(), mConfig.enableLegacyDhcpServer, mDeps.getIpServerDependencies())); mTetherStates.put(iface, tetherState); diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java index dbe789288c6f..068c346fbfc1 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -23,18 +23,6 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; -import static com.android.internal.R.array.config_mobile_hotspot_provision_app; -import static com.android.internal.R.array.config_tether_bluetooth_regexs; -import static com.android.internal.R.array.config_tether_dhcp_range; -import static com.android.internal.R.array.config_tether_upstream_types; -import static com.android.internal.R.array.config_tether_usb_regexs; -import static com.android.internal.R.array.config_tether_wifi_p2p_regexs; -import static com.android.internal.R.array.config_tether_wifi_regexs; -import static com.android.internal.R.bool.config_tether_upstream_automatic; -import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period; -import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui; -import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; - import android.content.Context; import android.content.res.Resources; import android.net.TetheringConfigurationParcel; @@ -45,6 +33,7 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; +import com.android.networkstack.tethering.R; import java.io.PrintWriter; import java.util.ArrayList; @@ -113,27 +102,30 @@ public class TetheringConfiguration { activeDataSubId = id; Resources res = getResources(ctx, activeDataSubId); - tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs); + tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs); // TODO: Evaluate deleting this altogether now that Wi-Fi always passes // us an interface name. Careful consideration needs to be given to // implications for Settings and for provisioning checks. - tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs); - tetherableWifiP2pRegexs = getResourceStringArray(res, config_tether_wifi_p2p_regexs); - tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs); + tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs); + tetherableWifiP2pRegexs = getResourceStringArray( + res, R.array.config_tether_wifi_p2p_regexs); + tetherableBluetoothRegexs = getResourceStringArray( + res, R.array.config_tether_bluetooth_regexs); isDunRequired = checkDunRequired(ctx); - chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic); + chooseUpstreamAutomatically = getResourceBoolean( + res, R.bool.config_tether_upstream_automatic); preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired); legacyDhcpRanges = getLegacyDhcpRanges(res); defaultIPv4DNS = copy(DEFAULT_IPV4_DNS); enableLegacyDhcpServer = getEnableLegacyDhcpServer(res); - provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app); + provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app); provisioningAppNoUi = getProvisioningAppNoUi(res); provisioningCheckPeriod = getResourceInteger(res, - config_mobile_hotspot_provision_check_period, + R.integer.config_mobile_hotspot_provision_check_period, 0 /* No periodic re-check */); configLog.log(toString()); @@ -248,7 +240,7 @@ public class TetheringConfiguration { } private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) { - final int[] ifaceTypes = res.getIntArray(config_tether_upstream_types); + final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types); final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); for (int i : ifaceTypes) { switch (i) { @@ -298,7 +290,7 @@ public class TetheringConfiguration { } private static String[] getLegacyDhcpRanges(Resources res) { - final String[] fromResource = getResourceStringArray(res, config_tether_dhcp_range); + final String[] fromResource = getResourceStringArray(res, R.array.config_tether_dhcp_range); if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) { return fromResource; } @@ -307,7 +299,7 @@ public class TetheringConfiguration { private static String getProvisioningAppNoUi(Resources res) { try { - return res.getString(config_mobile_hotspot_provision_app_no_ui); + return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui); } catch (Resources.NotFoundException e) { return ""; } @@ -339,7 +331,7 @@ public class TetheringConfiguration { } private boolean getEnableLegacyDhcpServer(final Resources res) { - return getResourceBoolean(res, config_tether_enable_legacy_dhcp_server) + return getResourceBoolean(res, R.bool.config_tether_enable_legacy_dhcp_server) || getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER); } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java index b16b3294a112..e019c3aca26a 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -16,18 +16,15 @@ package com.android.server.connectivity.tethering; +import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.net.INetd; -import android.net.INetworkPolicyManager; -import android.net.INetworkStatsService; import android.net.NetworkRequest; import android.net.ip.IpServer; import android.net.util.SharedLog; import android.os.Handler; import android.os.IBinder; -import android.os.INetworkManagementService; import android.os.Looper; -import android.os.ServiceManager; import com.android.internal.util.StateMachine; @@ -97,33 +94,6 @@ public abstract class TetheringDependencies { } /** - * Get a reference to INetworkManagementService to registerTetheringStatsProvider from - * OffloadController. Note: This should be removed soon by Usage refactor work in R - * development cycle. - */ - public INetworkManagementService getINetworkManagementService() { - return INetworkManagementService.Stub.asInterface( - ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); - } - - /** - * Get a reference to INetworkStatsService to force update tethering usage. - * Note: This should be removed in R development cycle. - */ - public INetworkStatsService getINetworkStatsService() { - return INetworkStatsService.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); - } - - /** - * Get a reference to INetworkPolicyManager to be used by tethering. - */ - public INetworkPolicyManager getINetworkPolicyManager() { - return INetworkPolicyManager.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); - } - - /** * Get a reference to INetd to be used by tethering. */ public INetd getINetd(Context context) { @@ -140,4 +110,9 @@ public abstract class TetheringDependencies { * Get Context of TetheringSerice. */ public abstract Context getContext(); + + /** + * Get a reference to BluetoothAdapter to be used by tethering. + */ + public abstract BluetoothAdapter getBluetoothAdapter(); } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java index e4e4a090603d..cb7d3920e693 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -24,6 +24,7 @@ import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED; import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR; import android.app.Service; +import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.content.Intent; import android.net.IIntResultListener; @@ -42,7 +43,6 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ResultReceiver; -import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserManager; import android.provider.Settings; @@ -363,7 +363,7 @@ public class TetheringService extends Service { IBinder connector; try { final long before = System.currentTimeMillis(); - while ((connector = ServiceManager.getService( + while ((connector = (IBinder) mContext.getSystemService( Context.NETWORK_STACK_SERVICE)) == null) { if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); @@ -377,6 +377,11 @@ public class TetheringService extends Service { } return INetworkStackConnector.Stub.asInterface(connector); } + + @Override + public BluetoothAdapter getBluetoothAdapter() { + return BluetoothAdapter.getDefaultAdapter(); + } }; } return mDeps; diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 65a0ac13a84b..1f50b6bf7fb3 100644 --- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -52,7 +52,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.net.INetd; -import android.net.INetworkStatsService; import android.net.InterfaceConfigurationParcel; import android.net.IpPrefix; import android.net.LinkAddress; @@ -99,7 +98,6 @@ public class IpServerTest { private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000; @Mock private INetd mNetd; - @Mock private INetworkStatsService mStatsService; @Mock private IpServer.Callback mCallback; @Mock private SharedLog mSharedLog; @Mock private IDhcpServer mDhcpServer; @@ -139,13 +137,13 @@ public class IpServerTest { mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH; } mIpServer = new IpServer( - IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mStatsService, + IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mCallback, usingLegacyDhcp, mDependencies); mIpServer.start(); // Starting the state machine always puts us in a consistent state and notifies // the rest of the world that we've changed from an unknown to available state. mLooper.dispatchAll(); - reset(mNetd, mStatsService, mCallback); + reset(mNetd, mCallback); when(mRaDaemon.start()).thenReturn(true); } @@ -162,7 +160,7 @@ public class IpServerTest { if (upstreamIface != null) { dispatchTetherConnectionChanged(upstreamIface); } - reset(mNetd, mStatsService, mCallback); + reset(mNetd, mCallback); } @Before public void setUp() throws Exception { @@ -173,13 +171,13 @@ public class IpServerTest { @Test public void startsOutAvailable() { mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, - mNetd, mStatsService, mCallback, false /* usingLegacyDhcp */, mDependencies); + mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies); mIpServer.start(); mLooper.dispatchAll(); verify(mCallback).updateInterfaceState( mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mCallback, mNetd, mStatsService); + verifyNoMoreInteractions(mCallback, mNetd); } @Test @@ -198,7 +196,7 @@ public class IpServerTest { // None of these commands should trigger us to request action from // the rest of the system. dispatchCommand(command); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } } @@ -210,7 +208,7 @@ public class IpServerTest { verify(mCallback).updateInterfaceState( mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -228,7 +226,7 @@ public class IpServerTest { mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -236,7 +234,7 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, null); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNetd, mStatsService, mCallback); + InOrder inOrder = inOrder(mNetd, mCallback); inOrder.verify(mNetd).tetherApplyDnsInterfaces(); inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME); inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME); @@ -245,7 +243,7 @@ public class IpServerTest { mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -265,7 +263,7 @@ public class IpServerTest { inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -285,7 +283,7 @@ public class IpServerTest { inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -298,7 +296,7 @@ public class IpServerTest { InOrder inOrder = inOrder(mNetd); inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -306,13 +304,12 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd, mStatsService); - inOrder.verify(mStatsService).forceUpdate(); + InOrder inOrder = inOrder(mNetd); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -322,12 +319,10 @@ public class IpServerTest { doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd, mStatsService); - inOrder.verify(mStatsService).forceUpdate(); + InOrder inOrder = inOrder(mNetd); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mStatsService).forceUpdate(); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); } @@ -340,13 +335,11 @@ public class IpServerTest { IFACE_NAME, UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); - InOrder inOrder = inOrder(mNetd, mStatsService); - inOrder.verify(mStatsService).forceUpdate(); + InOrder inOrder = inOrder(mNetd); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); - inOrder.verify(mStatsService).forceUpdate(); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2); } @@ -356,8 +349,7 @@ public class IpServerTest { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNetd, mStatsService, mCallback); - inOrder.verify(mStatsService).forceUpdate(); + InOrder inOrder = inOrder(mNetd, mCallback); inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNetd).tetherApplyDnsInterfaces(); @@ -368,7 +360,7 @@ public class IpServerTest { mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); inOrder.verify(mCallback).updateLinkProperties( eq(mIpServer), any(LinkProperties.class)); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } @Test @@ -435,11 +427,11 @@ public class IpServerTest { public void ignoresDuplicateUpstreamNotifications() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); for (int i = 0; i < 5; i++) { dispatchTetherConnectionChanged(UPSTREAM_IFACE); - verifyNoMoreInteractions(mNetd, mStatsService, mCallback); + verifyNoMoreInteractions(mNetd, mCallback); } } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index 79bba7f6e663..4f0746199786 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -27,7 +27,6 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -56,10 +55,10 @@ import android.telephony.CarrierConfigManager; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.R; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; +import com.android.networkstack.tethering.R; import org.junit.After; import org.junit.Before; @@ -157,16 +156,18 @@ public final class EntitlementManagerTest { eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); when(mResources.getStringArray(R.array.config_tether_dhcp_range)) - .thenReturn(new String[0]); + .thenReturn(new String[0]); when(mResources.getStringArray(R.array.config_tether_usb_regexs)) - .thenReturn(new String[0]); + .thenReturn(new String[0]); when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) - .thenReturn(new String[0]); + .thenReturn(new String[0]); when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) - .thenReturn(new String[0]); + .thenReturn(new String[0]); when(mResources.getIntArray(R.array.config_tether_upstream_types)) - .thenReturn(new int[0]); - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + .thenReturn(new int[0]); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + false); + when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); when(mLog.forSubComponent(anyString())).thenReturn(mLog); mMockContext = new MockContext(mContext); diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java index 7886ca6c132d..7e62e5aca993 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -16,21 +16,26 @@ package com.android.server.connectivity.tethering; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.SET_DEFAULT; -import static android.net.NetworkStats.STATS_PER_IFACE; -import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; +import static android.net.NetworkStats.UID_TETHERING; import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.TrafficStats.UID_TETHERING; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; +import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_IFACE; +import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_UID; import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; import static com.android.testutils.MiscAssertsKt.assertContainsAll; import static com.android.testutils.MiscAssertsKt.assertThrows; +import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyObject; @@ -39,11 +44,14 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.annotation.NonNull; +import android.app.usage.NetworkStatsManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.net.ITetheringStatsProvider; @@ -51,10 +59,12 @@ import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkStats; +import android.net.NetworkStats.Entry; import android.net.RouteInfo; +import android.net.netstats.provider.AbstractNetworkStatsProvider; +import android.net.netstats.provider.NetworkStatsProviderCallback; import android.net.util.SharedLog; import android.os.Handler; -import android.os.INetworkManagementService; import android.os.Looper; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; @@ -97,11 +107,13 @@ public class OffloadControllerTest { @Mock private OffloadHardwareInterface mHardware; @Mock private ApplicationInfo mApplicationInfo; @Mock private Context mContext; - @Mock private INetworkManagementService mNMService; + @Mock private NetworkStatsManager mStatsManager; + @Mock private NetworkStatsProviderCallback mTetherStatsProviderCb; private final ArgumentCaptor<ArrayList> mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class); - private final ArgumentCaptor<ITetheringStatsProvider.Stub> mTetherStatsProviderCaptor = - ArgumentCaptor.forClass(ITetheringStatsProvider.Stub.class); + private final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider> + mTetherStatsProviderCaptor = + ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class); private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor = ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); private MockContentResolver mContentResolver; @@ -114,6 +126,8 @@ public class OffloadControllerTest { mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); when(mContext.getContentResolver()).thenReturn(mContentResolver); FakeSettingsProvider.clearSettingsProvider(); + when(mStatsManager.registerNetworkStatsProvider(anyString(), any())) + .thenReturn(mTetherStatsProviderCb); } @After public void tearDown() throws Exception { @@ -139,9 +153,9 @@ public class OffloadControllerTest { private OffloadController makeOffloadController() throws Exception { OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()), - mHardware, mContentResolver, mNMService, new SharedLog("test")); - verify(mNMService).registerTetheringStatsProvider( - mTetherStatsProviderCaptor.capture(), anyString()); + mHardware, mContentResolver, mStatsManager, new SharedLog("test")); + verify(mStatsManager).registerNetworkStatsProvider(anyString(), + mTetherStatsProviderCaptor.capture()); return offload; } @@ -384,12 +398,11 @@ public class OffloadControllerTest { inOrder.verifyNoMoreInteractions(); } - private void assertNetworkStats(String iface, ForwardedStats stats, NetworkStats.Entry entry) { - assertEquals(iface, entry.iface); - assertEquals(stats.rxBytes, entry.rxBytes); - assertEquals(stats.txBytes, entry.txBytes); - assertEquals(SET_DEFAULT, entry.set); - assertEquals(TAG_NONE, entry.tag); + private static @NonNull Entry buildTestEntry(@NonNull OffloadController.StatsType how, + @NonNull String iface, long rxBytes, long txBytes) { + return new Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, SET_DEFAULT, + TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, 0L, + txBytes, 0L, 0L); } @Test @@ -400,19 +413,16 @@ public class OffloadControllerTest { final OffloadController offload = makeOffloadController(); offload.start(); + final OffloadController.OffloadTetheringStatsProvider provider = + mTetherStatsProviderCaptor.getValue(); + final String ethernetIface = "eth1"; final String mobileIface = "rmnet_data0"; - ForwardedStats ethernetStats = new ForwardedStats(); - ethernetStats.rxBytes = 12345; - ethernetStats.txBytes = 54321; - - ForwardedStats mobileStats = new ForwardedStats(); - mobileStats.rxBytes = 999; - mobileStats.txBytes = 99999; - - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats); - when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(mobileStats); + when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( + new ForwardedStats(12345, 54321)); + when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn( + new ForwardedStats(999, 99999)); InOrder inOrder = inOrder(mHardware); @@ -432,10 +442,35 @@ public class OffloadControllerTest { // Expect that we fetch stats from the previous upstream. inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface)); - ethernetStats = new ForwardedStats(); - ethernetStats.rxBytes = 100000; - ethernetStats.txBytes = 100000; - when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats); + // Verify that the fetched stats are stored. + final NetworkStats ifaceStats = provider.getTetherStats(STATS_PER_IFACE); + final NetworkStats uidStats = provider.getTetherStats(STATS_PER_UID); + final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) + .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321)); + + final NetworkStats expectedUidStats = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) + .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321)); + + assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats)); + assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats)); + + final ArgumentCaptor<NetworkStats> ifaceStatsCaptor = ArgumentCaptor.forClass( + NetworkStats.class); + final ArgumentCaptor<NetworkStats> uidStatsCaptor = ArgumentCaptor.forClass( + NetworkStats.class); + + // Force pushing stats update to verify the stats reported. + provider.pushTetherStats(); + verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), + ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); + assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue())); + assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue())); + + + when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn( + new ForwardedStats(100000, 100000)); offload.setUpstreamLinkProperties(null); // Expect that we first clear the HAL's upstream parameters. inOrder.verify(mHardware, times(1)).setUpstreamParameters( @@ -443,37 +478,38 @@ public class OffloadControllerTest { // Expect that we fetch stats from the previous upstream. inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface)); - ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue(); - NetworkStats stats = provider.getTetherStats(STATS_PER_IFACE); - NetworkStats perUidStats = provider.getTetherStats(STATS_PER_UID); - waitForIdle(); // There is no current upstream, so no stats are fetched. inOrder.verify(mHardware, never()).getForwardedStats(any()); inOrder.verifyNoMoreInteractions(); - assertEquals(2, stats.size()); - assertEquals(2, perUidStats.size()); - - NetworkStats.Entry entry = null; - for (int i = 0; i < stats.size(); i++) { - assertEquals(UID_ALL, stats.getValues(i, entry).uid); - assertEquals(UID_TETHERING, perUidStats.getValues(i, entry).uid); - } - - int ethernetPosition = ethernetIface.equals(stats.getValues(0, entry).iface) ? 0 : 1; - int mobilePosition = 1 - ethernetPosition; - - entry = stats.getValues(mobilePosition, entry); - assertNetworkStats(mobileIface, mobileStats, entry); - entry = perUidStats.getValues(mobilePosition, entry); - assertNetworkStats(mobileIface, mobileStats, entry); - - ethernetStats.rxBytes = 12345 + 100000; - ethernetStats.txBytes = 54321 + 100000; - entry = stats.getValues(ethernetPosition, entry); - assertNetworkStats(ethernetIface, ethernetStats, entry); - entry = perUidStats.getValues(ethernetPosition, entry); - assertNetworkStats(ethernetIface, ethernetStats, entry); + // Verify that the stored stats is accumulated. + final NetworkStats ifaceStatsAccu = provider.getTetherStats(STATS_PER_IFACE); + final NetworkStats uidStatsAccu = provider.getTetherStats(STATS_PER_UID); + final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999)) + .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321)); + + final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999)) + .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321)); + + assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu)); + assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu)); + + // Verify that only diff of stats is reported. + reset(mTetherStatsProviderCb); + provider.pushTetherStats(); + final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0)) + .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000)); + + final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2) + .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0)) + .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000)); + verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), + ifaceStatsCaptor.capture(), uidStatsCaptor.capture()); + assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue())); + assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue())); } @Test @@ -493,19 +529,19 @@ public class OffloadControllerTest { lp.setInterfaceName(ethernetIface); offload.setUpstreamLinkProperties(lp); - ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue(); + AbstractNetworkStatsProvider provider = mTetherStatsProviderCaptor.getValue(); final InOrder inOrder = inOrder(mHardware); when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true); when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); // Applying an interface quota to the current upstream immediately sends it to the hardware. - provider.setInterfaceQuota(ethernetIface, ethernetLimit); + provider.setLimit(ethernetIface, ethernetLimit); waitForIdle(); inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit); inOrder.verifyNoMoreInteractions(); // Applying an interface quota to another upstream does not take any immediate action. - provider.setInterfaceQuota(mobileIface, mobileLimit); + provider.setLimit(mobileIface, mobileLimit); waitForIdle(); inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); @@ -518,7 +554,7 @@ public class OffloadControllerTest { // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set // to Long.MAX_VALUE. - provider.setInterfaceQuota(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED); + provider.setLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED); waitForIdle(); inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE); @@ -526,7 +562,7 @@ public class OffloadControllerTest { when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false); lp.setInterfaceName(ethernetIface); offload.setUpstreamLinkProperties(lp); - provider.setInterfaceQuota(mobileIface, mobileLimit); + provider.setLimit(mobileIface, mobileLimit); waitForIdle(); inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong()); @@ -535,7 +571,7 @@ public class OffloadControllerTest { when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false); lp.setInterfaceName(mobileIface); offload.setUpstreamLinkProperties(lp); - provider.setInterfaceQuota(mobileIface, mobileLimit); + provider.setLimit(mobileIface, mobileLimit); waitForIdle(); inOrder.verify(mHardware).getForwardedStats(ethernetIface); inOrder.verify(mHardware).stopOffloadControl(); @@ -551,7 +587,7 @@ public class OffloadControllerTest { OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); callback.onStoppedLimitReached(); - verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue()); + verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any()); } @Test @@ -654,9 +690,10 @@ public class OffloadControllerTest { // Verify forwarded stats behaviour. verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); + // TODO: verify the exact stats reported. + verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any()); + verifyNoMoreInteractions(mTetherStatsProviderCb); verifyNoMoreInteractions(mHardware); - verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue()); - verifyNoMoreInteractions(mNMService); } @Test @@ -719,8 +756,8 @@ public class OffloadControllerTest { // Verify forwarded stats behaviour. verify(mHardware, times(1)).getForwardedStats(eq(RMNET0)); verify(mHardware, times(1)).getForwardedStats(eq(WLAN0)); - verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue()); - verifyNoMoreInteractions(mNMService); + verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any()); + verifyNoMoreInteractions(mTetherStatsProviderCb); // TODO: verify local prefixes and downstreams are also pushed to the HAL. verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java index ef97ad418245..3635964dd6a6 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java @@ -26,13 +26,6 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.internal.R.array.config_mobile_hotspot_provision_app; -import static com.android.internal.R.array.config_tether_bluetooth_regexs; -import static com.android.internal.R.array.config_tether_dhcp_range; -import static com.android.internal.R.array.config_tether_upstream_types; -import static com.android.internal.R.array.config_tether_usb_regexs; -import static com.android.internal.R.array.config_tether_wifi_regexs; -import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -51,6 +44,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.BroadcastInterceptingContext; +import com.android.networkstack.tethering.R; import org.junit.After; import org.junit.Before; @@ -120,15 +114,18 @@ public class TetheringConfigurationTest { () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); - when(mResources.getStringArray(config_tether_dhcp_range)).thenReturn(new String[0]); - when(mResources.getStringArray(config_tether_usb_regexs)).thenReturn(new String[0]); - when(mResources.getStringArray(config_tether_wifi_regexs)) + when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn( + new String[0]); + when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) .thenReturn(new String[]{ "test_wlan\\d" }); - when(mResources.getStringArray(config_tether_bluetooth_regexs)).thenReturn(new String[0]); - when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]); - when(mResources.getStringArray(config_mobile_hotspot_provision_app)) + when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn( + new String[0]); + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); + when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app)) .thenReturn(new String[0]); - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + false); mHasTelephonyManager = true; mMockContext = new MockContext(mContext); mEnableLegacyDhcpServer = false; @@ -140,7 +137,7 @@ public class TetheringConfigurationTest { } private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) { - when(mResources.getIntArray(config_tether_upstream_types)).thenReturn( + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn( legacyTetherUpstreamTypes); return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); } @@ -224,7 +221,7 @@ public class TetheringConfigurationTest { @Test public void testNoDefinedUpstreamTypesAddsEthernet() { - when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{}); + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[]{}); when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); final TetheringConfiguration cfg = new TetheringConfiguration( @@ -246,7 +243,7 @@ public class TetheringConfigurationTest { @Test public void testDefinedUpstreamTypesSansEthernetAddsEthernet() { - when(mResources.getIntArray(config_tether_upstream_types)).thenReturn( + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn( new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI}); when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); @@ -264,7 +261,7 @@ public class TetheringConfigurationTest { @Test public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() { - when(mResources.getIntArray(config_tether_upstream_types)) + when(mResources.getIntArray(R.array.config_tether_upstream_types)) .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI}); when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false); @@ -282,7 +279,8 @@ public class TetheringConfigurationTest { @Test public void testNewDhcpServerDisabled() { - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + true); doReturn(false).when( () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); @@ -291,7 +289,8 @@ public class TetheringConfigurationTest { new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertTrue(enableByRes.enableLegacyDhcpServer); - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + false); doReturn(true).when( () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); @@ -303,7 +302,8 @@ public class TetheringConfigurationTest { @Test public void testNewDhcpServerEnabled() { - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + false); doReturn(false).when( () -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY), eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean())); @@ -329,16 +329,17 @@ public class TetheringConfigurationTest { private void setUpResourceForSubId() { when(mResourcesForSubId.getStringArray( - config_tether_dhcp_range)).thenReturn(new String[0]); + R.array.config_tether_dhcp_range)).thenReturn(new String[0]); when(mResourcesForSubId.getStringArray( - config_tether_usb_regexs)).thenReturn(new String[0]); + R.array.config_tether_usb_regexs)).thenReturn(new String[0]); when(mResourcesForSubId.getStringArray( - config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" }); + R.array.config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" }); when(mResourcesForSubId.getStringArray( - config_tether_bluetooth_regexs)).thenReturn(new String[0]); - when(mResourcesForSubId.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]); + R.array.config_tether_bluetooth_regexs)).thenReturn(new String[0]); + when(mResourcesForSubId.getIntArray(R.array.config_tether_upstream_types)).thenReturn( + new int[0]); when(mResourcesForSubId.getStringArray( - config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); + R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME); } } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 0df32fd9f35b..857e4deaf919 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -19,6 +19,9 @@ package com.android.server.connectivity.tethering; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; +import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; +import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; +import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; import static android.net.RouteInfo.RTN_UNICAST; import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; @@ -37,8 +40,6 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; -import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server; - import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -53,6 +54,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; @@ -60,6 +62,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.app.usage.NetworkStatsManager; +import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -68,9 +72,8 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.res.Resources; import android.hardware.usb.UsbManager; +import android.net.ConnectivityManager; import android.net.INetd; -import android.net.INetworkPolicyManager; -import android.net.INetworkStatsService; import android.net.ITetheringEventCallback; import android.net.InetAddresses; import android.net.InterfaceConfigurationParcel; @@ -99,7 +102,6 @@ import android.net.wifi.p2p.WifiP2pInfo; import android.net.wifi.p2p.WifiP2pManager; import android.os.Bundle; import android.os.Handler; -import android.os.INetworkManagementService; import android.os.Looper; import android.os.PersistableBundle; import android.os.RemoteException; @@ -120,6 +122,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; +import com.android.networkstack.tethering.R; import org.junit.After; import org.junit.Before; @@ -133,6 +136,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Vector; @RunWith(AndroidJUnit4.class) @@ -151,9 +155,7 @@ public class TetheringTest { @Mock private ApplicationInfo mApplicationInfo; @Mock private Context mContext; - @Mock private INetworkManagementService mNMService; - @Mock private INetworkStatsService mStatsService; - @Mock private INetworkPolicyManager mPolicyManager; + @Mock private NetworkStatsManager mStatsManager; @Mock private OffloadHardwareInterface mOffloadHardwareInterface; @Mock private Resources mResources; @Mock private TelephonyManager mTelephonyManager; @@ -167,6 +169,7 @@ public class TetheringTest { @Mock private INetd mNetd; @Mock private UserManager mUserManager; @Mock private NetworkRequest mNetworkRequest; + @Mock private ConnectivityManager mCm; private final MockIpServerDependencies mIpServerDependencies = spy(new MockIpServerDependencies()); @@ -217,6 +220,8 @@ public class TetheringTest { if (Context.USB_SERVICE.equals(name)) return mUsbManager; if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager; if (Context.USER_SERVICE.equals(name)) return mUserManager; + if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager; + if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm; return super.getSystemService(name); } @@ -334,21 +339,6 @@ public class TetheringTest { } @Override - public INetworkManagementService getINetworkManagementService() { - return mNMService; - } - - @Override - public INetworkStatsService getINetworkStatsService() { - return mStatsService; - } - - @Override - public INetworkPolicyManager getINetworkPolicyManager() { - return mPolicyManager; - } - - @Override public INetd getINetd(Context context) { return mNetd; } @@ -362,6 +352,12 @@ public class TetheringTest { public Context getContext() { return mServiceContext; } + + @Override + public BluetoothAdapter getBluetoothAdapter() { + // TODO: add test for bluetooth tethering. + return null; + } } private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4, @@ -420,24 +416,24 @@ public class TetheringTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range)) + when(mResources.getStringArray(R.array.config_tether_dhcp_range)) .thenReturn(new String[0]); - when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs)) + when(mResources.getStringArray(R.array.config_tether_usb_regexs)) .thenReturn(new String[] { "test_rndis\\d" }); - when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs)) + when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) .thenReturn(new String[]{ "test_wlan\\d" }); - when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs)) + when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); - when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs)) + when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) .thenReturn(new String[0]); - when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types)) - .thenReturn(new int[0]); - when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic)) - .thenReturn(false); - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false); + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); + when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + false); when(mNetd.interfaceGetList()) .thenReturn(new String[] { TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME}); + when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); mInterfaceConfiguration = new InterfaceConfigurationParcel(); mInterfaceConfiguration.flags = new String[0]; when(mRouterAdvertisementDaemon.start()) @@ -457,7 +453,7 @@ public class TetheringTest { mServiceContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_TETHER_STATE_CHANGED)); mTethering = makeTethering(); - verify(mNMService).registerTetheringStatsProvider(any(), anyString()); + verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any()); verify(mNetd).registerUnsolicitedEventListener(any()); final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor = ArgumentCaptor.forClass(PhoneStateListener.class); @@ -501,13 +497,15 @@ public class TetheringTest { p2pInfo.groupFormed = isGroupFormed; p2pInfo.isGroupOwner = isGroupOwner; - WifiP2pGroup group = new WifiP2pGroup(); - group.setIsGroupOwner(isGroupOwner); - group.setInterface(ifname); + WifiP2pGroup group = mock(WifiP2pGroup.class); + when(group.isGroupOwner()).thenReturn(isGroupOwner); + when(group.getInterface()).thenReturn(ifname); + + final Intent intent = mock(Intent.class); + when(intent.getAction()).thenReturn(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); + when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO)).thenReturn(p2pInfo); + when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)).thenReturn(group); - final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); - intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, p2pInfo); - intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, group); mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL, P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST); } @@ -698,7 +696,8 @@ public class TetheringTest { @Test public void workingMobileUsbTethering_IPv4LegacyDhcp() { - when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + true); sendConfigurationChanged(); final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); runUsbTethering(upstreamState); @@ -786,8 +785,7 @@ public class TetheringTest { @Test public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception { - when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic)) - .thenReturn(true); + when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true); sendConfigurationChanged(); // Setup IPv6 @@ -1315,7 +1313,7 @@ public class TetheringTest { private void workingWifiP2pGroupOwnerLegacyMode( boolean emulateInterfaceStatusChanged) throws Exception { // change to legacy mode and update tethering information by chaning SIM - when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs)) + when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) .thenReturn(new String[]{}); final int fakeSubId = 1234; mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); @@ -1353,6 +1351,50 @@ public class TetheringTest { workingWifiP2pGroupClient(false); } + private void setDataSaverEnabled(boolean enabled) { + final Intent intent = new Intent(ACTION_RESTRICT_BACKGROUND_CHANGED); + mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL); + + final int status = enabled ? RESTRICT_BACKGROUND_STATUS_ENABLED + : RESTRICT_BACKGROUND_STATUS_DISABLED; + when(mCm.getRestrictBackgroundStatus()).thenReturn(status); + mLooper.dispatchAll(); + } + + @Test + public void testDataSaverChanged() { + // Start Tethering. + final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); + runUsbTethering(upstreamState); + assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); + // Data saver is ON. + setDataSaverEnabled(true); + // Verify that tethering should be disabled. + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); + mTethering.interfaceRemoved(TEST_USB_IFNAME); + mLooper.dispatchAll(); + assertEquals(mTethering.getTetheredIfaces(), new String[0]); + reset(mUsbManager); + + runUsbTethering(upstreamState); + // Verify that user can start tethering again without turning OFF data saver. + assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); + + // If data saver is keep ON with change event, tethering should not be OFF this time. + setDataSaverEnabled(true); + verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE); + assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); + + // If data saver is turned OFF, it should not change tethering. + setDataSaverEnabled(false); + verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE); + assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME); + } + + private static <T> void assertContains(Collection<T> collection, T element) { + assertTrue(element + " not found in " + collection, collection.contains(element)); + } + // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. } diff --git a/rs/java/android/renderscript/BaseObj.java b/rs/java/android/renderscript/BaseObj.java index b7e05d9c984c..7b5514b8a0d1 100644 --- a/rs/java/android/renderscript/BaseObj.java +++ b/rs/java/android/renderscript/BaseObj.java @@ -16,8 +16,10 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import dalvik.system.CloseGuard; + import java.util.concurrent.locks.ReentrantReadWriteLock; /** diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java index b8eb3a1d7a40..0941907d35f8 100644 --- a/rs/java/android/renderscript/Element.java +++ b/rs/java/android/renderscript/Element.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * <p>An Element represents one item within an {@link diff --git a/rs/java/android/renderscript/FileA3D.java b/rs/java/android/renderscript/FileA3D.java index 9a6b0bcd4544..7cc2825ae565 100644 --- a/rs/java/android/renderscript/FileA3D.java +++ b/rs/java/android/renderscript/FileA3D.java @@ -16,13 +16,13 @@ package android.renderscript; -import java.io.File; -import java.io.InputStream; - -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; import android.content.res.Resources; +import java.io.File; +import java.io.InputStream; + /** * @hide * @deprecated in API 16 diff --git a/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java index 583350e91795..df9d8019f28d 100644 --- a/rs/java/android/renderscript/Font.java +++ b/rs/java/android/renderscript/Font.java @@ -16,17 +16,16 @@ package android.renderscript; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.os.Environment; + import java.io.File; import java.io.InputStream; import java.util.HashMap; import java.util.Map; -import android.os.Environment; - -import android.annotation.UnsupportedAppUsage; -import android.content.res.AssetManager; -import android.content.res.Resources; - /** * @hide * @deprecated in API 16 diff --git a/rs/java/android/renderscript/Matrix4f.java b/rs/java/android/renderscript/Matrix4f.java index 026c9fbd7d5e..a9469c979494 100644 --- a/rs/java/android/renderscript/Matrix4f.java +++ b/rs/java/android/renderscript/Matrix4f.java @@ -16,8 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; -import java.lang.Math; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/Mesh.java b/rs/java/android/renderscript/Mesh.java index 5321dcb957dc..826225a70d86 100644 --- a/rs/java/android/renderscript/Mesh.java +++ b/rs/java/android/renderscript/Mesh.java @@ -16,7 +16,8 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import java.util.Vector; /** diff --git a/rs/java/android/renderscript/Program.java b/rs/java/android/renderscript/Program.java index e28d646f5f1c..ff072183e927 100644 --- a/rs/java/android/renderscript/Program.java +++ b/rs/java/android/renderscript/Program.java @@ -17,14 +17,14 @@ package android.renderscript; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.res.Resources; +import android.util.Log; + import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; -import android.annotation.UnsupportedAppUsage; -import android.content.res.Resources; -import android.util.Log; - /** * @hide diff --git a/rs/java/android/renderscript/ProgramFragment.java b/rs/java/android/renderscript/ProgramFragment.java index 3dde9b6d6400..880531207b4d 100644 --- a/rs/java/android/renderscript/ProgramFragment.java +++ b/rs/java/android/renderscript/ProgramFragment.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/ProgramFragmentFixedFunction.java b/rs/java/android/renderscript/ProgramFragmentFixedFunction.java index d05d41da8b6f..c741ce6e77ed 100644 --- a/rs/java/android/renderscript/ProgramFragmentFixedFunction.java +++ b/rs/java/android/renderscript/ProgramFragmentFixedFunction.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/ProgramRaster.java b/rs/java/android/renderscript/ProgramRaster.java index 33000acb4eb0..a21696c82161 100644 --- a/rs/java/android/renderscript/ProgramRaster.java +++ b/rs/java/android/renderscript/ProgramRaster.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/ProgramStore.java b/rs/java/android/renderscript/ProgramStore.java index 622fe21be47a..7e61347ee218 100644 --- a/rs/java/android/renderscript/ProgramStore.java +++ b/rs/java/android/renderscript/ProgramStore.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/ProgramVertex.java b/rs/java/android/renderscript/ProgramVertex.java index 83d9ea7be645..9257234de42c 100644 --- a/rs/java/android/renderscript/ProgramVertex.java +++ b/rs/java/android/renderscript/ProgramVertex.java @@ -38,7 +38,7 @@ **/ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/ProgramVertexFixedFunction.java b/rs/java/android/renderscript/ProgramVertexFixedFunction.java index 579d3bb507e8..03c2eaf91242 100644 --- a/rs/java/android/renderscript/ProgramVertexFixedFunction.java +++ b/rs/java/android/renderscript/ProgramVertexFixedFunction.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** diff --git a/rs/java/android/renderscript/RSSurfaceView.java b/rs/java/android/renderscript/RSSurfaceView.java index 561373cef625..6bdde387b334 100644 --- a/rs/java/android/renderscript/RSSurfaceView.java +++ b/rs/java/android/renderscript/RSSurfaceView.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.util.AttributeSet; import android.view.SurfaceHolder; diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index f4c27771c846..46c49e5a5e11 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.AssetManager; import android.graphics.Bitmap; diff --git a/rs/java/android/renderscript/RenderScriptCacheDir.java b/rs/java/android/renderscript/RenderScriptCacheDir.java index 1797bef4be8d..862d032d6987 100644 --- a/rs/java/android/renderscript/RenderScriptCacheDir.java +++ b/rs/java/android/renderscript/RenderScriptCacheDir.java @@ -16,7 +16,8 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; + import java.io.File; /** diff --git a/rs/java/android/renderscript/RenderScriptGL.java b/rs/java/android/renderscript/RenderScriptGL.java index 6fac83e8c4a8..dafaf367364d 100644 --- a/rs/java/android/renderscript/RenderScriptGL.java +++ b/rs/java/android/renderscript/RenderScriptGL.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.SurfaceTexture; import android.view.Surface; diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java index 9ad9aea9d7aa..d1d3a7642382 100644 --- a/rs/java/android/renderscript/Script.java +++ b/rs/java/android/renderscript/Script.java @@ -16,7 +16,7 @@ package android.renderscript; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.util.SparseArray; /** diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index d4fb9acdad5e..a6c48c40cff7 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -215,6 +215,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final AppOpsManager mAppOpsManager; + private final ActivityTaskManagerInternal mActivityTaskManagerService; + private final MainHandler mMainHandler; private final GlobalActionPerformer mGlobalActionPerformer; @@ -306,6 +308,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mMainHandler = new MainHandler(mContext.getMainLooper()); mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService); mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler); + mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class); registerBroadcastReceivers(); new AccessibilityContentObserver(mMainHandler).register( @@ -1633,7 +1636,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (service == null) { service = new AccessibilityServiceConnection(userState, mContext, componentName, installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy, - this, mWindowManagerService, mGlobalActionPerformer); + this, mWindowManagerService, mGlobalActionPerformer, + mActivityTaskManagerService); } else if (userState.mBoundServices.contains(service)) { continue; } @@ -3026,7 +3030,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState, mContext, COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy, AccessibilityManagerService.this, mWindowManagerService, - mGlobalActionPerformer) { + mGlobalActionPerformer, mActivityTaskManagerService) { @Override public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) { return true; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index b66caa5e324a..91031e602824 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -34,6 +34,7 @@ import android.util.Slog; import com.android.server.accessibility.AccessibilityManagerService.SecurityPolicy; import com.android.server.accessibility.AccessibilityManagerService.UserState; +import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; import java.lang.ref.WeakReference; @@ -58,6 +59,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect */ final WeakReference<UserState> mUserStateWeakReference; final Intent mIntent; + final ActivityTaskManagerInternal mActivityTaskManagerService; private final Handler mMainHandler; @@ -69,7 +71,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler, Object lock, SecurityPolicy securityPolicy, SystemSupport systemSupport, WindowManagerInternal windowManagerInternal, - GlobalActionPerformer globalActionPerfomer) { + GlobalActionPerformer globalActionPerfomer, + ActivityTaskManagerInternal activityTaskManagerService) { super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock, securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer); mUserStateWeakReference = new WeakReference<UserState>(userState); @@ -77,6 +80,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect mMainHandler = mainHandler; mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, com.android.internal.R.string.accessibility_binding_label); + mActivityTaskManagerService = activityTaskManagerService; final long identity = Binder.clearCallingIdentity(); try { mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, mSystemSupport.getPendingIntentActivity( @@ -103,6 +107,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } finally { Binder.restoreCallingIdentity(identity); } + mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), + mAccessibilityServiceInfo.getResolveInfo().serviceInfo.applicationInfo.uid, + userState.mUserId); } public void unbindLocked() { @@ -111,6 +118,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (userState == null) return; userState.removeServiceLocked(this); mSystemSupport.getMagnificationController().resetAllIfNeeded(mId); + // Set uid to -1 to clear allowing app switches. + mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), + /* uid= */ -1, userState.mUserId); resetLocked(); } @@ -208,6 +218,12 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public void onServiceDisconnected(ComponentName componentName) { binderDied(); + UserState userState = mUserStateWeakReference.get(); + if (userState != null) { + // Set uid to -1 to clear allowing app switches. + mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(), + /* uid= */ -1, userState.mUserId); + } } @Override diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 87991beefcee..ce2bc82389ef 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -18,6 +18,7 @@ package com.android.server.autofill; import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; +import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; import static android.view.autofill.AutofillManager.ACTION_START_SESSION; import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED; @@ -581,7 +582,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + ", flags=" + flags + ")"); } mForAugmentedAutofillOnly = true; - triggerAugmentedAutofillLocked(); + triggerAugmentedAutofillLocked(flags); return; } viewState.setState(newState); @@ -780,7 +781,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState id, mCompatMode); } // Although "standard" autofill is disabled, it might still trigger augmented autofill - if (triggerAugmentedAutofillLocked() != null) { + if (triggerAugmentedAutofillLocked(requestFlags) != null) { mForAugmentedAutofillOnly = true; if (sDebug) { Slog.d(TAG, "Service disabled autofill for " + mComponentName @@ -2424,7 +2425,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Slog.d(TAG, "updateLocked(" + id + "): augmented-autofillable"); // ...then trigger the augmented autofill UI - triggerAugmentedAutofillLocked(); + triggerAugmentedAutofillLocked(flags); return; } @@ -2688,8 +2689,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // The default autofill service cannot fullfill the request, let's check if the augmented // autofill service can. - mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(); - if (mAugmentedAutofillDestroyer == null) { + mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(flags); + if (mAugmentedAutofillDestroyer == null && ((flags & FLAG_PASSWORD_INPUT_TYPE) == 0)) { if (sVerbose) { Slog.v(TAG, "canceling session " + id + " when service returned null and it cannot " + "be augmented. AutofillableIds: " + autofillableIds); @@ -2699,8 +2700,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState removeSelf(); } else { if (sVerbose) { - Slog.v(TAG, "keeping session " + id + " when service returned null but " - + "it can be augmented. AutofillableIds: " + autofillableIds); + if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) { + Slog.v(TAG, "keeping session " + id + " when service returned null and " + + "augmented service is disabled for password fields. " + + "AutofillableIds: " + autofillableIds); + } else { + Slog.v(TAG, "keeping session " + id + " when service returned null but " + + "it can be augmented. AutofillableIds: " + autofillableIds); + } } mAugmentedAutofillableIds = autofillableIds; try { @@ -2719,7 +2726,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // TODO(b/123099468): might need to call it in other places, like when the service returns a // non-null response but without datasets (for example, just SaveInfo) @GuardedBy("mLock") - private Runnable triggerAugmentedAutofillLocked() { + private Runnable triggerAugmentedAutofillLocked(int flags) { + // (TODO: b/141703197) Fix later by passing info to service. + if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) { + return null; + } + // Check if Smart Suggestions is supported... final @SmartSuggestionMode int supportedModes = mService .getSupportedSmartSuggestionModesLocked(); diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java index ecea251cc1ac..9cdb58d8c019 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java @@ -16,7 +16,6 @@ package com.android.server.contentsuggestions; -import static android.Manifest.permission.BIND_CONTENT_SUGGESTIONS_SERVICE; import static android.Manifest.permission.MANAGE_CONTENT_SUGGESTIONS; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -96,7 +95,7 @@ public class ContentSuggestionsManagerService extends private void enforceCaller(int userId, String func) { Context ctx = getContext(); - if (ctx.checkCallingPermission(BIND_CONTENT_SUGGESTIONS_SERVICE) == PERMISSION_GRANTED + if (ctx.checkCallingPermission(MANAGE_CONTENT_SUGGESTIONS) == PERMISSION_GRANTED || mServiceNameResolver.isTemporary(userId) || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid())) { return; diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java index 06d9395cd7d6..7828050223f4 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.contentsuggestions.ClassificationsRequest; +import android.app.contentsuggestions.ContentSuggestionsManager; import android.app.contentsuggestions.IClassificationsCallback; import android.app.contentsuggestions.ISelectionsCallback; import android.app.contentsuggestions.SelectionsRequest; @@ -97,15 +98,19 @@ public final class ContentSuggestionsPerUserService extends void provideContextImageLocked(int taskId, @NonNull Bundle imageContextRequestExtras) { RemoteContentSuggestionsService service = ensureRemoteServiceLocked(); if (service != null) { - ActivityManager.TaskSnapshot snapshot = - mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false); GraphicBuffer snapshotBuffer = null; int colorSpaceId = 0; - if (snapshot != null) { - snapshotBuffer = snapshot.getSnapshot(); - ColorSpace colorSpace = snapshot.getColorSpace(); - if (colorSpace != null) { - colorSpaceId = colorSpace.getId(); + + // Skip taking TaskSnapshot when bitmap is provided. + if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) { + ActivityManager.TaskSnapshot snapshot = + mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false); + if (snapshot != null) { + snapshotBuffer = snapshot.getSnapshot(); + ColorSpace colorSpace = snapshot.getColorSpace(); + if (colorSpace != null) { + colorSpaceId = colorSpace.getId(); + } } } diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java index 5b0de5e2aae0..0a735029eead 100644 --- a/services/core/java/com/android/server/AlarmManagerInternal.java +++ b/services/core/java/com/android/server/AlarmManagerInternal.java @@ -16,6 +16,8 @@ package com.android.server; +import android.app.PendingIntent; + public interface AlarmManagerInternal { // Some other components in the system server need to know about // broadcast alarms currently in flight @@ -30,4 +32,10 @@ public interface AlarmManagerInternal { boolean isIdling(); public void removeAlarmsForUid(int uid); public void registerInFlightListener(InFlightListener callback); + + /** + * Removes any alarm with the given pending intent with equality determined using + * {@link android.app.PendingIntent#equals(java.lang.Object) PendingIntent.equals} + */ + void remove(PendingIntent rec); } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index bbb7c523bac9..3f6859329f76 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -210,7 +210,6 @@ class AlarmManagerService extends SystemService { IAlarmListener mTimeTickTrigger; PendingIntent mDateChangeSender; Random mRandom; - PendingIntent.CancelListener mOperationCancelListener; boolean mInteractive = true; long mNonInteractiveStartTime; long mNonInteractiveTime; @@ -1498,7 +1497,6 @@ class AlarmManagerService extends SystemService { synchronized (mLock) { mHandler = new AlarmHandler(); - mOperationCancelListener = (intent) -> removeImpl(intent, null); mConstants = new Constants(mHandler); mAppWakeupHistory = new AppWakeupHistory(Constants.DEFAULT_APP_STANDBY_WINDOW); @@ -1750,9 +1748,6 @@ class AlarmManagerService extends SystemService { } else { maxElapsed = triggerElapsed + windowLength; } - if (operation != null) { - operation.registerCancelListener(mOperationCancelListener); - } synchronized (mLock) { if (DEBUG_BATCH) { Slog.v(TAG, "set(" + operation + ") : type=" + type @@ -1765,8 +1760,6 @@ class AlarmManagerService extends SystemService { "Maximum limit of concurrent alarms " + mConstants.MAX_ALARMS_PER_UID + " reached for uid: " + UserHandle.formatUid(callingUid) + ", callingPackage: " + callingPackage; - mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER, - operation).sendToTarget(); Slog.w(TAG, errorMsg); throw new IllegalStateException(errorMsg); } @@ -1787,8 +1780,6 @@ class AlarmManagerService extends SystemService { if (ActivityManager.getService().isAppStartModeDisabled(callingUid, callingPackage)) { Slog.w(TAG, "Not setting alarm from " + callingUid + ":" + a + " -- package not allowed to start"); - mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER, - operation).sendToTarget(); return; } } catch (RemoteException e) { @@ -2042,6 +2033,11 @@ class AlarmManagerService extends SystemService { } @Override + public void remove(PendingIntent pi) { + mHandler.obtainMessage(AlarmHandler.REMOVE_FOR_CANCELED, pi).sendToTarget(); + } + + @Override public void registerInFlightListener(InFlightListener callback) { synchronized (mLock) { mInFlightListeners.add(callback); @@ -2146,8 +2142,6 @@ class AlarmManagerService extends SystemService { synchronized (mLock) { removeLocked(operation, listener); } - mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER, - operation).sendToTarget(); } @Override @@ -4152,7 +4146,7 @@ class AlarmManagerService extends SystemService { public static final int APP_STANDBY_BUCKET_CHANGED = 5; public static final int APP_STANDBY_PAROLE_CHANGED = 6; public static final int REMOVE_FOR_STOPPED = 7; - public static final int UNREGISTER_CANCEL_LISTENER = 8; + public static final int REMOVE_FOR_CANCELED = 8; AlarmHandler() { super(Looper.myLooper()); @@ -4235,10 +4229,10 @@ class AlarmManagerService extends SystemService { } break; - case UNREGISTER_CANCEL_LISTENER: - final PendingIntent pi = (PendingIntent) msg.obj; - if (pi != null) { - pi.unregisterCancelListener(mOperationCancelListener); + case REMOVE_FOR_CANCELED: + final PendingIntent operation = (PendingIntent) msg.obj; + synchronized (mLock) { + removeLocked(operation, null); } break; @@ -4697,11 +4691,6 @@ class AlarmManagerService extends SystemService { Intent.EXTRA_ALARM_COUNT, alarm.count), mDeliveryTracker, mHandler, null, allowWhileIdle ? mIdleOptions : null); - if (alarm.repeatInterval == 0) { - // Keep the listener for repeating alarms until they get cancelled - mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER, - alarm.operation).sendToTarget(); - } } catch (PendingIntent.CanceledException e) { if (alarm.repeatInterval > 0) { // This IntentSender is no longer valid, but this diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index e51025943df4..f2ce444a8b90 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -19,6 +19,7 @@ package com.android.server; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; +import android.app.ActivityThread; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -40,6 +41,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.BinderInternal; import com.android.internal.os.CachedDeviceState; +import com.android.internal.util.DumpUtils; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -49,6 +51,7 @@ import java.util.List; public class BinderCallsStatsService extends Binder { private static final String TAG = "BinderCallsStatsService"; + private static final String SERVICE_NAME = "binder_calls_stats"; private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING = "persist.sys.binder_calls_detailed_tracking"; @@ -246,7 +249,7 @@ public class BinderCallsStatsService extends Binder { mService = new BinderCallsStatsService( mBinderCallsStats, mWorkSourceProvider); publishLocalService(Internal.class, new Internal(mBinderCallsStats)); - publishBinderService("binder_calls_stats", mService); + publishBinderService(SERVICE_NAME, mService); boolean detailedTrackingEnabled = SystemProperties.getBoolean( PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false); @@ -293,6 +296,11 @@ public class BinderCallsStatsService extends Binder { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpAndUsageStatsPermission(ActivityThread.currentApplication(), + SERVICE_NAME, pw)) { + return; + } + boolean verbose = false; if (args != null) { for (final String arg : args) { diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 2ab46e65e77f..1fd4a0c384d3 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -291,6 +291,7 @@ public class DeviceIdleController extends SystemService private boolean mLightEnabled; private boolean mDeepEnabled; private boolean mQuickDozeActivated; + private boolean mQuickDozeActivatedWhileIdling; private boolean mForceIdle; private boolean mNetworkConnected; private boolean mScreenOn; @@ -302,6 +303,10 @@ public class DeviceIdleController extends SystemService private boolean mHasNetworkLocation; private Location mLastGenericLocation; private Location mLastGpsLocation; + + /** Time in the elapsed realtime timebase when this listener last received a motion event. */ + private long mLastMotionEventElapsed; + // Current locked state of the screen private boolean mScreenLocked; private int mNumBlockingConstraints = 0; @@ -547,6 +552,9 @@ public class DeviceIdleController extends SystemService */ private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>(); + private final ArraySet<StationaryListener> mStationaryListeners = + new ArraySet<>(); + private static final int EVENT_NULL = 0; private static final int EVENT_NORMAL = 1; private static final int EVENT_LIGHT_IDLE = 2; @@ -605,6 +613,30 @@ public class DeviceIdleController extends SystemService } }; + /** AlarmListener to start monitoring motion if there are registered stationary listeners. */ + private final AlarmManager.OnAlarmListener mMotionRegistrationAlarmListener = () -> { + synchronized (DeviceIdleController.this) { + if (mStationaryListeners.size() > 0) { + startMonitoringMotionLocked(); + } + } + }; + + private final AlarmManager.OnAlarmListener mMotionTimeoutAlarmListener = () -> { + synchronized (DeviceIdleController.this) { + if (!isStationaryLocked()) { + // If the device keeps registering motion, then the alarm should be + // rescheduled, so this shouldn't go off until the device is stationary. + // This case may happen in a race condition (alarm goes off right before + // motion is detected, but handleMotionDetectedLocked is called before + // we enter this block). + Slog.w(TAG, "motion timeout went off and device isn't stationary"); + return; + } + } + postStationaryStatusUpdated(); + }; + private final AlarmManager.OnAlarmListener mSensingTimeoutAlarmListener = new AlarmManager.OnAlarmListener() { @Override @@ -654,12 +686,70 @@ public class DeviceIdleController extends SystemService } }; + /** Post stationary status only to this listener. */ + private void postStationaryStatus(StationaryListener listener) { + mHandler.obtainMessage(MSG_REPORT_STATIONARY_STATUS, listener).sendToTarget(); + } + + /** Post stationary status to all registered listeners. */ + private void postStationaryStatusUpdated() { + mHandler.sendEmptyMessage(MSG_REPORT_STATIONARY_STATUS); + } + + private boolean isStationaryLocked() { + final long now = mInjector.getElapsedRealtime(); + return mMotionListener.active + // Listening for motion for long enough and last motion was long enough ago. + && now - Math.max(mMotionListener.activatedTimeElapsed, mLastMotionEventElapsed) + >= mConstants.MOTION_INACTIVE_TIMEOUT; + } + + @VisibleForTesting + void registerStationaryListener(StationaryListener listener) { + synchronized (this) { + if (!mStationaryListeners.add(listener)) { + // Listener already registered. + return; + } + postStationaryStatus(listener); + if (mMotionListener.active) { + if (!isStationaryLocked() && mStationaryListeners.size() == 1) { + // First listener to be registered and the device isn't stationary, so we + // need to register the alarm to report the device is stationary. + scheduleMotionTimeoutAlarmLocked(); + } + } else { + startMonitoringMotionLocked(); + scheduleMotionTimeoutAlarmLocked(); + } + } + } + + private void unregisterStationaryListener(StationaryListener listener) { + synchronized (this) { + if (mStationaryListeners.remove(listener) && mStationaryListeners.size() == 0 + // Motion detection is started when transitioning from INACTIVE to IDLE_PENDING + // and so doesn't need to be on for ACTIVE or INACTIVE states. + // Motion detection isn't needed when idling due to Quick Doze. + && (mState == STATE_ACTIVE || mState == STATE_INACTIVE + || mQuickDozeActivated)) { + maybeStopMonitoringMotionLocked(); + } + } + } + @VisibleForTesting final class MotionListener extends TriggerEventListener implements SensorEventListener { boolean active = false; + /** + * Time in the elapsed realtime timebase when this listener was activated. Only valid if + * {@link #active} is true. + */ + long activatedTimeElapsed; + public boolean isActive() { return active; } @@ -694,6 +784,7 @@ public class DeviceIdleController extends SystemService } if (success) { active = true; + activatedTimeElapsed = mInjector.getElapsedRealtime(); } else { Slog.e(TAG, "Unable to register for " + mMotionSensor); } @@ -1307,6 +1398,8 @@ public class DeviceIdleController extends SystemService private static final int MSG_SEND_CONSTRAINT_MONITORING = 10; private static final int MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11; private static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12; + @VisibleForTesting + static final int MSG_REPORT_STATIONARY_STATUS = 13; final class MyHandler extends Handler { MyHandler(Looper looper) { @@ -1443,6 +1536,30 @@ public class DeviceIdleController extends SystemService updatePreIdleFactor(); maybeDoImmediateMaintenance(); } break; + case MSG_REPORT_STATIONARY_STATUS: { + final StationaryListener newListener = (StationaryListener) msg.obj; + final StationaryListener[] listeners; + final boolean isStationary; + synchronized (DeviceIdleController.this) { + isStationary = isStationaryLocked(); + if (newListener == null) { + // Only notify all listeners if we aren't directing to one listener. + listeners = mStationaryListeners.toArray( + new StationaryListener[mStationaryListeners.size()]); + } else { + listeners = null; + } + } + if (listeners != null) { + for (StationaryListener listener : listeners) { + listener.onDeviceStationaryChanged(isStationary); + } + } + if (newListener != null) { + newListener.onDeviceStationaryChanged(isStationary); + } + } + break; } } } @@ -1628,6 +1745,19 @@ public class DeviceIdleController extends SystemService } } + /** + * Listener to be notified when DeviceIdleController determines that the device has + * moved or is stationary. + */ + public interface StationaryListener { + /** + * Called when DeviceIdleController has determined that the device is stationary or moving. + * + * @param isStationary true if the device is stationary, false otherwise + */ + void onDeviceStationaryChanged(boolean isStationary); + } + public class LocalService { public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) { synchronized (DeviceIdleController.this) { @@ -1693,6 +1823,24 @@ public class DeviceIdleController extends SystemService public int[] getPowerSaveTempWhitelistAppIds() { return DeviceIdleController.this.getAppIdTempWhitelistInternal(); } + + /** + * Registers a listener that will be notified when the system has detected that the device + * is + * stationary or in motion. + */ + public void registerStationaryListener(StationaryListener listener) { + DeviceIdleController.this.registerStationaryListener(listener); + } + + /** + * Unregisters a registered stationary listener from being notified when the system has + * detected + * that the device is stationary or in motion. + */ + public void unregisterStationaryListener(StationaryListener listener) { + DeviceIdleController.this.unregisterStationaryListener(listener); + } } static class Injector { @@ -1734,6 +1882,11 @@ public class DeviceIdleController extends SystemService return mConstants; } + /** Returns the current elapsed realtime in milliseconds. */ + long getElapsedRealtime() { + return SystemClock.elapsedRealtime(); + } + LocationManager getLocationManager() { if (mLocationManager == null) { mLocationManager = mContext.getSystemService(LocationManager.class); @@ -1745,6 +1898,27 @@ public class DeviceIdleController extends SystemService return controller.new MyHandler(BackgroundThread.getHandler().getLooper()); } + Sensor getMotionSensor() { + final SensorManager sensorManager = getSensorManager(); + Sensor motionSensor = null; + int sigMotionSensorId = mContext.getResources().getInteger( + com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor); + if (sigMotionSensorId > 0) { + motionSensor = sensorManager.getDefaultSensor(sigMotionSensorId, true); + } + if (motionSensor == null && mContext.getResources().getBoolean( + com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) { + motionSensor = sensorManager.getDefaultSensor( + Sensor.TYPE_WRIST_TILT_GESTURE, true); + } + if (motionSensor == null) { + // As a last ditch, fall back to SMD. + motionSensor = sensorManager.getDefaultSensor( + Sensor.TYPE_SIGNIFICANT_MOTION, true); + } + return motionSensor; + } + PowerManager getPowerManager() { return mContext.getSystemService(PowerManager.class); } @@ -1896,21 +2070,7 @@ public class DeviceIdleController extends SystemService mSensorManager = mInjector.getSensorManager(); if (mUseMotionSensor) { - int sigMotionSensorId = getContext().getResources().getInteger( - com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor); - if (sigMotionSensorId > 0) { - mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true); - } - if (mMotionSensor == null && getContext().getResources().getBoolean( - com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) { - mMotionSensor = mSensorManager.getDefaultSensor( - Sensor.TYPE_WRIST_TILT_GESTURE, true); - } - if (mMotionSensor == null) { - // As a last ditch, fall back to SMD. - mMotionSensor = mSensorManager.getDefaultSensor( - Sensor.TYPE_SIGNIFICANT_MOTION, true); - } + mMotionSensor = mInjector.getMotionSensor(); } if (getContext().getResources().getBoolean( @@ -2601,6 +2761,8 @@ public class DeviceIdleController extends SystemService void updateQuickDozeFlagLocked(boolean enabled) { if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled); mQuickDozeActivated = enabled; + mQuickDozeActivatedWhileIdling = + mQuickDozeActivated && (mState == STATE_IDLE || mState == STATE_IDLE_MAINTENANCE); if (enabled) { // If Quick Doze is enabled, see if we should go straight into it. becomeInactiveIfAppropriateLocked(); @@ -2767,10 +2929,11 @@ public class DeviceIdleController extends SystemService mNextIdleDelay = 0; mNextLightIdleDelay = 0; mIdleStartTime = 0; + mQuickDozeActivatedWhileIdling = false; cancelAlarmLocked(); cancelSensingTimeoutAlarmLocked(); cancelLocatingLocked(); - stopMonitoringMotionLocked(); + maybeStopMonitoringMotionLocked(); mAnyMotionDetector.stop(); updateActiveConstraintsLocked(); } @@ -3270,11 +3433,27 @@ public class DeviceIdleController extends SystemService void motionLocked() { if (DEBUG) Slog.d(TAG, "motionLocked()"); - // The motion sensor will have been disabled at this point + mLastMotionEventElapsed = mInjector.getElapsedRealtime(); handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion"); } void handleMotionDetectedLocked(long timeout, String type) { + if (mStationaryListeners.size() > 0) { + postStationaryStatusUpdated(); + scheduleMotionTimeoutAlarmLocked(); + // We need to re-register the motion listener, but we don't want the sensors to be + // constantly active or to churn the CPU by registering too early, register after some + // delay. + scheduleMotionRegistrationAlarmLocked(); + } + if (mQuickDozeActivated && !mQuickDozeActivatedWhileIdling) { + // Don't exit idle due to motion if quick doze is enabled. + // However, if the device started idling due to the normal progression (going through + // all the states) and then had quick doze activated, come out briefly on motion so the + // user can get slightly fresher content. + return; + } + maybeStopMonitoringMotionLocked(); // The device is not yet active, so we want to go back to the pending idle // state to wait again for no motion. Note that we only monitor for motion // after moving out of the inactive state, so no need to worry about that. @@ -3326,10 +3505,18 @@ public class DeviceIdleController extends SystemService } } - void stopMonitoringMotionLocked() { - if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()"); - if (mMotionSensor != null && mMotionListener.active) { - mMotionListener.unregisterLocked(); + /** + * Stops motion monitoring. Will not stop monitoring if there are registered stationary + * listeners. + */ + private void maybeStopMonitoringMotionLocked() { + if (DEBUG) Slog.d(TAG, "maybeStopMonitoringMotionLocked()"); + if (mMotionSensor != null && mStationaryListeners.size() == 0) { + if (mMotionListener.active) { + mMotionListener.unregisterLocked(); + cancelMotionTimeoutAlarmLocked(); + } + cancelMotionRegistrationAlarmLocked(); } } @@ -3356,6 +3543,14 @@ public class DeviceIdleController extends SystemService } } + private void cancelMotionTimeoutAlarmLocked() { + mAlarmManager.cancel(mMotionTimeoutAlarmListener); + } + + private void cancelMotionRegistrationAlarmLocked() { + mAlarmManager.cancel(mMotionRegistrationAlarmListener); + } + void cancelSensingTimeoutAlarmLocked() { if (mNextSensingTimeoutAlarmTime != 0) { mNextSensingTimeoutAlarmTime = 0; @@ -3402,6 +3597,23 @@ public class DeviceIdleController extends SystemService mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler); } + private void scheduleMotionRegistrationAlarmLocked() { + if (DEBUG) Slog.d(TAG, "scheduleMotionRegistrationAlarmLocked"); + long nextMotionRegistrationAlarmTime = + mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT / 2; + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionRegistrationAlarmTime, + "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener, + mHandler); + } + + private void scheduleMotionTimeoutAlarmLocked() { + if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked"); + long nextMotionTimeoutAlarmTime = + mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT; + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime, + "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler); + } + void scheduleSensingTimeoutAlarmLocked(long delay) { if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")"); mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay; @@ -4322,9 +4534,14 @@ public class DeviceIdleController extends SystemService } pw.println(" }"); } - if (mUseMotionSensor) { + if (mUseMotionSensor || mStationaryListeners.size() > 0) { pw.print(" mMotionActive="); pw.println(mMotionListener.active); pw.print(" mNotMoving="); pw.println(mNotMoving); + pw.print(" mMotionListener.activatedTimeElapsed="); + pw.println(mMotionListener.activatedTimeElapsed); + pw.print(" mLastMotionEventElapsed="); pw.println(mLastMotionEventElapsed); + pw.print(" "); pw.print(mStationaryListeners.size()); + pw.println(" stationary listeners registered"); } pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps="); pw.print(mHasGps); pw.print(" mHasNetwork="); diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index 7909e3096cbe..c60460fccb76 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -44,9 +44,9 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements private static final String TAG = "DynamicSystemService"; private static final String NO_SERVICE_ERROR = "no gsiservice"; private static final int GSID_ROUGH_TIMEOUT_MS = 8192; - private static final String PATH_DEFAULT = "/data/gsi"; + private static final String PATH_DEFAULT = "/data/gsi/"; private Context mContext; - private String mInstallPath; + private String mInstallPath, mDsuSlot; private volatile IGsiService mGsiService; DynamicSystemService(Context context) { @@ -115,7 +115,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements } @Override - public boolean startInstallation() throws RemoteException { + public boolean startInstallation(String dsuSlot) throws RemoteException { IGsiService service = getGsiService(); // priority from high to low: sysprop -> sdcard -> /data String path = SystemProperties.get("os.aot.path"); @@ -129,16 +129,17 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements if (!Environment.MEDIA_MOUNTED.equals(volume.getState())) continue; File sdCard = volume.getPathFile(); if (sdCard.isDirectory()) { - path = sdCard.getPath(); + path = new File(sdCard, dsuSlot).getPath(); break; } } if (path.isEmpty()) { - path = PATH_DEFAULT; + path = PATH_DEFAULT + dsuSlot; } Slog.i(TAG, "startInstallation -> " + path); } mInstallPath = path; + mDsuSlot = dsuSlot; if (service.openInstall(path) != 0) { Slog.i(TAG, "Failed to open " + path); return false; @@ -203,7 +204,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements public boolean setEnable(boolean enable, boolean oneShot) throws RemoteException { IGsiService gsiService = getGsiService(); if (enable) { - return gsiService.enableGsi(oneShot) == 0; + return gsiService.enableGsi(oneShot, mDsuSlot) == 0; } else { return gsiService.disableGsi(); } diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java index 06c46b908b7a..6a9246dacc51 100644 --- a/services/core/java/com/android/server/MasterClearReceiver.java +++ b/services/core/java/com/android/server/MasterClearReceiver.java @@ -82,7 +82,7 @@ public class MasterClearReceiver extends BroadcastReceiver { } }; - if (mWipeExternalStorage || mWipeEsims) { + if (mWipeExternalStorage) { // thr will be started at the end of this task. new WipeDataTask(context, thr).execute(); } else { diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index 9a7a4e79ab33..2fb1f7783814 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -54,7 +54,6 @@ import android.provider.Settings.Global; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.IntArray; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -527,7 +526,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @Override public void accept(INetworkScoreCache networkScoreCache, Object cookie) { - int filterType = NetworkScoreManager.CACHE_FILTER_NONE; + int filterType = NetworkScoreManager.SCORE_FILTER_NONE; if (cookie instanceof Integer) { filterType = (Integer) cookie; } @@ -551,17 +550,17 @@ public class NetworkScoreService extends INetworkScoreService.Stub { private List<ScoredNetwork> filterScores(List<ScoredNetwork> scoredNetworkList, int filterType) { switch (filterType) { - case NetworkScoreManager.CACHE_FILTER_NONE: + case NetworkScoreManager.SCORE_FILTER_NONE: return scoredNetworkList; - case NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK: + case NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK: if (mCurrentNetworkFilter == null) { mCurrentNetworkFilter = new CurrentNetworkScoreCacheFilter(new WifiInfoSupplier(mContext)); } return mCurrentNetworkFilter.apply(scoredNetworkList); - case NetworkScoreManager.CACHE_FILTER_SCAN_RESULTS: + case NetworkScoreManager.SCORE_FILTER_SCAN_RESULTS: if (mScanResultsFilter == null) { mScanResultsFilter = new ScanResultsScoreCacheFilter( new ScanResultsSupplier(mContext)); diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index ea3dd3d4ba8d..fc140a2bfbaa 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -56,6 +56,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; @@ -80,6 +81,22 @@ public class PackageWatchdog { static final String PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED = "watchdog_explicit_health_check_enabled"; + public static final int FAILURE_REASON_UNKNOWN = 0; + public static final int FAILURE_REASON_NATIVE_CRASH = 1; + public static final int FAILURE_REASON_EXPLICIT_HEALTH_CHECK = 2; + public static final int FAILURE_REASON_APP_CRASH = 3; + public static final int FAILURE_REASON_APP_NOT_RESPONDING = 4; + + @IntDef(prefix = { "FAILURE_REASON_" }, value = { + FAILURE_REASON_UNKNOWN, + FAILURE_REASON_NATIVE_CRASH, + FAILURE_REASON_EXPLICIT_HEALTH_CHECK, + FAILURE_REASON_APP_CRASH, + FAILURE_REASON_APP_NOT_RESPONDING + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FailureReasons {} + // Duration to count package failures before it resets to 0 private static final int DEFAULT_TRIGGER_FAILURE_DURATION_MS = (int) TimeUnit.MINUTES.toMillis(1); @@ -295,14 +312,15 @@ public class PackageWatchdog { } /** - * Called when a process fails either due to a crash or ANR. + * Called when a process fails due to a crash, ANR or explicit health check. * * <p>For each package contained in the process, one registered observer with the least user * impact will be notified for mitigation. * * <p>This method could be called frequently if there is a severe problem on the device. */ - public void onPackageFailure(List<VersionedPackage> packages) { + public void onPackageFailure(List<VersionedPackage> packages, + @FailureReasons int failureReason) { mLongTaskHandler.post(() -> { synchronized (mLock) { if (mAllObservers.isEmpty()) { @@ -333,7 +351,7 @@ public class PackageWatchdog { // Execute action with least user impact if (currentObserverToNotify != null) { - currentObserverToNotify.execute(versionedPackage); + currentObserverToNotify.execute(versionedPackage, failureReason); } } } @@ -404,7 +422,7 @@ public class PackageWatchdog { * * @return {@code true} if action was executed successfully, {@code false} otherwise */ - boolean execute(VersionedPackage versionedPackage); + boolean execute(VersionedPackage versionedPackage, @FailureReasons int failureReason); // TODO(b/120598832): Ensure uniqueness? /** @@ -648,7 +666,8 @@ public class PackageWatchdog { // the tests don't install any packages versionedPkg = new VersionedPackage(failedPackage, 0L); } - registeredObserver.execute(versionedPkg); + registeredObserver.execute(versionedPkg, + PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); } } } @@ -759,7 +778,7 @@ public class PackageWatchdog { final List<VersionedPackage> pkgList = Collections.singletonList(pkg); final long failureCount = getTriggerFailureCount(); for (int i = 0; i < failureCount; i++) { - onPackageFailure(pkgList); + onPackageFailure(pkgList, FAILURE_REASON_EXPLICIT_HEALTH_CHECK); } }); } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index d1bc6af88347..90b0c11cc8dd 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -2648,9 +2648,11 @@ class StorageManagerService extends IStorageManager.Stub */ @Override public boolean supportsCheckpoint() throws RemoteException { - // Only the system process is permitted to start checkpoints - if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { - throw new SecurityException("no permission to check filesystem checkpoint support"); + // Only the root, system_server and shell processes are permitted to start checkpoints + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID + && callingUid != Process.SHELL_UID) { + throw new SecurityException("no permission to start filesystem checkpoint"); } return mVold.supportsCheckpoint(); @@ -2665,8 +2667,10 @@ class StorageManagerService extends IStorageManager.Stub */ @Override public void startCheckpoint(int numTries) throws RemoteException { - // Only the system process is permitted to start checkpoints - if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { + // Only the root, system_server and shell processes are permitted to start checkpoints + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID + && callingUid != Process.SHELL_UID) { throw new SecurityException("no permission to start filesystem checkpoint"); } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 630554ddf72d..1b53bef773fe 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -1293,7 +1293,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // only CarrierService with carrier privilege rule should have the permission int[] subIds = Arrays.stream(SubscriptionManager.from(mContext) .getActiveSubscriptionIdList(false)) - .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(i)).toArray(); + .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext, + i)).toArray(); if (ArrayUtils.isEmpty(subIds)) { loge("notifyCarrierNetworkChange without carrier privilege"); // the active subId does not have carrier privilege. @@ -1595,7 +1596,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { TelephonyManager.DATA_UNKNOWN, TelephonyManager.NETWORK_TYPE_UNKNOWN, ApnSetting.getApnTypesBitmaskFromString(apnType), null, null, - DataFailCause.NONE)); + DataFailCause.NONE, null)); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) @@ -1780,7 +1781,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { TelephonyManager.DATA_UNKNOWN, TelephonyManager.NETWORK_TYPE_UNKNOWN, ApnSetting.getApnTypesBitmaskFromString(apnType), null, null, - failCause)); + failCause, null)); for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) @@ -2301,7 +2302,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return; } - TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege( + TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mContext, SubscriptionManager.getDefaultSubscriptionId(), method); } diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 9d71896ef08b..7ad04728b512 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -58,6 +58,7 @@ import android.util.ArraySet; import android.util.Slog; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.DisableCarModeActivity; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -65,6 +66,7 @@ import com.android.internal.util.DumpUtils; import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; import com.android.server.twilight.TwilightState; +import com.android.server.wm.WindowManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -85,15 +87,24 @@ final class UiModeManagerService extends SystemService { private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; private int mNightMode = UiModeManager.MODE_NIGHT_NO; + // we use the override auto mode + // for example: force night mode off in the night time while in auto mode + private int mNightModeOverride = mNightMode; + protected static final String OVERRIDE_NIGHT_MODE = Secure.UI_NIGHT_MODE + "_override"; private Map<Integer, String> mCarModePackagePriority = new HashMap<>(); private boolean mCarModeEnabled = false; private boolean mCharging = false; private boolean mPowerSave = false; + // Do not change configuration now. wait until screen turns off. + // This prevents jank and activity restart when the user + // is actively using the device + private boolean mWaitForScreenOff = false; private int mDefaultUiModeType; private boolean mCarModeKeepsScreenOn; private boolean mDeskModeKeepsScreenOn; private boolean mTelevision; + private boolean mCar; private boolean mWatch; private boolean mVrHeadset; private boolean mComputedNightMode; @@ -119,6 +130,7 @@ final class UiModeManagerService extends SystemService { private TwilightManager mTwilightManager; private NotificationManager mNotificationManager; private StatusBarManager mStatusBarManager; + private WindowManagerInternal mWindowManager; private PowerManager.WakeLock mWakeLock; @@ -128,6 +140,17 @@ final class UiModeManagerService extends SystemService { super(context); } + @VisibleForTesting + protected UiModeManagerService(Context context, WindowManagerInternal wm, + PowerManager.WakeLock wl, TwilightManager tm, + boolean setupWizardComplete) { + super(context); + mWindowManager = wm; + mWakeLock = wl; + mTwilightManager = tm; + mSetupWizardComplete = setupWizardComplete; + } + private static Intent buildHomeIntent(String category) { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(category); @@ -189,13 +212,31 @@ final class UiModeManagerService extends SystemService { public void onTwilightStateChanged(@Nullable TwilightState state) { synchronized (mLock) { if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) { - updateComputedNightModeLocked(); - updateLocked(0, 0); + if (mCar) { + updateLocked(0, 0); + } else { + registerScreenOffEvent(); + } } } } }; + /** + * DO NOT USE DIRECTLY + * see register registerScreenOffEvent and unregisterScreenOffEvent + */ + private final BroadcastReceiver mOnScreenOffHandler = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + // must unregister first before updating + unregisterScreenOffEvent(); + updateLocked(0, 0); + } + } + }; + private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { @Override public void onVrStateChanged(boolean enabled) { @@ -227,8 +268,10 @@ final class UiModeManagerService extends SystemService { private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange, Uri uri) { - final int mode = Secure.getIntForUser(getContext().getContentResolver(), - Secure.UI_NIGHT_MODE, mNightMode, 0); + int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, + mNightMode, 0); + mode = mode == UiModeManager.MODE_NIGHT_AUTO + ? UiModeManager.MODE_NIGHT_YES : mode; SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode)); } }; @@ -247,6 +290,7 @@ final class UiModeManagerService extends SystemService { final PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); + mWindowManager = LocalServices.getService(WindowManagerInternal.class); // If setup isn't complete for this user listen for completion so we can unblock // being able to send a night mode configuration change event @@ -290,6 +334,7 @@ final class UiModeManagerService extends SystemService { final PackageManager pm = context.getPackageManager(); mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION) || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK); + mCar = pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH); updateNightModeFromSettings(context, res, UserHandle.getCallingUserId()); @@ -298,7 +343,7 @@ final class UiModeManagerService extends SystemService { SystemServerInitThreadPool.get().submit(() -> { synchronized (mLock) { updateConfigurationLocked(); - sendConfigurationLocked(); + applyConfigurationExternallyLocked(); } }, TAG + ".onStart"); @@ -313,6 +358,16 @@ final class UiModeManagerService extends SystemService { false, mDarkThemeObserver, 0); } + @VisibleForTesting + protected IUiModeManager getService() { + return mService; + } + + @VisibleForTesting + protected Configuration getConfiguration() { + return mConfiguration; + } + // Records whether setup wizard has happened or not and adds an observer for this user if not. private void verifySetupWizardCompleted() { final Context context = getContext(); @@ -347,13 +402,32 @@ final class UiModeManagerService extends SystemService { if (mSetupWizardComplete) { mNightMode = Secure.getIntForUser(context.getContentResolver(), Secure.UI_NIGHT_MODE, defaultNightMode, userId); + mNightModeOverride = Secure.getIntForUser(context.getContentResolver(), + OVERRIDE_NIGHT_MODE, defaultNightMode, userId); } else { mNightMode = defaultNightMode; + mNightModeOverride = defaultNightMode; } return oldNightMode != mNightMode; } + private void registerScreenOffEvent() { + mWaitForScreenOff = true; + final IntentFilter intentFilter = + new IntentFilter(Intent.ACTION_SCREEN_OFF); + getContext().registerReceiver(mOnScreenOffHandler, intentFilter); + } + + private void unregisterScreenOffEvent() { + mWaitForScreenOff = false; + try { + getContext().unregisterReceiver(mOnScreenOffHandler); + } catch (IllegalArgumentException e) { + // we ignore this exception if the receiver is unregistered already. + } + } + private final IUiModeManager.Stub mService = new IUiModeManager.Stub() { @Override public void enableCarMode(@UiModeManager.EnableCarMode int flags, @@ -474,14 +548,23 @@ final class UiModeManagerService extends SystemService { try { synchronized (mLock) { if (mNightMode != mode) { - // Only persist setting if not in car mode - if (!mCarModeEnabled) { - Secure.putIntForUser(getContext().getContentResolver(), - Secure.UI_NIGHT_MODE, mode, user); + if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) { + unregisterScreenOffEvent(); } mNightMode = mode; - updateLocked(0, 0); + mNightModeOverride = mode; + + // Only persist setting if not in car mode + if (!mCarModeEnabled) { + persistNightMode(user); + } + // on screen off will update configuration instead + if (mNightMode != UiModeManager.MODE_NIGHT_AUTO || mCar) { + updateLocked(0, 0); + } else { + registerScreenOffEvent(); + } } } } finally { @@ -521,6 +604,33 @@ final class UiModeManagerService extends SystemService { if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return; dumpImpl(pw); } + + @Override + public boolean setNightModeActivated(boolean active) { + synchronized (mLock) { + final int user = UserHandle.getCallingUserId(); + final long ident = Binder.clearCallingIdentity(); + try { + if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) { + unregisterScreenOffEvent(); + mNightModeOverride = active + ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO; + } else if (mNightMode == UiModeManager.MODE_NIGHT_NO + && active) { + mNightMode = UiModeManager.MODE_NIGHT_YES; + } else if (mNightMode == UiModeManager.MODE_NIGHT_YES + && !active) { + mNightMode = UiModeManager.MODE_NIGHT_NO; + } + updateConfigurationLocked(); + applyConfigurationExternallyLocked(); + persistNightMode(user); + return true; + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } }; void dumpImpl(PrintWriter pw) { @@ -724,6 +834,13 @@ final class UiModeManagerService extends SystemService { } } + private void persistNightMode(int user) { + Secure.putIntForUser(getContext().getContentResolver(), + Secure.UI_NIGHT_MODE, mNightMode, user); + Secure.putIntForUser(getContext().getContentResolver(), + OVERRIDE_NIGHT_MODE, mNightModeOverride, user); + } + private void updateConfigurationLocked() { int uiMode = mDefaultUiModeType; if (mUiModeLocked) { @@ -769,15 +886,14 @@ final class UiModeManagerService extends SystemService { } mCurUiMode = uiMode; - if (!mHoldingConfiguration) { + if (!mHoldingConfiguration || !mWaitForScreenOff) { mConfiguration.uiMode = uiMode; } } - private void sendConfigurationLocked() { + private void applyConfigurationExternallyLocked() { if (mSetUiMode != mConfiguration.uiMode) { mSetUiMode = mConfiguration.uiMode; - try { ActivityTaskManager.getService().updateConfiguration(mConfiguration); } catch (RemoteException e) { @@ -957,7 +1073,7 @@ final class UiModeManagerService extends SystemService { } // Send the new configuration. - sendConfigurationLocked(); + applyConfigurationExternallyLocked(); // If we did not start a dock app, then start dreaming if supported. if (category != null && !dockAppStarted) { @@ -1022,6 +1138,19 @@ final class UiModeManagerService extends SystemService { if (state != null) { mComputedNightMode = state.isNight(); } + if (mNightModeOverride == UiModeManager.MODE_NIGHT_YES && !mComputedNightMode) { + mComputedNightMode = true; + return; + } + if (mNightModeOverride == UiModeManager.MODE_NIGHT_NO && mComputedNightMode) { + mComputedNightMode = false; + return; + } + + mNightModeOverride = mNightMode; + final int user = UserHandle.getCallingUserId(); + Secure.putIntForUser(getContext().getContentResolver(), + OVERRIDE_NIGHT_MODE, mNightModeOverride, user); } } diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 1432f57b4464..0d2882216f08 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -1289,6 +1289,33 @@ public class AccountManagerService } protected UserAccounts getUserAccounts(int userId) { + try { + return getUserAccountsNotChecked(userId); + } catch (RuntimeException e) { + if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + // Let it go... + throw e; + } + // User accounts database is corrupted, we must wipe out the whole user, otherwise the + // system will crash indefinitely + Slog.wtf(TAG, "Removing user " + userId + " due to exception (" + e + ") reading its " + + "account database"); + if (userId == ActivityManager.getCurrentUser() && userId != UserHandle.USER_SYSTEM) { + Slog.i(TAG, "Switching to system user first"); + try { + ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM); + } catch (RemoteException re) { + Slog.e(TAG, "Could not switch to " + UserHandle.USER_SYSTEM + ": " + re); + } + } + if (!getUserManager().removeUserEvenWhenDisallowed(userId)) { + Slog.e(TAG, "could not remove user " + userId); + } + throw e; + } + } + + private UserAccounts getUserAccountsNotChecked(int userId) { synchronized (mUsers) { UserAccounts accounts = mUsers.get(userId); boolean validateAccounts = false; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2af04ae3f800..d7b716561dc4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -18055,7 +18055,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int getCurrentUserId() { - return mUserController.getCurrentUserIdLU(); + return mUserController.getCurrentUserId(); } @Override diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 1ff6f4dac724..a4c695067139 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -430,7 +430,8 @@ class AppErrors { RescueParty.noteAppCrash(mContext, r.uid); } - mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode()); + mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(), + PackageWatchdog.FAILURE_REASON_APP_CRASH); } final int relaunchReason = r != null @@ -884,7 +885,8 @@ class AppErrors { } // Notify PackageWatchdog without the lock held if (packageList != null) { - mPackageWatchdog.onPackageFailure(packageList); + mPackageWatchdog.onPackageFailure(packageList, + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING); } } diff --git a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java index c7de7b17b1c2..ebfc2a011e88 100644 --- a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java @@ -37,6 +37,7 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.TextView; + import com.android.internal.R; diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS index 2f9a5c952659..7cc2e8eb2954 100644 --- a/services/core/java/com/android/server/am/OWNERS +++ b/services/core/java/com/android/server/am/OWNERS @@ -35,3 +35,5 @@ michaelwr@google.com narayan@google.com per-file SettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com + +per-file CarUserSwitchingDialog.java = keunyoung@google.com, felipeal@google.com, gurunagarajan@google.com diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java index d75591cc7432..df76713d58a6 100644 --- a/services/core/java/com/android/server/am/PendingIntentController.java +++ b/services/core/java/com/android/server/am/PendingIntentController.java @@ -43,6 +43,7 @@ import android.util.Slog; import com.android.internal.os.IResultReceiver; import com.android.internal.util.function.pooled.PooledLambda; +import com.android.server.AlarmManagerInternal; import com.android.server.LocalServices; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.SafeActivityOptions; @@ -293,6 +294,8 @@ public class PendingIntentController { PendingIntentController::handlePendingIntentCancelled, this, callbacks); mH.sendMessage(m); } + final AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class); + ami.remove(new PendingIntent(rec)); } private void handlePendingIntentCancelled(RemoteCallbackList<IResultReceiver> callbacks) { diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index b311233694ce..598a68e90aa8 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -134,12 +134,12 @@ class UserController implements Handler.Callback { static final int CONTINUE_USER_SWITCH_MSG = 20; static final int USER_SWITCH_TIMEOUT_MSG = 30; static final int START_PROFILES_MSG = 40; - static final int SYSTEM_USER_START_MSG = 50; - static final int SYSTEM_USER_CURRENT_MSG = 60; + static final int USER_START_MSG = 50; + static final int USER_CURRENT_MSG = 60; static final int FOREGROUND_PROFILE_CHANGED_MSG = 70; static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80; static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90; - static final int SYSTEM_USER_UNLOCK_MSG = 100; + static final int USER_UNLOCK_MSG = 100; static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110; static final int START_USER_SWITCH_FG_MSG = 120; @@ -369,16 +369,18 @@ class UserController implements Handler.Callback { } } - mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG, - userId, 0)); - Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null); - intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - mInjector.broadcastIntent(intent, null, resultTo, 0, null, null, - new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, - AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, - Binder.getCallingUid(), Binder.getCallingPid(), userId); + if (!mInjector.getUserManager().isPreCreated(userId)) { + mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG, + userId, 0)); + Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + mInjector.broadcastIntent(intent, null, resultTo, 0, null, null, + new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, + AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, + Binder.getCallingUid(), Binder.getCallingPid(), userId); + } } // We need to delay unlocking managed profiles until the parent user @@ -439,8 +441,7 @@ class UserController implements Handler.Callback { // Dispatch unlocked to system services; when fully dispatched, // that calls through to the next "unlocked" phase - mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss) - .sendToTarget(); + mHandler.obtainMessage(USER_UNLOCK_MSG, userId, 0, uss).sendToTarget(); }); return true; } @@ -556,6 +557,17 @@ class UserController implements Handler.Callback { } } + if (userInfo.preCreated) { + Slog.i(TAG, "Stopping pre-created user " + userInfo.toFullString()); + // Pre-created user was started right after creation so services could properly + // intialize it; it should be stopped right away as it's not really a "real" user. + // TODO(b/140750212): in the long-term, we should add a onCreateUser() callback + // on SystemService instead. + stopUser(userInfo.id, /* force= */ true, /* stopUserCallback= */ null, + /* keyEvictedCallback= */ null); + return; + } + // Spin up app widgets prior to boot-complete, so they can be ready promptly mInjector.startUserWidgets(userId); @@ -808,7 +820,8 @@ class UserController implements Handler.Callback { mInjector.systemServiceManagerCleanupUser(userId); mInjector.stackSupervisorRemoveUser(userId); // Remove the user if it is ephemeral. - if (getUserInfo(userId).isEphemeral()) { + UserInfo userInfo = getUserInfo(userId); + if (userInfo.isEphemeral() && !userInfo.preCreated) { mInjector.getUserManager().removeUserEvenWhenDisallowed(userId); } @@ -985,11 +998,13 @@ class UserController implements Handler.Callback { * <ul> * <li>{@link Intent#ACTION_USER_STARTED} - sent to registered receivers of the new user * <li>{@link Intent#ACTION_USER_BACKGROUND} - sent to registered receivers of the outgoing - * user and all profiles of this user. Sent only if {@code foreground} parameter is true + * user and all profiles of this user. Sent only if {@code foreground} parameter is + * {@code false} * <li>{@link Intent#ACTION_USER_FOREGROUND} - sent to registered receivers of the new - * user and all profiles of this user. Sent only if {@code foreground} parameter is true + * user and all profiles of this user. Sent only if {@code foreground} parameter is + * {@code true} * <li>{@link Intent#ACTION_USER_SWITCHED} - sent to registered receivers of the new user. - * Sent only if {@code foreground} parameter is true + * Sent only if {@code foreground} parameter is {@code true} * <li>{@link Intent#ACTION_USER_STARTING} - ordered broadcast sent to registered receivers * of the new fg user * <li>{@link Intent#ACTION_LOCKED_BOOT_COMPLETED} - ordered broadcast sent to receivers of @@ -1063,6 +1078,11 @@ class UserController implements Handler.Callback { return false; } + if (foreground && userInfo.preCreated) { + Slog.w(TAG, "Cannot start pre-created user #" + userId + " as foreground"); + return false; + } + if (foreground && mUserSwitchUiEnabled) { mInjector.getWindowManager().startFreezingScreen( R.anim.screen_user_exit, R.anim.screen_user_enter); @@ -1157,13 +1177,11 @@ class UserController implements Handler.Callback { // Booting up a new user, need to tell system services about it. // Note that this is on the same handler as scheduling of broadcasts, // which is important because it needs to go first. - mHandler.sendMessage( - mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0)); + mHandler.sendMessage(mHandler.obtainMessage(USER_START_MSG, userId, 0)); } if (foreground) { - mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId, - oldUserId)); + mHandler.sendMessage(mHandler.obtainMessage(USER_CURRENT_MSG, userId, oldUserId)); mHandler.removeMessages(REPORT_USER_SWITCH_MSG); mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG, @@ -1172,6 +1190,10 @@ class UserController implements Handler.Callback { oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS); } + if (userInfo.preCreated) { + needStart = false; + } + if (needStart) { // Send USER_STARTED broadcast Intent intent = new Intent(Intent.ACTION_USER_STARTED); @@ -2129,13 +2151,13 @@ class UserController implements Handler.Callback { case START_PROFILES_MSG: startProfiles(); break; - case SYSTEM_USER_START_MSG: + case USER_START_MSG: mInjector.batteryStatsServiceNoteEvent( BatteryStats.HistoryItem.EVENT_USER_RUNNING_START, Integer.toString(msg.arg1), msg.arg1); mInjector.getSystemServiceManager().startUser(msg.arg1); break; - case SYSTEM_USER_UNLOCK_MSG: + case USER_UNLOCK_MSG: final int userId = msg.arg1; mInjector.getSystemServiceManager().unlockUser(userId); // Loads recents on a worker thread that allows disk I/O @@ -2144,7 +2166,7 @@ class UserController implements Handler.Callback { }); finishUserUnlocked((UserState) msg.obj); break; - case SYSTEM_USER_CURRENT_MSG: + case USER_CURRENT_MSG: mInjector.batteryStatsServiceNoteEvent( BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH, Integer.toString(msg.arg2), msg.arg2); diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java index 98f5557903d6..0ec4454cc4ea 100644 --- a/services/core/java/com/android/server/am/UserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java @@ -24,6 +24,7 @@ import android.os.Handler; import android.os.Message; import android.os.UserHandle; import android.os.UserManager; +import android.util.Slog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewTreeObserver; @@ -46,6 +47,9 @@ class UserSwitchingDialog extends AlertDialog // Time to wait for the onWindowShown() callback before continuing the user switch private static final int WINDOW_SHOWN_TIMEOUT_MS = 3000; + // User switching doesn't happen that frequently, so it doesn't hurt to have it always on + protected static final boolean DEBUG = true; + private final ActivityManagerService mService; private final int mUserId; private static final int MSG_START_USER = 1; @@ -118,7 +122,7 @@ class UserSwitchingDialog extends AlertDialog @Override public void show() { - // Slog.v(TAG, "show called"); + if (DEBUG) Slog.d(TAG, "show called"); super.show(); final View decorView = getWindow().getDecorView(); if (decorView != null) { @@ -132,13 +136,14 @@ class UserSwitchingDialog extends AlertDialog @Override public void onWindowShown() { - // Slog.v(TAG, "onWindowShown called"); + if (DEBUG) Slog.d(TAG, "onWindowShown called"); startUser(); } void startUser() { synchronized (this) { if (!mStartedUser) { + Slog.i(TAG, "starting user " + mUserId); mService.mUserController.startUserInForeground(mUserId); dismiss(); mStartedUser = true; @@ -147,6 +152,8 @@ class UserSwitchingDialog extends AlertDialog decorView.getViewTreeObserver().removeOnWindowShownListener(this); } mHandler.removeMessages(MSG_START_USER); + } else { + Slog.i(TAG, "user " + mUserId + " already started"); } } } @@ -156,6 +163,8 @@ class UserSwitchingDialog extends AlertDialog public void handleMessage(Message msg) { switch (msg.what) { case MSG_START_USER: + Slog.w(TAG, "user switch window not shown in " + + WINDOW_SHOWN_TIMEOUT_MS + " ms"); startUser(); break; } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 314e04c8da32..d45bc72aee11 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -1110,8 +1110,8 @@ public class AppOpsService extends IAppOpsService.Stub { return Collections.emptyList(); } synchronized (this) { - Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* edit */, - false /* uidMismatchExpected */); + Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* isPrivileged */, + false /* edit */); if (pkgOps == null) { return null; } @@ -1208,8 +1208,7 @@ public class AppOpsService extends IAppOpsService.Stub { private void pruneOp(Op op, int uid, String packageName) { if (!op.hasAnyTime()) { - Ops ops = getOpsRawLocked(uid, packageName, false /* edit */, - false /* uidMismatchExpected */); + Ops ops = getOpsRawLocked(uid, packageName, false /* isPrivileged */, false /* edit */); if (ops != null) { ops.remove(op.op); if (ops.size() <= 0) { @@ -1409,11 +1408,6 @@ public class AppOpsService extends IAppOpsService.Stub { } } - @Override - public void setMode(int code, int uid, String packageName, int mode) { - setMode(code, uid, packageName, mode, true, false); - } - /** * Sets the mode for a certain op and uid. * @@ -1421,19 +1415,25 @@ public class AppOpsService extends IAppOpsService.Stub { * @param uid The UID for which to set * @param packageName The package for which to set * @param mode The new mode to set - * @param verifyUid Iff {@code true}, check that the package name belongs to the uid - * @param isPrivileged Whether the package is privileged. (Only used if {@code verifyUid == - * false}) */ - private void setMode(int code, int uid, @NonNull String packageName, int mode, - boolean verifyUid, boolean isPrivileged) { + @Override + public void setMode(int code, int uid, @NonNull String packageName, int mode) { enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid); verifyIncomingOp(code); ArraySet<ModeCallback> repCbs = null; code = AppOpsManager.opToSwitch(code); + + boolean isPrivileged; + try { + isPrivileged = verifyAndGetIsPrivileged(uid, packageName); + } catch (SecurityException e) { + Slog.e(TAG, "Cannot setMode", e); + return; + } + synchronized (this) { UidState uidState = getUidStateLocked(uid, false); - Op op = getOpLocked(code, uid, packageName, true, verifyUid, isPrivileged); + Op op = getOpLocked(code, uid, packageName, isPrivileged, true); if (op != null) { if (op.mode != mode) { op.mode = mode; @@ -1799,34 +1799,32 @@ public class AppOpsService extends IAppOpsService.Stub { } /** - * @see #checkOperationUnchecked(int, int, String, boolean, boolean) - */ - private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName, - boolean raw) { - return checkOperationUnchecked(code, uid, packageName, raw, true); - } - - /** * Get the mode of an app-op. * * @param code The code of the op * @param uid The uid of the package the op belongs to * @param packageName The package the op belongs to * @param raw If the raw state of eval-ed state should be checked. - * @param verify If the code should check the package belongs to the uid * * @return The mode of the op */ private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName, - boolean raw, boolean verify) { + boolean raw) { if (isOpRestrictedDueToSuspend(code, packageName, uid)) { return AppOpsManager.MODE_IGNORED; } + + boolean isPrivileged; + + try { + isPrivileged = verifyAndGetIsPrivileged(uid, packageName); + } catch (SecurityException e) { + Slog.e(TAG, "checkOperation", e); + return AppOpsManager.opToDefaultMode(code); + } + synchronized (this) { - if (verify) { - checkPackage(uid, packageName); - } - if (isOpRestrictedLocked(uid, code, packageName)) { + if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) { return AppOpsManager.MODE_IGNORED; } code = AppOpsManager.opToSwitch(code); @@ -1836,7 +1834,7 @@ public class AppOpsService extends IAppOpsService.Stub { final int rawMode = uidState.opModes.get(code); return raw ? rawMode : uidState.evalMode(code, rawMode); } - Op op = getOpLocked(code, uid, packageName, false, verify, false); + Op op = getOpLocked(code, uid, packageName, false, false); if (op == null) { return AppOpsManager.opToDefaultMode(code); } @@ -1941,14 +1939,12 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public int checkPackage(int uid, String packageName) { Preconditions.checkNotNull(packageName); - synchronized (this) { - Ops ops = getOpsRawLocked(uid, packageName, true /* edit */, - true /* uidMismatchExpected */); - if (ops != null) { - return AppOpsManager.MODE_ALLOWED; - } else { - return AppOpsManager.MODE_ERRORED; - } + try { + verifyAndGetIsPrivileged(uid, packageName); + + return AppOpsManager.MODE_ALLOWED; + } catch (SecurityException ignored) { + return AppOpsManager.MODE_ERRORED; } } @@ -2011,9 +2007,16 @@ public class AppOpsService extends IAppOpsService.Stub { private int noteOperationUnchecked(int code, int uid, String packageName, int proxyUid, String proxyPackageName, @OpFlags int flags) { + boolean isPrivileged; + try { + isPrivileged = verifyAndGetIsPrivileged(uid, packageName); + } catch (SecurityException e) { + Slog.e(TAG, "noteOperation", e); + return AppOpsManager.MODE_ERRORED; + } + synchronized (this) { - final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */, - false /* uidMismatchExpected */); + final Ops ops = getOpsRawLocked(uid, packageName, isPrivileged, true /* edit */); if (ops == null) { scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); @@ -2022,7 +2025,7 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_ERRORED; } final Op op = getOpLocked(ops, code, true); - if (isOpRestrictedLocked(uid, code, packageName)) { + if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) { scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED); return AppOpsManager.MODE_IGNORED; @@ -2181,16 +2184,25 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_IGNORED; } ClientState client = (ClientState)token; + + boolean isPrivileged; + try { + isPrivileged = verifyAndGetIsPrivileged(uid, packageName); + } catch (SecurityException e) { + Slog.e(TAG, "startOperation", e); + return AppOpsManager.MODE_ERRORED; + } + synchronized (this) { - final Ops ops = getOpsRawLocked(uid, resolvedPackageName, true /* edit */, - false /* uidMismatchExpected */); + final Ops ops = getOpsRawLocked(uid, resolvedPackageName, isPrivileged, + true /* edit */); if (ops == null) { if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid + " package " + resolvedPackageName); return AppOpsManager.MODE_ERRORED; } final Op op = getOpLocked(ops, code, true); - if (isOpRestrictedLocked(uid, code, resolvedPackageName)) { + if (isOpRestrictedLocked(uid, code, resolvedPackageName, isPrivileged)) { return AppOpsManager.MODE_IGNORED; } final int switchCode = AppOpsManager.opToSwitch(code); @@ -2262,8 +2274,17 @@ public class AppOpsService extends IAppOpsService.Stub { return; } ClientState client = (ClientState) token; + + boolean isPrivileged; + try { + isPrivileged = verifyAndGetIsPrivileged(uid, packageName); + } catch (SecurityException e) { + Slog.e(TAG, "Cannot finishOperation", e); + return; + } + synchronized (this) { - Op op = getOpLocked(code, uid, resolvedPackageName, true, true, false); + Op op = getOpLocked(code, uid, resolvedPackageName, isPrivileged, true); if (op == null) { return; } @@ -2513,8 +2534,76 @@ public class AppOpsService extends IAppOpsService.Stub { uidState.pendingStateCommitTime = 0; } - private Ops getOpsRawLocked(int uid, String packageName, boolean edit, - boolean uidMismatchExpected) { + /** + * Verify that package belongs to uid and return whether the package is privileged. + * + * @param uid The uid the package belongs to + * @param packageName The package the might belong to the uid + * + * @return {@code true} iff the package is privileged + */ + private boolean verifyAndGetIsPrivileged(int uid, String packageName) { + if (uid == Process.ROOT_UID) { + // For backwards compatibility, don't check package name for root UID. + return false; + } + + // Do not check if uid/packageName is already known + synchronized (this) { + UidState uidState = mUidStates.get(uid); + if (uidState != null && uidState.pkgOps != null) { + Ops ops = uidState.pkgOps.get(packageName); + + if (ops != null) { + return ops.isPrivileged; + } + } + } + + boolean isPrivileged = false; + final long ident = Binder.clearCallingIdentity(); + try { + int pkgUid; + + ApplicationInfo appInfo = LocalServices.getService(PackageManagerInternal.class) + .getApplicationInfo(packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS + | PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_INSTANT, + Process.SYSTEM_UID, UserHandle.getUserId(uid)); + if (appInfo != null) { + pkgUid = appInfo.uid; + isPrivileged = (appInfo.privateFlags + & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; + } else { + pkgUid = resolveUid(packageName); + if (pkgUid >= 0) { + isPrivileged = false; + } + } + if (pkgUid != uid) { + throw new SecurityException("Specified package " + packageName + " under uid " + uid + + " but it is really " + pkgUid); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + + return isPrivileged; + } + + /** + * Get (and potentially create) ops. + * + * @param uid The uid the package belongs to + * @param packageName The name of the package + * @param isPrivileged If the package is privilidged (ignored if {@code edit} is false) + * @param edit If an ops does not exist, create the ops? + + * @return + */ + private Ops getOpsRawLocked(int uid, String packageName, boolean isPrivileged, boolean edit) { UidState uidState = getUidStateLocked(uid, edit); if (uidState == null) { return null; @@ -2532,47 +2621,6 @@ public class AppOpsService extends IAppOpsService.Stub { if (!edit) { return null; } - boolean isPrivileged = false; - // This is the first time we have seen this package name under this uid, - // so let's make sure it is valid. - if (uid != 0) { - final long ident = Binder.clearCallingIdentity(); - try { - int pkgUid = -1; - try { - ApplicationInfo appInfo = ActivityThread.getPackageManager() - .getApplicationInfo(packageName, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - UserHandle.getUserId(uid)); - if (appInfo != null) { - pkgUid = appInfo.uid; - isPrivileged = (appInfo.privateFlags - & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; - } else { - pkgUid = resolveUid(packageName); - if (pkgUid >= 0) { - isPrivileged = false; - } - } - } catch (RemoteException e) { - Slog.w(TAG, "Could not contact PackageManager", e); - } - if (pkgUid != uid) { - // Oops! The package name is not valid for the uid they are calling - // under. Abort. - if (!uidMismatchExpected) { - RuntimeException ex = new RuntimeException("here"); - ex.fillInStackTrace(); - Slog.w(TAG, "Bad call: specified package " + packageName - + " under uid " + uid + " but it is really " + pkgUid, ex); - } - return null; - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } ops = new Ops(packageName, uidState, isPrivileged); uidState.pkgOps.put(packageName, ops); } @@ -2580,7 +2628,7 @@ public class AppOpsService extends IAppOpsService.Stub { } /** - * Get the state of all ops for a package, <b>don't verify that package belongs to uid</b>. + * Get the state of all ops for a package. * * <p>Usually callers should use {@link #getOpLocked} and not call this directly. * @@ -2638,23 +2686,15 @@ public class AppOpsService extends IAppOpsService.Stub { * @param code The code of the op * @param uid The uid the of the package * @param packageName The package name for which to get the state for + * @param isPrivileged Whether the package is privileged or not (only used if {@code edit + * == true}) * @param edit Iff {@code true} create the {@link Op} object if not yet created - * @param verifyUid Iff {@code true} check that the package belongs to the uid - * @param isPrivileged Whether the package is privileged or not (only used if {@code verifyUid - * == false}) * * @return The {@link Op state} of the op */ - private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, boolean edit, - boolean verifyUid, boolean isPrivileged) { - Ops ops; - - if (verifyUid) { - ops = getOpsRawLocked(uid, packageName, edit, false /* uidMismatchExpected */); - } else { - ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged); - } - + private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, + boolean isPrivileged, boolean edit) { + Ops ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged); if (ops == null) { return null; } @@ -2684,7 +2724,8 @@ public class AppOpsService extends IAppOpsService.Stub { return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid)); } - private boolean isOpRestrictedLocked(int uid, int code, String packageName) { + private boolean isOpRestrictedLocked(int uid, int code, String packageName, + boolean isPrivileged) { int userHandle = UserHandle.getUserId(uid); final int restrictionSetCount = mOpUserRestrictions.size(); @@ -2696,8 +2737,8 @@ public class AppOpsService extends IAppOpsService.Stub { if (AppOpsManager.opAllowSystemBypassRestriction(code)) { // If we are the system, bypass user restrictions for certain codes synchronized (this) { - Ops ops = getOpsRawLocked(uid, packageName, true /* edit */, - false /* uidMismatchExpected */); + Ops ops = getOpsRawLocked(uid, packageName, isPrivileged, + true /* edit */); if ((ops != null) && ops.isPrivileged) { return false; } @@ -3068,7 +3109,7 @@ public class AppOpsService extends IAppOpsService.Stub { out.attribute(null, "n", Integer.toString(pkg.getUid())); synchronized (this) { Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), - false /* edit */, false /* uidMismatchExpected */); + false /* isPrivileged */, false /* edit */); // Should always be present as the list of PackageOps is generated // from Ops. if (ops != null) { @@ -4647,18 +4688,8 @@ public class AppOpsService extends IAppOpsService.Stub { } @Override - public void setUidMode(int code, int uid, int mode) { - AppOpsService.this.setUidMode(code, uid, mode); - } - - @Override public void setAllPkgModesToDefault(int code, int uid) { AppOpsService.this.setAllPkgModesToDefault(code, uid); } - - @Override - public @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName) { - return AppOpsService.this.checkOperationUnchecked(code, uid, packageName, true, false); - } } } diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING index a53797dfa9e0..1a5dac503463 100644 --- a/services/core/java/com/android/server/appop/TEST_MAPPING +++ b/services/core/java/com/android/server/appop/TEST_MAPPING @@ -10,6 +10,14 @@ "include-filter": "com.android.server.appop" } ] + }, + { + "name": "FrameworksMockingServicesTests", + "options": [ + { + "include-filter": "com.android.server.appop" + } + ] } ] } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 25fdf6447061..b41e60bab183 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -2814,6 +2814,7 @@ public class AudioService extends IAudioService.Stub } public void setMasterMute(boolean mute, int flags, String callingPackage, int userId) { + enforceModifyAudioRoutingPermission(); setMasterMuteInternal(mute, flags, callingPackage, Binder.getCallingUid(), userId); } @@ -6292,7 +6293,7 @@ public class AudioService extends IAudioService.Stub return false; } boolean suppress = false; - if (resolvedStream == DEFAULT_VOL_STREAM_NO_PLAYBACK && mController != null) { + if (resolvedStream != AudioSystem.STREAM_MUSIC && mController != null) { final long now = SystemClock.uptimeMillis(); if ((flags & AudioManager.FLAG_SHOW_UI) != 0 && !mVisible) { // ui will become visible diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java index 85ca627e3b02..2d96d8978e57 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java @@ -233,18 +233,24 @@ class RadioModule { } private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) { + List<TunerSession> deadSessions = null; for (TunerSession tunerSession : mAidlTunerSessions) { try { runnable.run(tunerSession.mCallback); } catch (DeadObjectException ex) { - // The other side died without calling close(), so just purge it from our - // records. + // The other side died without calling close(), so just purge it from our records. Slog.e(TAG, "Removing dead TunerSession"); - mAidlTunerSessions.remove(tunerSession); + if (deadSessions == null) { + deadSessions = new ArrayList<>(); + } + deadSessions.add(tunerSession); } catch (RemoteException ex) { Slog.e(TAG, "Failed to invoke ITunerCallback: ", ex); } } + if (deadSessions != null) { + mAidlTunerSessions.removeAll(deadSessions); + } } public android.hardware.radio.ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes, diff --git a/services/core/java/com/android/server/connectivity/DataConnectionStats.java b/services/core/java/com/android/server/connectivity/DataConnectionStats.java index 1b1c54682255..5010e46a74eb 100644 --- a/services/core/java/com/android/server/connectivity/DataConnectionStats.java +++ b/services/core/java/com/android/server/connectivity/DataConnectionStats.java @@ -16,6 +16,9 @@ package com.android.server.connectivity; +import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; +import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -24,6 +27,7 @@ import android.net.ConnectivityManager; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; +import android.telephony.NetworkRegistrationInfo; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -91,7 +95,10 @@ public class DataConnectionStats extends BroadcastReceiver { boolean visible = (simReadyOrUnknown || isCdma()) // we only check the sim state for GSM && hasService() && mDataState == TelephonyManager.DATA_CONNECTED; - int networkType = mServiceState.getDataNetworkType(); + NetworkRegistrationInfo regInfo = + mServiceState.getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN); + int networkType = regInfo == null ? TelephonyManager.NETWORK_TYPE_UNKNOWN + : regInfo.getAccessNetworkTechnology(); if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible", networkType, visible ? "" : "not ")); try { diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java index 6fa999cb039a..04c792ae482b 100644 --- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java +++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java @@ -26,6 +26,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN; @@ -46,19 +47,18 @@ import android.net.NetworkIdentity; import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.net.NetworkRequest; +import android.net.NetworkSpecifier; import android.net.NetworkStats; import android.net.NetworkTemplate; -import android.net.StringNetworkSpecifier; +import android.net.TelephonyNetworkSpecifier; +import android.net.Uri; import android.os.BestClock; import android.os.Handler; import android.os.SystemClock; -import android.net.Uri; import android.os.UserHandle; import android.provider.Settings; import android.telephony.TelephonyManager; -import android.util.DataUnit; import android.util.DebugUtils; -import android.util.Pair; import android.util.Range; import android.util.Slog; @@ -74,7 +74,6 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; -import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -185,7 +184,6 @@ public class MultipathPolicyTracker { // Track information on mobile networks as they come and go. class MultipathTracker { final Network network; - final int subId; final String subscriberId; private long mQuota; @@ -198,13 +196,14 @@ public class MultipathPolicyTracker { public MultipathTracker(Network network, NetworkCapabilities nc) { this.network = network; this.mNetworkCapabilities = new NetworkCapabilities(nc); - try { - subId = Integer.parseInt( - ((StringNetworkSpecifier) nc.getNetworkSpecifier()).toString()); - } catch (ClassCastException | NullPointerException | NumberFormatException e) { + NetworkSpecifier specifier = nc.getNetworkSpecifier(); + int subId = INVALID_SUBSCRIPTION_ID; + if (specifier instanceof TelephonyNetworkSpecifier) { + subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId(); + } else { throw new IllegalStateException(String.format( - "Can't get subId from mobile network %s (%s): %s", - network, nc, e.getMessage())); + "Can't get subId from mobile network %s (%s)", + network, nc)); } TelephonyManager tele = mContext.getSystemService(TelephonyManager.class); diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 21795184b1bd..2c415570d5fa 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -28,7 +28,7 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.net.NetworkSpecifier; -import android.net.StringNetworkSpecifier; +import android.net.TelephonyNetworkSpecifier; import android.net.wifi.WifiInfo; import android.os.UserHandle; import android.telephony.SubscriptionManager; @@ -223,14 +223,8 @@ public class NetworkNotificationManager { // name has been added to it NetworkSpecifier specifier = nai.networkCapabilities.getNetworkSpecifier(); int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; - if (specifier instanceof StringNetworkSpecifier) { - try { - subId = Integer.parseInt( - ((StringNetworkSpecifier) specifier).specifier); - } catch (NumberFormatException e) { - Slog.e(TAG, "NumberFormatException on " - + ((StringNetworkSpecifier) specifier).specifier); - } + if (specifier instanceof TelephonyNetworkSpecifier) { + subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId(); } details = mTelephonyManager.createForSubscriptionId(subId) diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 31632dc007a5..177e2d8b5fa2 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -41,6 +41,7 @@ import android.util.MathUtils; import android.util.Slog; import android.util.TimeUtils; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.server.EventLogTags; @@ -215,7 +216,26 @@ class AutomaticBrightnessController { private IActivityTaskManager mActivityTaskManager; private PackageManager mPackageManager; - public AutomaticBrightnessController(Callbacks callbacks, Looper looper, + private final Injector mInjector; + + AutomaticBrightnessController(Callbacks callbacks, Looper looper, + SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper, + int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor, + int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, + long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, + HysteresisLevels ambientBrightnessThresholds, + HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout, + PackageManager packageManager) { + this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper, + lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor, + lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig, + darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig, + ambientBrightnessThresholds, screenBrightnessThresholds, shortTermModelTimeout, + packageManager); + } + + @VisibleForTesting + AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper, SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper, int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, @@ -223,6 +243,7 @@ class AutomaticBrightnessController { HysteresisLevels ambientBrightnessThresholds, HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout, PackageManager packageManager) { + mInjector = injector; mCallbacks = callbacks; mSensorManager = sensorManager; mBrightnessMapper = mapper; @@ -725,8 +746,8 @@ class AutomaticBrightnessController { float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName, mForegroundAppCategory); - int newScreenAutoBrightness = - clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON)); + int newScreenAutoBrightness = Math.round(clampScreenBrightness( + value * PowerManager.BRIGHTNESS_ON)); // If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold, // in which case we ignore the new screen brightness if it doesn't differ enough from the @@ -750,10 +771,10 @@ class AutomaticBrightnessController { } mScreenAutoBrightness = newScreenAutoBrightness; - mScreenBrighteningThreshold = - mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness); - mScreenDarkeningThreshold = - mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness); + mScreenBrighteningThreshold = clampScreenBrightness( + mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness)); + mScreenDarkeningThreshold = clampScreenBrightness( + mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness)); if (sendUpdate) { mCallbacks.updateBrightness(); @@ -761,7 +782,7 @@ class AutomaticBrightnessController { } } - private int clampScreenBrightness(int value) { + private float clampScreenBrightness(float value) { return MathUtils.constrain(value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum); } @@ -839,7 +860,7 @@ class AutomaticBrightnessController { } // The ActivityTaskManager's lock tends to get contended, so this is done in a background // thread and applied via this thread's handler synchronously. - BackgroundThread.getHandler().post(new Runnable() { + mInjector.getBackgroundThreadHandler().post(new Runnable() { public void run() { try { // The foreground app is the top activity of the focused tasks stack. @@ -965,6 +986,9 @@ class AutomaticBrightnessController { private int mCount; public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) { + if (lightSensorRate <= 0) { + throw new IllegalArgumentException("lightSensorRate must be above 0"); + } mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate); mRingLux = new float[mCapacity]; mRingTime = new long[mCapacity]; @@ -1076,4 +1100,10 @@ class AutomaticBrightnessController { return index; } } + + public static class Injector { + public Handler getBackgroundThreadHandler() { + return BackgroundThread.getHandler(); + } + } } diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 126beeffbb96..765fdb63bc7d 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -94,6 +94,8 @@ public class BrightnessTracker { static final String TAG = "BrightnessTracker"; static final boolean DEBUG = false; + @VisibleForTesting + static final boolean ENABLE_COLOR_SAMPLING = false; private static final String EVENTS_FILE = "brightness_events.xml"; private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml"; @@ -757,7 +759,8 @@ public class BrightnessTracker { } private void enableColorSampling() { - if (!mInjector.isBrightnessModeAutomatic(mContentResolver) + if (!ENABLE_COLOR_SAMPLING + || !mInjector.isBrightnessModeAutomatic(mContentResolver) || !mInjector.isInteractive(mContext) || mColorSamplingEnabled) { return; diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 1fc0db3ff7cb..6dcfadef5f8c 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -76,7 +76,7 @@ public class DisplayModeDirector { private static final int GLOBAL_ID = -1; // The tolerance within which we consider something approximately equals. - private static final float EPSILON = 0.001f; + private static final float EPSILON = 0.01f; private final Object mLock = new Object(); private final Context mContext; diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java index 2db1d03893d2..f0a505d4d818 100644 --- a/services/core/java/com/android/server/display/HysteresisLevels.java +++ b/services/core/java/com/android/server/display/HysteresisLevels.java @@ -18,13 +18,16 @@ package com.android.server.display; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; + import java.io.PrintWriter; import java.util.Arrays; /** * A helper class for handling access to illuminance hysteresis level values. */ -final class HysteresisLevels { +@VisibleForTesting +public class HysteresisLevels { private static final String TAG = "HysteresisLevels"; // Default hysteresis constraints for brightening or darkening. @@ -60,7 +63,7 @@ final class HysteresisLevels { /** * Return the brightening hysteresis threshold for the given value level. */ - float getBrighteningThreshold(float value) { + public float getBrighteningThreshold(float value) { float brightConstant = getReferenceLevel(value, mBrighteningThresholds); float brightThreshold = value * (1.0f + brightConstant); if (DEBUG) { @@ -73,7 +76,7 @@ final class HysteresisLevels { /** * Return the darkening hysteresis threshold for the given value level. */ - float getDarkeningThreshold(float value) { + public float getDarkeningThreshold(float value) { float darkConstant = getReferenceLevel(value, mDarkeningThresholds); float darkThreshold = value * (1.0f - darkConstant); if (DEBUG) { diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java index 0bf43b6d1b9c..2dc2cf0d8e90 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -589,16 +589,18 @@ public final class ColorDisplayService extends SystemService { if (immediate) { dtm.setColorMatrix(tintController.getLevel(), to); } else { - tintController.setAnimator(ValueAnimator.ofObject(COLOR_MATRIX_EVALUATOR, - from == null ? MATRIX_IDENTITY : from, to)); - tintController.getAnimator().setDuration(TRANSITION_DURATION); - tintController.getAnimator().setInterpolator(AnimationUtils.loadInterpolator( + TintValueAnimator valueAnimator = TintValueAnimator.ofMatrix(COLOR_MATRIX_EVALUATOR, + from == null ? MATRIX_IDENTITY : from, to); + tintController.setAnimator(valueAnimator); + valueAnimator.setDuration(TRANSITION_DURATION); + valueAnimator.setInterpolator(AnimationUtils.loadInterpolator( getContext(), android.R.interpolator.fast_out_slow_in)); - tintController.getAnimator().addUpdateListener((ValueAnimator animator) -> { + valueAnimator.addUpdateListener((ValueAnimator animator) -> { final float[] value = (float[]) animator.getAnimatedValue(); dtm.setColorMatrix(tintController.getLevel(), value); + ((TintValueAnimator) animator).updateMinMaxComponents(); }); - tintController.getAnimator().addListener(new AnimatorListenerAdapter() { + valueAnimator.addListener(new AnimatorListenerAdapter() { private boolean mIsCancelled; @@ -609,9 +611,14 @@ public final class ColorDisplayService extends SystemService { @Override public void onAnimationEnd(Animator animator) { + TintValueAnimator t = (TintValueAnimator) animator; Slog.d(TAG, tintController.getClass().getSimpleName() + " Animation cancelled: " + mIsCancelled - + " to matrix: " + TintController.matrixToString(to, 16)); + + " to matrix: " + TintController.matrixToString(to, 16) + + " min matrix coefficients: " + + TintController.matrixToString(t.getMin(), 16) + + " max matrix coefficients: " + + TintController.matrixToString(t.getMax(), 16)); if (!mIsCancelled) { // Ensure final color matrix is set at the end of the animation. If the // animation is cancelled then don't set the final color matrix so the new @@ -621,7 +628,7 @@ public final class ColorDisplayService extends SystemService { tintController.setAnimator(null); } }); - tintController.getAnimator().start(); + valueAnimator.start(); } } @@ -1109,6 +1116,51 @@ public final class ColorDisplayService extends SystemService { } /** + * Only animates matrices and saves min and max coefficients for logging. + */ + static class TintValueAnimator extends ValueAnimator { + private float[] min; + private float[] max; + + public static TintValueAnimator ofMatrix(ColorMatrixEvaluator evaluator, + Object... values) { + TintValueAnimator anim = new TintValueAnimator(); + anim.setObjectValues(values); + anim.setEvaluator(evaluator); + if (values == null || values.length == 0) { + return null; + } + float[] m = (float[]) values[0]; + anim.min = new float[m.length]; + anim.max = new float[m.length]; + for (int i = 0; i < m.length; ++i) { + anim.min[i] = Float.MAX_VALUE; + anim.max[i] = Float.MIN_VALUE; + } + return anim; + } + + public void updateMinMaxComponents() { + float[] value = (float[]) getAnimatedValue(); + if (value == null) { + return; + } + for (int i = 0; i < value.length; ++i) { + min[i] = Math.min(min[i], value[i]); + max[i] = Math.max(max[i], value[i]); + } + } + + public float[] getMin() { + return min; + } + + public float[] getMax() { + return max; + } + } + + /** * Interpolates between two 4x4 color transform matrices (in column-major order). */ private static class ColorMatrixEvaluator implements TypeEvaluator<float[]> { diff --git a/services/core/java/com/android/server/display/color/TintController.java b/services/core/java/com/android/server/display/color/TintController.java index 8d8b9b2af04e..422dd328d2b6 100644 --- a/services/core/java/com/android/server/display/color/TintController.java +++ b/services/core/java/com/android/server/display/color/TintController.java @@ -24,14 +24,14 @@ import java.io.PrintWriter; abstract class TintController { - private ValueAnimator mAnimator; + private ColorDisplayService.TintValueAnimator mAnimator; private Boolean mIsActivated; - public ValueAnimator getAnimator() { + public ColorDisplayService.TintValueAnimator getAnimator() { return mAnimator; } - public void setAnimator(ValueAnimator animator) { + public void setAnimator(ColorDisplayService.TintValueAnimator animator) { mAnimator = animator; } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index b33870559f59..7e6e6680905e 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -221,6 +221,8 @@ public class InputManagerService extends IInputManager.Stub private static native void nativeSetFocusedApplication(long ptr, int displayId, InputApplicationHandle application); private static native void nativeSetFocusedDisplay(long ptr, int displayId); + private static native boolean nativeTransferTouchFocus(long ptr, + InputChannel fromChannel, InputChannel toChannel); private static native void nativeSetPointerSpeed(long ptr, int speed); private static native void nativeSetShowTouches(long ptr, boolean enabled); private static native void nativeSetInteractive(long ptr, boolean interactive); @@ -1543,6 +1545,29 @@ public class InputManagerService extends IInputManager.Stub nativeSetSystemUiVisibility(mPtr, visibility); } + /** + * Atomically transfers touch focus from one window to another as identified by + * their input channels. It is possible for multiple windows to have + * touch focus if they support split touch dispatch + * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this + * method only transfers touch focus of the specified window without affecting + * other windows that may also have touch focus at the same time. + * @param fromChannel The channel of a window that currently has touch focus. + * @param toChannel The channel of the window that should receive touch focus in + * place of the first. + * @return True if the transfer was successful. False if the window with the + * specified channel did not actually have touch focus at the time of the request. + */ + public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) { + if (fromChannel == null) { + throw new IllegalArgumentException("fromChannel must not be null."); + } + if (toChannel == null) { + throw new IllegalArgumentException("toChannel must not be null."); + } + return nativeTransferTouchFocus(mPtr, fromChannel, toChannel); + } + @Override // Binder call public void tryPointerSpeed(int speed) { if (!checkCallingPermission(android.Manifest.permission.SET_POINTER_SPEED, diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 63302705b36f..69cbc22f41bf 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3454,7 +3454,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } /** - * This is kept due to {@link android.annotation.UnsupportedAppUsage} in + * This is kept due to {@link android.compat.annotation.UnsupportedAppUsage} in * {@link InputMethodManager#getInputMethodWindowVisibleHeight()} and a dependency in * {@link InputMethodService#onCreate()}. * diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java index ababad961c2d..97df55706bf8 100644 --- a/services/core/java/com/android/server/job/controllers/TimeController.java +++ b/services/core/java/com/android/server/job/controllers/TimeController.java @@ -325,8 +325,7 @@ public final class TimeController extends StateController { && !wouldBeReadyWithConstraintLocked( job, JobStatus.CONSTRAINT_TIMING_DELAY)) { if (DEBUG) { - Slog.i(TAG, - "Skipping " + job + " because delay won't make it ready."); + Slog.i(TAG, "Skipping " + job + " because delay won't make it ready."); } continue; } @@ -385,7 +384,8 @@ public final class TimeController extends StateController { /** * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's * delay will expire. - * This alarm <b>will</b> wake up the phone. + * This alarm <b>will not</b> wake up the phone if + * {@link TcConstants#USE_NON_WAKEUP_ALARM_FOR_DELAY} is true. */ private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) { alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis); @@ -393,8 +393,11 @@ public final class TimeController extends StateController { return; } mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis; - updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener, - mNextDelayExpiredElapsedMillis, ws); + final int alarmType = + mTcConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY + ? AlarmManager.ELAPSED_REALTIME : AlarmManager.ELAPSED_REALTIME_WAKEUP; + updateAlarmWithListenerLocked(DELAY_TAG, alarmType, + mNextDelayExpiredListener, mNextDelayExpiredElapsedMillis, ws); } /** @@ -408,16 +411,16 @@ public final class TimeController extends StateController { return; } mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis; - updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener, - mNextJobExpiredElapsedMillis, ws); + updateAlarmWithListenerLocked(DEADLINE_TAG, AlarmManager.ELAPSED_REALTIME_WAKEUP, + mDeadlineExpiredListener, mNextJobExpiredElapsedMillis, ws); } private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) { return Math.max(proposedAlarmTimeElapsedMillis, sElapsedRealtimeClock.millis()); } - private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener, - long alarmTimeElapsed, WorkSource ws) { + private void updateAlarmWithListenerLocked(String tag, @AlarmManager.AlarmType int alarmType, + OnAlarmListener listener, long alarmTimeElapsed, WorkSource ws) { ensureAlarmServiceLocked(); if (alarmTimeElapsed == Long.MAX_VALUE) { mAlarmService.cancel(listener); @@ -425,7 +428,7 @@ public final class TimeController extends StateController { if (DEBUG) { Slog.d(TAG, "Setting " + tag + " for: " + alarmTimeElapsed); } - mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsed, + mAlarmService.set(alarmType, alarmTimeElapsed, AlarmManager.WINDOW_HEURISTIC, 0, tag, listener, null, ws); } } @@ -464,8 +467,11 @@ public final class TimeController extends StateController { private final KeyValueListParser mParser = new KeyValueListParser(','); private static final String KEY_SKIP_NOT_READY_JOBS = "skip_not_ready_jobs"; + private static final String KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY = + "use_non_wakeup_delay_alarm"; private static final boolean DEFAULT_SKIP_NOT_READY_JOBS = true; + private static final boolean DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY = true; /** * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't @@ -474,6 +480,12 @@ public final class TimeController extends StateController { public boolean SKIP_NOT_READY_JOBS = DEFAULT_SKIP_NOT_READY_JOBS; /** + * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't + * ready now. + */ + public boolean USE_NON_WAKEUP_ALARM_FOR_DELAY = DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY; + + /** * Creates a content observer. * * @param handler The handler to run {@link #onChange} on, or null if none. @@ -510,6 +522,12 @@ public final class TimeController extends StateController { recheckAlarmsLocked(); } } + + USE_NON_WAKEUP_ALARM_FOR_DELAY = mParser.getBoolean( + KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY, DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY); + // Intentionally not calling checkExpiredDelaysAndResetAlarm() here. There's no need to + // iterate through the entire list again for this constant change. The next delay alarm + // that is set will make use of the new constant value. } private void dump(IndentingPrintWriter pw) { @@ -517,12 +535,16 @@ public final class TimeController extends StateController { pw.println("TimeController:"); pw.increaseIndent(); pw.printPair(KEY_SKIP_NOT_READY_JOBS, SKIP_NOT_READY_JOBS).println(); + pw.printPair(KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY, + USE_NON_WAKEUP_ALARM_FOR_DELAY).println(); pw.decreaseIndent(); } private void dump(ProtoOutputStream proto) { final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER); proto.write(ConstantsProto.TimeController.SKIP_NOT_READY_JOBS, SKIP_NOT_READY_JOBS); + proto.write(ConstantsProto.TimeController.USE_NON_WAKEUP_ALARM_FOR_DELAY, + USE_NON_WAKEUP_ALARM_FOR_DELAY); proto.end(tcToken); } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 3fc843810313..8d0397ed9aec 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -75,6 +75,8 @@ import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; import com.android.internal.location.gnssmetrics.GnssMetrics; import com.android.internal.telephony.TelephonyIntents; +import com.android.server.DeviceIdleController; +import com.android.server.LocalServices; import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback; import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback; @@ -178,6 +180,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private static final int AGPS_SUPL_MODE_MSA = 0x02; private static final int AGPS_SUPL_MODE_MSB = 0x01; + private static final int UPDATE_LOW_POWER_MODE = 1; private static final int SET_REQUEST = 3; private static final int INJECT_NTP_TIME = 5; // PSDS stands for Predicted Satellite Data Service @@ -371,6 +374,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private boolean mDisableGpsForPowerManager = false; /** + * True if the device idle controller has determined that the device is stationary. This is only + * updated when the device enters idle mode. + */ + private volatile boolean mIsDeviceStationary = false; + + /** * Properties loaded from PROPERTIES_FILE. * It must be accessed only inside {@link #mHandler}. */ @@ -462,6 +471,15 @@ public class GnssLocationProvider extends AbstractLocationProvider implements public GnssNavigationMessageProvider getGnssNavigationMessageProvider() { return mGnssNavigationMessageProvider; } + + private final DeviceIdleController.StationaryListener mDeviceIdleStationaryListener = + isStationary -> { + mIsDeviceStationary = isStationary; + // Call updateLowPowerMode on handler thread so it's always called from the same + // thread. + mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE); + }; + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -478,11 +496,22 @@ public class GnssLocationProvider extends AbstractLocationProvider implements case ALARM_TIMEOUT: hibernate(); break; - case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED: case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED: + DeviceIdleController.LocalService deviceIdleService = LocalServices.getService( + DeviceIdleController.LocalService.class); + if (mPowerManager.isDeviceIdleMode()) { + deviceIdleService.registerStationaryListener(mDeviceIdleStationaryListener); + } else { + deviceIdleService.unregisterStationaryListener( + mDeviceIdleStationaryListener); + } + // Intentional fall-through. + case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED: case Intent.ACTION_SCREEN_OFF: case Intent.ACTION_SCREEN_ON: - updateLowPowerMode(); + // Call updateLowPowerMode on handler thread so it's always called from the + // same thread. + mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE); break; case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED: @@ -542,10 +571,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } private void updateLowPowerMode() { - // Disable GPS if we are in device idle mode. - boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode(); - final PowerSaveState result = - mPowerManager.getPowerSaveState(ServiceType.LOCATION); + // Disable GPS if we are in device idle mode and the device is stationary. + boolean disableGpsForPowerManager = mPowerManager.isDeviceIdleMode() && mIsDeviceStationary; + final PowerSaveState result = mPowerManager.getPowerSaveState(ServiceType.LOCATION); switch (result.locationMode) { case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF: case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF: @@ -2043,6 +2071,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements case REPORT_SV_STATUS: handleReportSvStatus((SvStatusInfo) msg.obj); break; + case UPDATE_LOW_POWER_MODE: + updateLowPowerMode(); + break; } if (msg.arg2 == 1) { // wakelock was taken for this message, release it diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java index ea4f9c456856..dd522b95a938 100644 --- a/services/core/java/com/android/server/location/GnssVisibilityControl.java +++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java @@ -637,7 +637,7 @@ class GnssVisibilityControl { return new Notification.Builder(context, SystemNotificationChannels.NETWORK_ALERTS) .setSmallIcon(com.android.internal.R.drawable.stat_sys_gps_on) .setWhen(0) - .setOngoing(true) + .setOngoing(false) .setAutoCancel(true) .setColor(context.getColor( com.android.internal.R.color.system_notification_accent_color)) diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 04b51a93b994..c60fed073a15 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -161,7 +161,7 @@ import android.net.NetworkStack; import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkTemplate; -import android.net.StringNetworkSpecifier; +import android.net.TelephonyNetworkSpecifier; import android.net.TrafficStats; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; @@ -2869,17 +2869,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override - public void onTetheringChanged(String iface, boolean tethering) { - // No need to enforce permission because setRestrictBackground() will do it. - synchronized (mUidRulesFirstLock) { - if (mRestrictBackground && tethering) { - Log.d(TAG, "Tethering on (" + iface +"); disable Data Saver"); - setRestrictBackground(false); - } - } - } - - @Override public void setRestrictBackground(boolean restrictBackground) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setRestrictBackground"); try { @@ -4547,7 +4536,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } case MSG_STATS_PROVIDER_LIMIT_REACHED: { mNetworkStats.forceUpdate(); + synchronized (mNetworkPoliciesSecondLock) { + // Some providers might hit the limit reached event prior to others. Thus, + // re-calculate and update interface quota for every provider is needed. + updateNetworkRulesNL(); updateNetworkEnabledNL(); updateNotificationsNL(); } @@ -4555,17 +4548,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } case MSG_LIMIT_REACHED: { final String iface = (String) msg.obj; - synchronized (mNetworkPoliciesSecondLock) { - if (mMeteredIfaces.contains(iface)) { - // force stats update to make sure we have - // numbers that caused alert to trigger. - mNetworkStats.forceUpdate(); - - updateNetworkEnabledNL(); - updateNotificationsNL(); + // fast return if not needed. + if (!mMeteredIfaces.contains(iface)) { + return true; } } + + // force stats update to make sure the service have the numbers that caused + // alert to trigger. + mNetworkStats.forceUpdate(); + + synchronized (mNetworkPoliciesSecondLock) { + updateNetworkRulesNL(); + updateNetworkEnabledNL(); + updateNotificationsNL(); + } return true; } case MSG_RESTRICT_BACKGROUND_CHANGED: { @@ -5304,16 +5302,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } private int parseSubId(NetworkState state) { - // TODO: moved to using a legitimate NetworkSpecifier instead of string parsing int subId = INVALID_SUBSCRIPTION_ID; if (state != null && state.networkCapabilities != null && state.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { NetworkSpecifier spec = state.networkCapabilities.getNetworkSpecifier(); - if (spec instanceof StringNetworkSpecifier) { - try { - subId = Integer.parseInt(((StringNetworkSpecifier) spec).specifier); - } catch (NumberFormatException e) { - } + if (spec instanceof TelephonyNetworkSpecifier) { + subId = ((TelephonyNetworkSpecifier) spec).getSubscriptionId(); } } return subId; diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java index f295ed37939c..7098435ca299 100644 --- a/services/core/java/com/android/server/notification/NotificationComparator.java +++ b/services/core/java/com/android/server/notification/NotificationComparator.java @@ -123,6 +123,12 @@ public class NotificationComparator return -1 * Integer.compare(leftPriority, rightPriority); } + final boolean leftInterruptive = left.isInterruptive(); + final boolean rightInterruptive = right.isInterruptive(); + if (leftInterruptive != rightInterruptive) { + return -1 * Boolean.compare(leftInterruptive, rightInterruptive); + } + // then break ties by time, most recent first return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs()); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6f45825e81fe..0fe0971409e3 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -163,6 +163,7 @@ import android.os.IDeviceIdleController; import android.os.IInterface; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; @@ -281,6 +282,9 @@ public class NotificationManagerService extends SystemService { public static final boolean ENABLE_CHILD_NOTIFICATIONS = SystemProperties.getBoolean("debug.child_notifs", true); + // pullStats report request: undecorated remote view stats + public static final int REPORT_REMOTE_VIEWS = 0x01; + static final boolean DEBUG_INTERRUPTIVENESS = SystemProperties.getBoolean( "debug.notification.interruptiveness", false); @@ -3733,6 +3737,8 @@ public class NotificationManagerService extends SystemService { try { if (filter.stats) { dumpJson(pw, filter); + } else if (filter.rvStats) { + dumpRemoteViewStats(pw, filter); } else if (filter.proto) { dumpProto(fd, filter); } else if (filter.criticalPriority) { @@ -4209,6 +4215,49 @@ public class NotificationManagerService extends SystemService { new NotificationShellCmd(NotificationManagerService.this) .exec(this, in, out, err, args, callback, resultReceiver); } + + /** + * Get stats committed after startNs + * + * @param startNs Report stats committed after this time in nanoseconds. + * @param report Indicatess which section to include in the stats. + * @param doAgg Whether to aggregate the stats or keep them separated. + * @param out List of protos of individual commits or one representing the + * aggregate. + * @return the report time in nanoseconds, or 0 on error. + */ + @Override + public long pullStats(long startNs, int report, boolean doAgg, + List<ParcelFileDescriptor> out) { + checkCallerIsSystemOrShell(); + long startMs = TimeUnit.MILLISECONDS.convert(startNs, TimeUnit.NANOSECONDS); + + final long identity = Binder.clearCallingIdentity(); + try { + switch (report) { + case REPORT_REMOTE_VIEWS: + Slog.e(TAG, "pullStats REPORT_REMOTE_VIEWS from: " + + startMs + " wtih " + doAgg); + PulledStats stats = mUsageStats.remoteViewStats(startMs, doAgg); + if (stats != null) { + out.add(stats.toParcelFileDescriptor(report)); + Slog.e(TAG, "exiting pullStats with: " + out.size()); + long endNs = TimeUnit.NANOSECONDS + .convert(stats.endTimeMs(), TimeUnit.MILLISECONDS); + return endNs; + } + Slog.e(TAG, "null stats for: " + report); + } + } catch (IOException e) { + + Slog.e(TAG, "exiting pullStats: on error", e); + return 0; + } finally { + Binder.restoreCallingIdentity(identity); + } + Slog.e(TAG, "exiting pullStats: bad request"); + return 0; + } }; @VisibleForTesting @@ -4424,6 +4473,15 @@ public class NotificationManagerService extends SystemService { pw.println(dump); } + private void dumpRemoteViewStats(PrintWriter pw, @NonNull DumpFilter filter) { + PulledStats stats = mUsageStats.remoteViewStats(filter.since, true); + if (stats == null) { + pw.println("no remote view stats reported."); + return; + } + stats.dump(REPORT_REMOTE_VIEWS, pw, filter); + } + private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter) { final ProtoOutputStream proto = new ProtoOutputStream(fd); synchronized (mNotificationLock) { @@ -5550,7 +5608,9 @@ public class NotificationManagerService extends SystemService { notification.flags |= old.getNotification().flags & FLAG_FOREGROUND_SERVICE; r.isUpdate = true; - r.setTextChanged(isVisuallyInterruptive(old, r)); + final boolean isInterruptive = isVisuallyInterruptive(old, r); + r.setTextChanged(isInterruptive); + r.setInterruptive(isInterruptive); } mNotificationsByKey.put(n.getKey(), r); @@ -5649,7 +5709,6 @@ public class NotificationManagerService extends SystemService { Notification oldN = old.sbn.getNotification(); Notification newN = r.sbn.getNotification(); - if (oldN.extras == null || newN.extras == null) { if (DEBUG_INTERRUPTIVENESS) { Slog.v(TAG, "INTERRUPTIVENESS: " @@ -5681,6 +5740,7 @@ public class NotificationManagerService extends SystemService { } return true; } + // Do not compare Spannables (will always return false); compare unstyled Strings final String oldText = String.valueOf(oldN.extras.get(Notification.EXTRA_TEXT)); final String newText = String.valueOf(newN.extras.get(Notification.EXTRA_TEXT)); @@ -5695,6 +5755,7 @@ public class NotificationManagerService extends SystemService { } return true; } + if (oldN.hasCompletedProgress() != newN.hasCompletedProgress()) { if (DEBUG_INTERRUPTIVENESS) { Slog.v(TAG, "INTERRUPTIVENESS: " @@ -5702,6 +5763,16 @@ public class NotificationManagerService extends SystemService { } return true; } + + // Fields below are invisible to bubbles. + if (r.canBubble()) { + if (DEBUG_INTERRUPTIVENESS) { + Slog.v(TAG, "INTERRUPTIVENESS: " + + r.getKey() + " is not interruptive: bubble"); + } + return false; + } + // Actions if (Notification.areActionsVisiblyDifferent(oldN, newN)) { if (DEBUG_INTERRUPTIVENESS) { @@ -5735,7 +5806,6 @@ public class NotificationManagerService extends SystemService { } catch (Exception e) { Slog.w(TAG, "error recovering builder", e); } - return false; } @@ -5930,12 +6000,17 @@ public class NotificationManagerService extends SystemService { Slog.v(TAG, "INTERRUPTIVENESS: " + record.getKey() + " is not interruptive: summary"); } + } else if (record.canBubble()) { + if (DEBUG_INTERRUPTIVENESS) { + Slog.v(TAG, "INTERRUPTIVENESS: " + + record.getKey() + " is not interruptive: bubble"); + } } else { + record.setInterruptive(true); if (DEBUG_INTERRUPTIVENESS) { Slog.v(TAG, "INTERRUPTIVENESS: " + record.getKey() + " is interruptive: alerted"); } - record.setInterruptive(true); } MetricsLogger.action(record.getLogMaker() .setCategory(MetricsEvent.NOTIFICATION_ALERT) @@ -6294,15 +6369,21 @@ public class NotificationManagerService extends SystemService { int indexBefore = findNotificationRecordIndexLocked(record); boolean interceptBefore = record.isIntercepted(); int visibilityBefore = record.getPackageVisibilityOverride(); + boolean interruptiveBefore = record.isInterruptive(); + recon.applyChangesLocked(record); applyZenModeLocked(record); mRankingHelper.sort(mNotificationList); - int indexAfter = findNotificationRecordIndexLocked(record); - boolean interceptAfter = record.isIntercepted(); - int visibilityAfter = record.getPackageVisibilityOverride(); - changed = indexBefore != indexAfter || interceptBefore != interceptAfter - || visibilityBefore != visibilityAfter; - if (interceptBefore && !interceptAfter + boolean indexChanged = indexBefore != findNotificationRecordIndexLocked(record); + boolean interceptChanged = interceptBefore != record.isIntercepted(); + boolean visibilityChanged = visibilityBefore != record.getPackageVisibilityOverride(); + + // Broadcast isInterruptive changes for bubbles. + boolean interruptiveChanged = + record.canBubble() && (interruptiveBefore != record.isInterruptive()); + + changed = indexChanged || interceptChanged || visibilityChanged || interruptiveChanged; + if (interceptBefore && !record.isIntercepted() && record.isNewEnoughForAlerting(System.currentTimeMillis())) { buzzBeepBlinkLocked(record); } @@ -7433,7 +7514,8 @@ public class NotificationManagerService extends SystemService { record.getSound() != null || record.getVibration() != null, record.getSystemGeneratedSmartActions(), record.getSmartReplies(), - record.canBubble() + record.canBubble(), + record.isInterruptive() ); rankings.add(ranking); } @@ -8542,6 +8624,7 @@ public class NotificationManagerService extends SystemService { public boolean zen; public long since; public boolean stats; + public boolean rvStats; public boolean redact = true; public boolean proto = false; public boolean criticalPriority = false; @@ -8577,6 +8660,14 @@ public class NotificationManagerService extends SystemService { } else { filter.since = 0; } + } else if ("--remote-view-stats".equals(a)) { + filter.rvStats = true; + if (ai < args.length-1) { + ai++; + filter.since = Long.parseLong(args[ai]); + } else { + filter.since = 0; + } } else if (PRIORITY_ARG.equals(a)) { // Bugreport will call the service twice with priority arguments, first to dump // critical sections and then non critical ones. Set approriate filters @@ -8621,6 +8712,7 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting void resetAssistantUserSet(int userId) { + checkCallerIsSystemOrShell(); mAssistants.setUserSet(userId, false); handleSavePolicyFile(); } @@ -8628,12 +8720,14 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting @Nullable ComponentName getApprovedAssistant(int userId) { + checkCallerIsSystemOrShell(); List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId); return CollectionUtils.firstOrNull(allowedComponents); } @VisibleForTesting protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) { + checkCallerIsSystemOrShell(); // only use for testing: mimic receive broadcast that package is (un)suspended // but does not actually (un)suspend the package final Bundle extras = new Bundle(); @@ -8650,6 +8744,7 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting protected void simulatePackageDistractionBroadcast(int flag, String[] pkgs) { + checkCallerIsSystemOrShell(); // only use for testing: mimic receive broadcast that package is (un)distracting // but does not actually register that info with packagemanager final Bundle extras = new Bundle(); diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 3c2169a446a1..c46aad9704b8 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -1289,6 +1289,18 @@ public final class NotificationRecord { return getLogMaker().setCategory(MetricsEvent.NOTIFICATION_ITEM); } + public boolean hasUndecoratedRemoteView() { + Notification notification = getNotification(); + Class<? extends Notification.Style> style = notification.getNotificationStyle(); + boolean hasDecoratedStyle = style != null + && (Notification.DecoratedCustomViewStyle.class.equals(style) + || Notification.DecoratedMediaCustomViewStyle.class.equals(style)); + boolean hasCustomRemoteView = notification.contentView != null + || notification.bigContentView != null + || notification.headsUpContentView != null; + return hasCustomRemoteView && !hasDecoratedStyle; + } + @VisibleForTesting static final class Light { public final int color; diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java index dd0f420b6df5..c6c98965d668 100644 --- a/services/core/java/com/android/server/notification/NotificationShellCmd.java +++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java @@ -16,6 +16,12 @@ package com.android.server.notification; +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS; +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; +import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE; +import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; +import static android.app.NotificationManager.INTERRUPTION_FILTER_UNKNOWN; + import android.app.ActivityManager; import android.app.INotificationManager; import android.app.Notification; @@ -26,6 +32,7 @@ import android.app.Person; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.graphics.drawable.BitmapDrawable; @@ -47,8 +54,8 @@ import java.util.Collections; * Implementation of `cmd notification` in NotificationManagerService. */ public class NotificationShellCmd extends ShellCommand { - private static final String USAGE = - "usage: cmd notification SUBCMD [args]\n\n" + private static final String TAG = "NotifShellCmd"; + private static final String USAGE = "usage: cmd notification SUBCMD [args]\n\n" + "SUBCMDs:\n" + " allow_listener COMPONENT [user_id (current user if not specified)]\n" + " disallow_listener COMPONENT [user_id (current user if not specified)]\n" @@ -89,18 +96,19 @@ public class NotificationShellCmd extends ShellCommand { + "an <intentspec> is (broadcast|service|activity) <args>\n" + " <args> are as described in `am start`"; - public static final int NOTIFICATION_ID = 1138; - public static final String NOTIFICATION_PACKAGE = "com.android.shell"; - public static final String CHANNEL_ID = "shellcmd"; + public static final int NOTIFICATION_ID = 2020; + public static final String CHANNEL_ID = "shell_cmd"; public static final String CHANNEL_NAME = "Shell command"; public static final int CHANNEL_IMP = NotificationManager.IMPORTANCE_DEFAULT; private final NotificationManagerService mDirectService; private final INotificationManager mBinderService; + private final PackageManager mPm; public NotificationShellCmd(NotificationManagerService service) { mDirectService = service; mBinderService = service.getBinderService(); + mPm = mDirectService.getContext().getPackageManager(); } @Override @@ -108,9 +116,44 @@ public class NotificationShellCmd extends ShellCommand { if (cmd == null) { return handleDefaultCommands(cmd); } + String callingPackage = null; + final int callingUid = Binder.getCallingUid(); + long identity = Binder.clearCallingIdentity(); + try { + String[] packages = mPm.getPackagesForUid(callingUid); + if (packages != null && packages.length > 0) { + callingPackage = packages[0]; + } + } catch (Exception e) { + Slog.e(TAG, "failed to get caller pkg", e); + } finally { + Binder.restoreCallingIdentity(identity); + } final PrintWriter pw = getOutPrintWriter(); try { switch (cmd.replace('-', '_')) { + case "set_dnd": { + String mode = getNextArgRequired(); + int interruptionFilter = INTERRUPTION_FILTER_UNKNOWN; + switch(mode) { + case "none": + case "on": + interruptionFilter = INTERRUPTION_FILTER_NONE; + break; + case "priority": + interruptionFilter = INTERRUPTION_FILTER_PRIORITY; + break; + case "alarms": + interruptionFilter = INTERRUPTION_FILTER_ALARMS; + break; + case "all": + case "off": + interruptionFilter = INTERRUPTION_FILTER_ALL; + } + final int filter = interruptionFilter; + mBinderService.setInterruptionFilter(callingPackage, filter); + } + break; case "allow_dnd": { String packageName = getNextArgRequired(); int userId = ActivityManager.getCurrentUser(); @@ -226,7 +269,7 @@ public class NotificationShellCmd extends ShellCommand { } case "post": case "notify": - doNotify(pw); + doNotify(pw, callingPackage, callingUid); break; default: return handleDefaultCommands(cmd); @@ -238,27 +281,14 @@ public class NotificationShellCmd extends ShellCommand { return 0; } - void ensureChannel() throws RemoteException { - final int uid = Binder.getCallingUid(); - final int userid = UserHandle.getCallingUserId(); - final long token = Binder.clearCallingIdentity(); - try { - if (mBinderService.getNotificationChannelForPackage(NOTIFICATION_PACKAGE, - uid, CHANNEL_ID, false) == null) { - final NotificationChannel chan = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, - CHANNEL_IMP); - Slog.v(NotificationManagerService.TAG, - "creating shell channel for user " + userid + " uid " + uid + ": " + chan); - mBinderService.createNotificationChannelsForPackage(NOTIFICATION_PACKAGE, uid, - new ParceledListSlice<NotificationChannel>( - Collections.singletonList(chan))); - Slog.v(NotificationManagerService.TAG, "created channel: " - + mBinderService.getNotificationChannelForPackage(NOTIFICATION_PACKAGE, - uid, CHANNEL_ID, false)); - } - } finally { - Binder.restoreCallingIdentity(token); - } + void ensureChannel(String callingPackage, int callingUid) throws RemoteException { + final NotificationChannel channel = + new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, CHANNEL_IMP); + mBinderService.createNotificationChannels(callingPackage, + new ParceledListSlice<>(Collections.singletonList(channel))); + Slog.v(NotificationManagerService.TAG, "created channel: " + + mBinderService.getNotificationChannel(callingPackage, + UserHandle.getUserId(callingUid), callingPackage, CHANNEL_ID)); } Icon parseIcon(Resources res, String encoded) throws IllegalArgumentException { @@ -287,7 +317,8 @@ public class NotificationShellCmd extends ShellCommand { return null; } - private int doNotify(PrintWriter pw) throws RemoteException, URISyntaxException { + private int doNotify(PrintWriter pw, String callingPackage, int callingUid) + throws RemoteException, URISyntaxException { final Context context = mDirectService.getContext(); final Resources res = context.getResources(); final Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID); @@ -481,26 +512,18 @@ public class NotificationShellCmd extends ShellCommand { builder.setSmallIcon(smallIcon); } - ensureChannel(); + ensureChannel(callingPackage, callingUid); final Notification n = builder.build(); pw.println("posting:\n " + n); Slog.v("NotificationManager", "posting: " + n); - final int userId = UserHandle.getCallingUserId(); - final long token = Binder.clearCallingIdentity(); - try { - mBinderService.enqueueNotificationWithTag( - NOTIFICATION_PACKAGE, "android", - tag, NOTIFICATION_ID, - n, userId); - } finally { - Binder.restoreCallingIdentity(token); - } + mBinderService.enqueueNotificationWithTag(callingPackage, callingPackage, tag, + NOTIFICATION_ID, n, UserHandle.getUserId(callingUid)); if (verbose) { NotificationRecord nr = mDirectService.findNotificationLocked( - NOTIFICATION_PACKAGE, tag, NOTIFICATION_ID, userId); + callingPackage, tag, NOTIFICATION_ID, UserHandle.getUserId(callingUid)); for (int tries = 3; tries-- > 0; ) { if (nr != null) break; try { @@ -509,7 +532,7 @@ public class NotificationShellCmd extends ShellCommand { } catch (InterruptedException e) { } nr = mDirectService.findNotificationLocked( - NOTIFICATION_PACKAGE, tag, NOTIFICATION_ID, userId); + callingPackage, tag, NOTIFICATION_ID, UserHandle.getUserId(callingUid)); } if (nr == null) { pw.println("warning: couldn't find notification after enqueueing"); diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index dd393b8846f0..fbf9934fcfd1 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -150,6 +150,7 @@ public class NotificationUsageStats { stats.numPostedByApp++; stats.updateInterarrivalEstimate(now); stats.countApiUse(notification); + stats.numUndecoratedRemoteViews += (notification.hasUndecoratedRemoteView() ? 1 : 0); } releaseAggregatedStatsLocked(aggregatedStatsArray); if (ENABLE_SQLITE_LOG) { @@ -327,6 +328,15 @@ public class NotificationUsageStats { return dump; } + public PulledStats remoteViewStats(long startMs, boolean aggregate) { + if (ENABLE_SQLITE_LOG) { + if (aggregate) { + return mSQLiteLog.remoteViewAggStats(startMs); + } + } + return null; + } + public synchronized void dump(PrintWriter pw, String indent, DumpFilter filter) { if (ENABLE_AGGREGATED_IN_MEMORY_STATS) { for (AggregatedStats as : mStats.values()) { @@ -404,6 +414,7 @@ public class NotificationUsageStats { public int numRateViolations; public int numAlertViolations; public int numQuotaViolations; + public int numUndecoratedRemoteViews; public long mLastAccessTime; public AggregatedStats(Context context, String key) { @@ -670,6 +681,8 @@ public class NotificationUsageStats { output.append(indentPlusTwo).append(noisyImportance.toString()).append("\n"); output.append(indentPlusTwo).append(quietImportance.toString()).append("\n"); output.append(indentPlusTwo).append(finalImportance.toString()).append("\n"); + output.append(indentPlusTwo); + output.append("numUndecorateRVs=").append(numUndecoratedRemoteViews).append("\n"); output.append(indent).append("}"); return output.toString(); } @@ -1028,7 +1041,7 @@ public class NotificationUsageStats { private static final int MSG_DISMISS = 4; private static final String DB_NAME = "notification_log.db"; - private static final int DB_VERSION = 5; + private static final int DB_VERSION = 7; /** Age in ms after which events are pruned from the DB. */ private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L; // 1 week @@ -1061,6 +1074,7 @@ public class NotificationUsageStats { private static final String COL_FIRST_EXPANSIONTIME_MS = "first_expansion_time_ms"; private static final String COL_AIRTIME_EXPANDED_MS = "expansion_airtime_ms"; private static final String COL_EXPAND_COUNT = "expansion_count"; + private static final String COL_UNDECORATED = "undecorated"; private static final int EVENT_TYPE_POST = 1; @@ -1086,12 +1100,20 @@ public class NotificationUsageStats { "COUNT(*) AS cnt, " + "SUM(" + COL_MUTED + ") as muted, " + "SUM(" + COL_NOISY + ") as noisy, " + - "SUM(" + COL_DEMOTED + ") as demoted " + + "SUM(" + COL_DEMOTED + ") as demoted, " + + "SUM(" + COL_UNDECORATED + ") as undecorated " + "FROM " + TAB_LOG + " " + "WHERE " + COL_EVENT_TYPE + "=" + EVENT_TYPE_POST + " AND " + COL_EVENT_TIME + " > %d " + " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG; + private static final String UNDECORATED_QUERY = "SELECT " + + COL_PKG + ", " + + "MAX(" + COL_EVENT_TIME + ") as max_time " + + "FROM " + TAB_LOG + " " + + "WHERE " + COL_UNDECORATED + "> 0 " + + " AND " + COL_EVENT_TIME + " > %d " + + "GROUP BY " + COL_PKG; public SQLiteLog(Context context) { HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log", @@ -1147,7 +1169,8 @@ public class NotificationUsageStats { COL_AIRTIME_MS + " INT," + COL_FIRST_EXPANSIONTIME_MS + " INT," + COL_AIRTIME_EXPANDED_MS + " INT," + - COL_EXPAND_COUNT + " INT" + + COL_EXPAND_COUNT + " INT," + + COL_UNDECORATED + " INT" + ")"); } @@ -1257,6 +1280,7 @@ public class NotificationUsageStats { } else { putPosttimeVisibility(r, cv); } + cv.put(COL_UNDECORATED, (r.hasUndecoratedRemoteView() ? 1 : 0)); SQLiteDatabase db = mHelper.getWritableDatabase(); if (db.insert(TAB_LOG, null, cv) < 0) { Log.wtf(TAG, "Error while trying to insert values: " + cv); @@ -1337,5 +1361,22 @@ public class NotificationUsageStats { } return dump; } + + public PulledStats remoteViewAggStats(long startMs) { + PulledStats stats = new PulledStats(startMs); + SQLiteDatabase db = mHelper.getReadableDatabase(); + String q = String.format(UNDECORATED_QUERY, startMs); + Cursor cursor = db.rawQuery(q, null); + try { + for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { + String pkg = cursor.getString(0); + long maxTimeMs = cursor.getLong(1); + stats.addUndecoratedPackage(pkg, maxTimeMs); + } + } finally { + cursor.close(); + } + return stats; + } } } diff --git a/services/core/java/com/android/server/notification/PulledStats.java b/services/core/java/com/android/server/notification/PulledStats.java new file mode 100644 index 000000000000..ada890a10361 --- /dev/null +++ b/services/core/java/com/android/server/notification/PulledStats.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.notification; + +import static com.android.server.notification.NotificationManagerService.REPORT_REMOTE_VIEWS; + +import android.os.ParcelFileDescriptor; +import android.service.notification.NotificationRemoteViewsProto; +import android.service.notification.PackageRemoteViewInfoProto; +import android.util.Slog; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +public class PulledStats { + static final String TAG = "PulledStats"; + + private final long mTimePeriodStartMs; + private long mTimePeriodEndMs; + private List<String> mUndecoratedPackageNames; + + public PulledStats(long startMs) { + mTimePeriodEndMs = mTimePeriodStartMs = startMs; + mUndecoratedPackageNames = new ArrayList<>(); + } + + ParcelFileDescriptor toParcelFileDescriptor(int report) + throws IOException { + final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); + switch(report) { + case REPORT_REMOTE_VIEWS: + Thread thr = new Thread("NotificationManager pulled metric output") { + public void run() { + try { + FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream( + fds[1]); + final ProtoOutputStream proto = new ProtoOutputStream(fout); + writeToProto(report, proto); + proto.flush(); + fout.close(); + } catch (IOException e) { + Slog.w(TAG, "Failure writing pipe", e); + } + } + }; + thr.start(); + break; + + default: + Slog.w(TAG, "Unknown pulled stats request: " + report); + break; + } + return fds[0]; + } + + /* + * @return the most recent timestamp in the report, as nanoseconds. + */ + public long endTimeMs() { + return mTimePeriodEndMs; + } + + public void dump(int report, PrintWriter pw, NotificationManagerService.DumpFilter filter) { + switch(report) { + case REPORT_REMOTE_VIEWS: + pw.print(" Packages with undecordated notifications ("); + pw.print(mTimePeriodStartMs); + pw.print(" - "); + pw.print(mTimePeriodEndMs); + pw.println("):"); + if (mUndecoratedPackageNames.size() == 0) { + pw.println(" none"); + } else { + for (String pkg : mUndecoratedPackageNames) { + if (!filter.filtered || pkg.equals(filter.pkgFilter)) { + pw.println(" " + pkg); + } + } + } + break; + + default: + pw.println("Unknown pulled stats request: " + report); + break; + } + } + + @VisibleForTesting + void writeToProto(int report, ProtoOutputStream proto) { + switch(report) { + case REPORT_REMOTE_VIEWS: + for (String pkg: mUndecoratedPackageNames) { + long token = proto.start(NotificationRemoteViewsProto.PACKAGE_REMOTE_VIEW_INFO); + proto.write(PackageRemoteViewInfoProto.PACKAGE_NAME, pkg); + proto.end(token); + } + break; + + default: + Slog.w(TAG, "Unknown pulled stats request: " + report); + break; + } + } + + public void addUndecoratedPackage(String packageName, long timestampMs) { + mUndecoratedPackageNames.add(packageName); + mTimePeriodEndMs = Math.max(mTimePeriodEndMs, timestampMs); + } +} diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 0329e2c5ed45..fd8db4b99be8 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -259,6 +259,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements // Don't hold mSessions lock when calling restoreSession, since it might trigger an APK // atomic install which needs to query sessions, which requires lock on mSessions. for (PackageInstallerSession session : stagedSessionsToRestore) { + if (mPm.isDeviceUpgrading() && !session.isStagedAndInTerminalState()) { + session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + "Build fingerprint has changed"); + } mStagingManager.restoreSession(session); } // Broadcasts are not sent while we restore sessions on boot, since no processes would be diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 4eddb9301a69..7091c7ca7316 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2209,8 +2209,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED); // Send broadcast to default launcher only if it's a new install + // TODO(b/144270665): Secure the usage of this broadcast. final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); - if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) { + if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts() + && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { mPm.sendSessionCommitBroadcast(generateInfo(), userId); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7e7822cd978e..7a5bcc59de77 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -23122,6 +23122,13 @@ public class PackageManagerService extends IPackageManager.Stub } } + boolean readPermissionStateForUser(@UserIdInt int userId) { + synchronized (mPackages) { + mSettings.readPermissionStateForUserSyncLPr(userId); + return mSettings.areDefaultRuntimePermissionsGrantedLPr(userId); + } + } + @Override public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException { mContext.enforceCallingOrSelfPermission( diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index e991a91fe8ba..85bf4d94cd3c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2376,6 +2376,7 @@ class PackageManagerShellCommand extends ShellCommand { int userId = -1; int flags = 0; String opt; + boolean preCreateOnly = false; while ((opt = getNextOption()) != null) { if ("--profileOf".equals(opt)) { userId = UserHandle.parseUserArg(getNextArgRequired()); @@ -2389,16 +2390,22 @@ class PackageManagerShellCommand extends ShellCommand { flags |= UserInfo.FLAG_GUEST; } else if ("--demo".equals(opt)) { flags |= UserInfo.FLAG_DEMO; + } else if ("--pre-create-only".equals(opt)) { + preCreateOnly = true; } else { getErrPrintWriter().println("Error: unknown option " + opt); return 1; } } String arg = getNextArg(); - if (arg == null) { + if (arg == null && !preCreateOnly) { getErrPrintWriter().println("Error: no user name specified."); return 1; } + if (arg != null && preCreateOnly) { + getErrPrintWriter().println("Warning: name is ignored for pre-created users"); + } + name = arg; UserInfo info; IUserManager um = IUserManager.Stub.asInterface( @@ -2412,7 +2419,7 @@ class PackageManagerShellCommand extends ShellCommand { accm.addSharedAccountsFromParentUser(parentUserId, userId, (Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell"); } else if (userId < 0) { - info = um.createUser(name, flags); + info = preCreateOnly ? um.preCreateUser(flags) : um.createUser(name, flags); } else { info = um.createProfileForUser(name, flags, userId, null); } @@ -3307,8 +3314,11 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" trim-caches DESIRED_FREE_SPACE [internal|UUID]"); pw.println(" Trim cache files to reach the given free space."); pw.println(""); + pw.println(" list users"); + pw.println(" Lists the current users."); + pw.println(""); pw.println(" create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral]"); - pw.println(" [--guest] USER_NAME"); + pw.println(" [--guest] [--pre-create-only] USER_NAME"); pw.println(" Create a new user with the given USER_NAME, printing the new user identifier"); pw.println(" of the user."); pw.println(""); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index d1e4537b9235..994fca80e672 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3140,6 +3140,10 @@ public final class Settings { return true; } + void readPermissionStateForUserSyncLPr(@UserIdInt int userId) { + mRuntimePermissionsPersistence.readStateForUserSyncLPr(userId); + } + void applyDefaultPreferredAppsLPw(int userId) { // First pull data from any pre-installed apps. final PackageManagerInternal pmInternal = diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 2705455078ff..349a0031613b 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -41,6 +41,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.os.ParcelableException; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; @@ -414,29 +415,28 @@ public class StagingManager { } else { params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; } - int apkSessionId = mPi.createSession( - params, originalSession.getInstallerPackageName(), - 0 /* UserHandle.SYSTEM */); - PackageInstallerSession apkSession = mPi.getSession(apkSessionId); - try { + int apkSessionId = mPi.createSession( + params, originalSession.getInstallerPackageName(), + 0 /* UserHandle.SYSTEM */); + PackageInstallerSession apkSession = mPi.getSession(apkSessionId); apkSession.open(); for (String apkFilePath : apkFilePaths) { File apkFile = new File(apkFilePath); ParcelFileDescriptor pfd = ParcelFileDescriptor.open(apkFile, ParcelFileDescriptor.MODE_READ_ONLY); - long sizeBytes = pfd.getStatSize(); + long sizeBytes = (pfd == null) ? -1 : pfd.getStatSize(); if (sizeBytes < 0) { Slog.e(TAG, "Unable to get size of: " + apkFilePath); return null; } apkSession.write(apkFile.getName(), 0, sizeBytes, pfd); } - } catch (IOException e) { + return apkSession; + } catch (IOException | ParcelableException e) { Slog.e(TAG, "Failure to install APK staged session " + originalSession.sessionId, e); return null; } - return apkSession; } private boolean commitApkSession(@NonNull PackageInstallerSession apkSession, diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 31960e47ffce..cc42f6215899 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -41,6 +41,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ShortcutServiceInternal; import android.content.pm.UserInfo; +import android.content.pm.UserInfo.UserInfoFlag; import android.content.res.Resources; import android.graphics.Bitmap; import android.os.Binder; @@ -66,6 +67,7 @@ import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManager.EnforcingUser; @@ -83,6 +85,7 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.TimeUtils; +import android.util.TimingsTraceLog; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -155,6 +158,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_SERIAL_NO = "serialNumber"; private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber"; private static final String ATTR_PARTIAL = "partial"; + private static final String ATTR_PRE_CREATED = "preCreated"; private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove"; private static final String ATTR_USER_VERSION = "version"; private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId"; @@ -475,6 +479,10 @@ public class UserManagerService extends IUserManager.Stub { public void onBootPhase(int phase) { if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { mUms.cleanupPartialUsers(); + + if (mUms.mPm.isDeviceUpgrading()) { + mUms.cleanupPreCreatedUsers(); + } } } @@ -582,7 +590,8 @@ public class UserManagerService extends IUserManager.Stub { final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { UserInfo ui = mUsers.valueAt(i).info; - if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) { + if ((ui.partial || ui.guestToRemove || (ui.isEphemeral() && !ui.preCreated)) + && i != 0) { partials.add(ui); addRemovingUserIdLocked(ui.id); ui.partial = true; @@ -598,6 +607,33 @@ public class UserManagerService extends IUserManager.Stub { } } + /** + * Removes any pre-created users from the system. Should be invoked after OTAs, to ensure + * pre-created users are not stale. New pre-created pool can be re-created after the update. + */ + void cleanupPreCreatedUsers() { + final ArrayList<UserInfo> preCreatedUsers; + synchronized (mUsersLock) { + final int userSize = mUsers.size(); + preCreatedUsers = new ArrayList<>(userSize); + for (int i = 0; i < userSize; i++) { + UserInfo ui = mUsers.valueAt(i).info; + if (ui.preCreated) { + preCreatedUsers.add(ui); + addRemovingUserIdLocked(ui.id); + ui.flags |= UserInfo.FLAG_DISABLED; + ui.partial = true; + } + } + } + final int preCreatedSize = preCreatedUsers.size(); + for (int i = 0; i < preCreatedSize; i++) { + UserInfo ui = preCreatedUsers.get(i); + Slog.i(LOG_TAG, "Removing pre-created user " + ui.id); + removeUserState(ui.id); + } + } + @Override public String getUserAccount(int userId) { checkManageUserAndAcrossUsersFullPermission("get user account"); @@ -645,20 +681,25 @@ public class UserManagerService extends IUserManager.Stub { return null; } - @Override public @NonNull List<UserInfo> getUsers(boolean excludeDying) { + return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); + } + + @Override + public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying, + boolean excludePreCreated) { checkManageOrCreateUsersPermission("query users"); synchronized (mUsersLock) { ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size()); final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { UserInfo ui = mUsers.valueAt(i).info; - if (ui.partial) { + if ((excludePartial && ui.partial) + || (excludeDying && mRemovingUserIds.get(ui.id)) + || (excludePreCreated && ui.preCreated)) { continue; } - if (!excludeDying || !mRemovingUserIds.get(ui.id)) { - users.add(userWithName(ui)); - } + users.add(userWithName(ui)); } return users; } @@ -1179,8 +1220,9 @@ public class UserManagerService extends IUserManager.Stub { } } - private void checkManageOrInteractPermIfCallerInOtherProfileGroup(int userId, String name) { - int callingUserId = UserHandle.getCallingUserId(); + private void checkManageOrInteractPermIfCallerInOtherProfileGroup(@UserIdInt int userId, + String name) { + final int callingUserId = UserHandle.getCallingUserId(); if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) || hasManageUsersPermission()) { return; @@ -1193,8 +1235,8 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean isDemoUser(int userId) { - int callingUserId = UserHandle.getCallingUserId(); + public boolean isDemoUser(@UserIdInt int userId) { + final int callingUserId = UserHandle.getCallingUserId(); if (callingUserId != userId && !hasManageUsersPermission()) { throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId + " is a demo user"); @@ -1206,6 +1248,19 @@ public class UserManagerService extends IUserManager.Stub { } @Override + public boolean isPreCreated(@UserIdInt int userId) { + final int callingUserId = UserHandle.getCallingUserId(); + if (callingUserId != userId && !hasManageUsersPermission()) { + throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId + + " is pre-created"); + } + synchronized (mUsersLock) { + UserInfo userInfo = getUserInfoLU(userId); + return userInfo != null && userInfo.preCreated; + } + } + + @Override public boolean isRestricted() { synchronized (mUsersLock) { return getUserInfoLU(UserHandle.getCallingUserId()).isRestricted(); @@ -1859,7 +1914,7 @@ public class UserManagerService extends IUserManager.Stub { // Skip over users being removed for (int i = 0; i < totalUserCount; i++) { UserInfo user = mUsers.valueAt(i).info; - if (!mRemovingUserIds.get(user.id) && !user.isGuest()) { + if (!mRemovingUserIds.get(user.id) && !user.isGuest() && !user.preCreated) { aliveUserCount++; } } @@ -2320,6 +2375,9 @@ public class UserManagerService extends IUserManager.Stub { if (userInfo.partial) { serializer.attribute(null, ATTR_PARTIAL, "true"); } + if (userInfo.preCreated) { + serializer.attribute(null, ATTR_PRE_CREATED, "true"); + } if (userInfo.guestToRemove) { serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true"); } @@ -2476,6 +2534,7 @@ public class UserManagerService extends IUserManager.Stub { int profileBadge = 0; int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID; boolean partial = false; + boolean preCreated = false; boolean guestToRemove = false; boolean persistSeedData = false; String seedAccountName = null; @@ -2520,6 +2579,10 @@ public class UserManagerService extends IUserManager.Stub { if ("true".equals(valueString)) { partial = true; } + valueString = parser.getAttributeValue(null, ATTR_PRE_CREATED); + if ("true".equals(valueString)) { + preCreated = true; + } valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE); if ("true".equals(valueString)) { guestToRemove = true; @@ -2573,6 +2636,7 @@ public class UserManagerService extends IUserManager.Stub { userInfo.lastLoggedInTime = lastLoggedInTime; userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint; userInfo.partial = partial; + userInfo.preCreated = preCreated; userInfo.guestToRemove = guestToRemove; userInfo.profileGroupId = profileGroupId; userInfo.profileBadge = profileBadge; @@ -2644,7 +2708,8 @@ public class UserManagerService extends IUserManager.Stub { public UserInfo createProfileForUserEvenWhenDisallowed(String name, int flags, int userId, String[] disallowedPackages) { checkManageOrCreateUsersPermission(flags); - return createUserInternalUnchecked(name, flags, userId, disallowedPackages); + return createUserInternalUnchecked(name, flags, userId, /* preCreate= */ false, + disallowedPackages); } @Override @@ -2659,12 +2724,27 @@ public class UserManagerService extends IUserManager.Stub { return createUserInternal(name, flags, UserHandle.USER_NULL); } - private UserInfo createUserInternal(String name, int flags, int parentId) { + @Override + public UserInfo preCreateUser(int flags) { + checkManageOrCreateUsersPermission(flags); + + Preconditions.checkArgument(!UserInfo.isManagedProfile(flags), + "cannot pre-create managed profiles"); + + Slog.i(LOG_TAG, "Pre-creating user with flags " + UserInfo.flagsToString(flags)); + + return createUserInternalUnchecked(/* name= */ null, flags, + /* parentId= */ UserHandle.USER_NULL, /* preCreate= */ true, + /* disallowedPackages= */ null); + } + + private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags, + @UserIdInt int parentId) { return createUserInternal(name, flags, parentId, null); } - private UserInfo createUserInternal(String name, int flags, int parentId, - String[] disallowedPackages) { + private UserInfo createUserInternal(@Nullable String name, @UserInfoFlag int flags, + @UserIdInt int parentId, @Nullable String[] disallowedPackages) { String restriction = ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0) ? UserManager.DISALLOW_ADD_MANAGED_PROFILE : UserManager.DISALLOW_ADD_USER; @@ -2672,19 +2752,80 @@ public class UserManagerService extends IUserManager.Stub { Log.w(LOG_TAG, "Cannot add user. " + restriction + " is enabled."); return null; } - return createUserInternalUnchecked(name, flags, parentId, disallowedPackages); + return createUserInternalUnchecked(name, flags, parentId, /* preCreate= */ false, + disallowedPackages); } - private UserInfo createUserInternalUnchecked(String name, int flags, int parentId, - String[] disallowedPackages) { + private UserInfo createUserInternalUnchecked(@Nullable String name, @UserInfoFlag int flags, + @UserIdInt int parentId, boolean preCreate, + @Nullable String[] disallowedPackages) { + final TimingsTraceLog t = new TimingsTraceLog(LOG_TAG, Trace.TRACE_TAG_SYSTEM_SERVER); + t.traceBegin("createUser-" + flags); + try { + return createUserInternalUncheckedNoTracing(name, flags, parentId, preCreate, + disallowedPackages, t); + } finally { + t.traceEnd(); + } + } + + private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name, + @UserInfoFlag int flags, @UserIdInt int parentId, boolean preCreate, + @Nullable String[] disallowedPackages, @NonNull TimingsTraceLog t) { + + // First try to use a pre-created user (if available). + // NOTE: currently we don't support pre-created managed profiles + if (!preCreate && (parentId < 0 && !UserInfo.isManagedProfile(flags))) { + final UserData preCreatedUserData; + synchronized (mUsersLock) { + preCreatedUserData = getPreCreatedUserLU(flags); + } + if (preCreatedUserData != null) { + final UserInfo preCreatedUser = preCreatedUserData.info; + if (UserInfo.isGuest(flags) && areGuestUsersEphemeral()) { + // TODO(b/143092698): this pre-created user has (persisted) storage keys + // that will be removed when the user is stopped and ideally we should + // remove them from storage right now, but that's not possible with the + // current StorageManager APIs (there are just a + // createUserKey(userId, serial, isEphemeral) and destroyUserKey(userId) + // methods; we would need a makeUserKeyEphemeral(userId) method) + preCreatedUserData.info.flags |= UserInfo.FLAG_EPHEMERAL; + } + Log.i(LOG_TAG, "Reusing pre-created user " + preCreatedUser.id + " for flags " + + UserInfo.flagsToString(flags) + "; new flags: " + + UserInfo.flagsToString(preCreatedUserData.info.flags)); + if (DBG) { + Log.d(LOG_TAG, "pre-created user flags: " + + UserInfo.flagsToString(preCreatedUser.flags) + + " new-user flags: " + UserInfo.flagsToString(flags)); + } + preCreatedUser.name = name; + preCreatedUser.preCreated = false; + preCreatedUser.creationTime = getCreationTime(); + + synchronized (mPackagesLock) { + writeUserLP(preCreatedUserData); + writeUserListLP(); + } + + updateUserIds(); + if (!mPm.readPermissionStateForUser(preCreatedUser.id)) { + // Could not read the existing permissions, re-grant them. + mPm.onNewUserCreated(preCreatedUser.id); + } + dispatchUserAddedIntent(preCreatedUser); + return preCreatedUser; + } + } + DeviceStorageMonitorInternal dsm = LocalServices .getService(DeviceStorageMonitorInternal.class); if (dsm.isMemoryLow()) { Log.w(LOG_TAG, "Cannot add user. Not enough space on disk."); return null; } - final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0; - final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0; + final boolean isGuest = UserInfo.isGuest(flags); + final boolean isManagedProfile = UserInfo.isManagedProfile(flags); final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0; final boolean isDemo = (flags & UserInfo.FLAG_DEMO) != 0; final long ident = Binder.clearCallingIdentity(); @@ -2705,13 +2846,13 @@ public class UserManagerService extends IUserManager.Stub { return null; } if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) { - // If we're not adding a guest/demo user or a managed profile and the limit has - // been reached, cannot add a user. + // If we're not adding a guest/demo user or a managed profile, + // and the limit has been reached, cannot add a user. Log.e(LOG_TAG, "Cannot add user. Maximum user limit is reached."); return null; } // If we're adding a guest and there already exists one, bail. - if (isGuest && findCurrentGuestUser() != null) { + if (isGuest && !preCreate && findCurrentGuestUser() != null) { Log.e(LOG_TAG, "Cannot add guest user. Guest user already exists."); return null; } @@ -2747,21 +2888,21 @@ public class UserManagerService extends IUserManager.Stub { userId = getNextAvailableId(); Environment.getUserSystemDirectory(userId).mkdirs(); - boolean ephemeralGuests = Resources.getSystem() - .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral); + boolean ephemeralGuests = areGuestUsersEphemeral(); synchronized (mUsersLock) { // Add ephemeral flag to guests/users if required. Also inherit it from parent. - if ((isGuest && ephemeralGuests) || mForceEphemeralUsers - || (parent != null && parent.info.isEphemeral())) { + if (!preCreate && ((isGuest && ephemeralGuests) + || mForceEphemeralUsers + || (parent != null && parent.info.isEphemeral()))) { flags |= UserInfo.FLAG_EPHEMERAL; } userInfo = new UserInfo(userId, name, null, flags); userInfo.serialNumber = mNextSerialNumber++; - long now = System.currentTimeMillis(); - userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0; + userInfo.creationTime = getCreationTime(); userInfo.partial = true; + userInfo.preCreated = preCreate; userInfo.lastLoggedInFingerprint = Build.FINGERPRINT; if (isManagedProfile && parentId != UserHandle.USER_NULL) { userInfo.profileBadge = getFreeProfileBadgeLU(parentId); @@ -2807,19 +2948,95 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mRestrictionsLock) { mBaseUserRestrictions.append(userId, restrictions); } + + t.traceBegin("PM.onNewUserCreated-" + userId); mPm.onNewUserCreated(userId); - Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, - android.Manifest.permission.MANAGE_USERS); - MetricsLogger.count(mContext, isGuest ? TRON_GUEST_CREATED - : (isDemo ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1); + t.traceEnd(); + if (preCreate) { + // Must start user (which will be stopped right away, through + // UserController.finishUserUnlockedCompleted) so services can properly + // intialize it. + // TODO(b/140750212): in the long-term, we should add a onCreateUser() callback + // on SystemService instead. + Slog.i(LOG_TAG, "starting pre-created user " + userInfo.toFullString()); + final IActivityManager am = ActivityManager.getService(); + try { + am.startUserInBackground(userId); + } catch (RemoteException e) { + Slog.w(LOG_TAG, "could not start pre-created user " + userId, e); + } + } else { + dispatchUserAddedIntent(userInfo); + } + } finally { Binder.restoreCallingIdentity(ident); } + + // TODO(b/140750212): it's possible to reach "max users overflow" when the user is created + // "from scratch" (i.e., not from a pre-created user) and reaches the maximum number of + // users without counting the pre-created one. Then when the pre-created is converted, the + // "effective" number of max users is exceeds. Example: + // Max: 3 Current: 2 full (u0 and u10) + 1 pre-created (u11) + // Step 1: create(/* flags doesn't match u11 */): u12 is created, "effective max" is now 3 + // (u0, u10, u12) but "real" max is 4 (u0, u10, u11, u12) + // Step 2: create(/* flags match u11 */): u11 is converted, now "effective max" is also 4 + // (u0, u10, u11, u12) + // One way to avoid this issue is by removing a pre-created user from the pool when the + // "real" max exceeds the max here. + return userInfo; } + private long getCreationTime() { + final long now = System.currentTimeMillis(); + return (now > EPOCH_PLUS_30_YEARS) ? now : 0; + } + + private void dispatchUserAddedIntent(@NonNull UserInfo userInfo) { + Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id); + mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, + android.Manifest.permission.MANAGE_USERS); + MetricsLogger.count(mContext, userInfo.isGuest() ? TRON_GUEST_CREATED + : (userInfo.isDemo() ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1); + } + + private boolean areGuestUsersEphemeral() { + return Resources.getSystem() + .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral); + } + + /** + * Gets a pre-created user for the given flag. + * + * <p>Should be used only during user creation, so the pre-created user can be used (instead of + * creating and initializing a new user from scratch). + */ + // TODO(b/140750212): add unit test + @GuardedBy("mUsersLock") + private @Nullable UserData getPreCreatedUserLU(@UserInfoFlag int flags) { + if (DBG) { + Slog.d(LOG_TAG, "getPreCreatedUser(): flags= " + UserInfo.flagsToString(flags)); + } + final int userSize = mUsers.size(); + for (int i = 0; i < userSize; i++) { + final UserData user = mUsers.valueAt(i); + if (DBG) Slog.d(LOG_TAG, i + ":" + user.info.toFullString()); + if (user.info.preCreated + && (user.info.flags & ~UserInfo.FLAG_INITIALIZED) == flags) { + if (!user.info.isInitialized()) { + Slog.w(LOG_TAG, "found pre-created user for flags " + + "" + UserInfo.flagsToString(flags) + + ", but it's not initialized yet: " + user.info.toFullString()); + continue; + } + return user; + } + } + return null; + } + @VisibleForTesting UserData putUserInfo(UserInfo userInfo) { final UserData userData = new UserData(); @@ -2872,7 +3089,8 @@ public class UserManagerService extends IUserManager.Stub { final int size = mUsers.size(); for (int i = 0; i < size; i++) { final UserInfo user = mUsers.valueAt(i).info; - if (user.isGuest() && !user.guestToRemove && !mRemovingUserIds.get(user.id)) { + if (user.isGuest() && !user.guestToRemove && !user.preCreated + && !mRemovingUserIds.get(user.id)) { return user; } } @@ -3426,14 +3644,16 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mUsersLock) { final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { - if (!mUsers.valueAt(i).info.partial) { + UserInfo userInfo = mUsers.valueAt(i).info; + if (!userInfo.partial && !userInfo.preCreated) { num++; } } final int[] newUsers = new int[num]; int n = 0; for (int i = 0; i < userSize; i++) { - if (!mUsers.valueAt(i).info.partial) { + UserInfo userInfo = mUsers.valueAt(i).info; + if (!userInfo.partial && !userInfo.preCreated) { newUsers[n++] = mUsers.keyAt(i); } } @@ -3485,7 +3705,10 @@ public class UserManagerService extends IUserManager.Stub { * recycled. */ void reconcileUsers(String volumeUuid) { - mUserDataPreparer.reconcileUsers(volumeUuid, getUsers(true /* excludeDying */)); + mUserDataPreparer.reconcileUsers(volumeUuid, getUsers( + /* excludePartial= */ true, + /* excludeDying= */ true, + /* excludePreCreated= */ false)); } /** @@ -3651,7 +3874,7 @@ public class UserManagerService extends IUserManager.Stub { try { switch(cmd) { case "list": - return runList(pw); + return runList(pw, shell); default: return shell.handleDefaultCommands(cmd); } @@ -3661,17 +3884,58 @@ public class UserManagerService extends IUserManager.Stub { return -1; } - private int runList(PrintWriter pw) throws RemoteException { + private int runList(PrintWriter pw, Shell shell) throws RemoteException { + boolean all = false; + boolean verbose = false; + String opt; + while ((opt = shell.getNextOption()) != null) { + switch (opt) { + case "-v": + verbose = true; + break; + case "--all": + all = true; + break; + default: + pw.println("Invalid option: " + opt); + return -1; + } + } final IActivityManager am = ActivityManager.getService(); - final List<UserInfo> users = getUsers(false); + final List<UserInfo> users = getUsers(/* excludePartial= */ !all, + /* excludingDying=*/ false, /* excludePreCreated= */ !all); if (users == null) { pw.println("Error: couldn't get users"); return 1; } else { - pw.println("Users:"); - for (int i = 0; i < users.size(); i++) { - String running = am.isUserRunning(users.get(i).id, 0) ? " running" : ""; - pw.println("\t" + users.get(i).toString() + running); + final int size = users.size(); + int currentUser = UserHandle.USER_NULL; + if (verbose) { + pw.printf("%d users:\n\n", size); + currentUser = am.getCurrentUser().id; + } else { + // NOTE: the standard "list users" command is used by integration tests and + // hence should not be changed. If you need to add more info, use the + // verbose option. + pw.println("Users:"); + } + for (int i = 0; i < size; i++) { + final UserInfo user = users.get(i); + final boolean running = am.isUserRunning(user.id, 0); + final boolean current = user.id == currentUser; + if (verbose) { + pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s\n", i, user.id, user.name, + UserInfo.flagsToString(user.flags), + running ? " (running)" : "", + user.partial ? " (partial)" : "", + user.preCreated ? " (pre-created)" : "", + current ? " (current)" : ""); + } else { + // NOTE: the standard "list users" command is used by integration tests and + // hence should not be changed. If you need to add more info, use the + // verbose option. + pw.printf("\t%s%s\n", user, running ? " running" : ""); + } } return 0; } @@ -3683,6 +3947,16 @@ public class UserManagerService extends IUserManager.Stub { long now = System.currentTimeMillis(); final long nowRealtime = SystemClock.elapsedRealtime(); + + final ActivityManagerInternal amInternal = LocalServices + .getService(ActivityManagerInternal.class); + pw.print("Current user: "); + if (amInternal != null) { + pw.println(amInternal.getCurrentUserId()); + } else { + pw.println("N/A"); + } + StringBuilder sb = new StringBuilder(); synchronized (mPackagesLock) { synchronized (mUsersLock) { @@ -3696,13 +3970,19 @@ public class UserManagerService extends IUserManager.Stub { final int userId = userInfo.id; pw.print(" "); pw.print(userInfo); pw.print(" serialNo="); pw.print(userInfo.serialNumber); + pw.print(" isPrimary="); pw.print(userInfo.isPrimary()); if (mRemovingUserIds.get(userId)) { pw.print(" <removing> "); } if (userInfo.partial) { pw.print(" <partial>"); } + if (userInfo.preCreated) { + pw.print(" <pre-created>"); + } pw.println(); + pw.print(" Flags: "); pw.print(userInfo.flags); pw.print(" ("); + pw.print(UserInfo.flagsToString(userInfo.flags)); pw.println(")"); pw.print(" State: "); final int state; synchronized (mUserStates) { @@ -3778,13 +4058,16 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mUserStates) { pw.println(" Started users state: " + mUserStates); } - // Dump some capabilities - pw.println(); - pw.println(" Max users: " + UserManager.getMaxSupportedUsers()); - pw.println(" Supports switchable users: " + UserManager.supportsMultipleUsers()); - pw.println(" All guests ephemeral: " + Resources.getSystem().getBoolean( - com.android.internal.R.bool.config_guestUserEphemeral)); - } + } // synchronized (mPackagesLock) + + // Dump some capabilities + pw.println(); + pw.print(" Max users: " + UserManager.getMaxSupportedUsers()); + pw.println(" (limit reached: " + isUserLimitReached() + ")"); + pw.println(" Supports switchable users: " + UserManager.supportsMultipleUsers()); + pw.println(" All guests ephemeral: " + areGuestUsersEphemeral()); + pw.println(" Is split-system user: " + UserManager.isSplitSystemUser()); + pw.println(" User version: " + mUserVersion); } private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) { @@ -3969,7 +4252,7 @@ public class UserManagerService extends IUserManager.Stub { public UserInfo createUserEvenWhenDisallowed(String name, int flags, String[] disallowedPackages) { UserInfo user = createUserInternalUnchecked(name, flags, UserHandle.USER_NULL, - disallowedPackages); + /* preCreated= */ false, disallowedPackages); // Keep this in sync with UserManager.createUser if (user != null && !user.isAdmin() && !user.isDemo()) { setUserRestriction(UserManager.DISALLOW_SMS, true, user.id); @@ -4134,7 +4417,7 @@ public class UserManagerService extends IUserManager.Stub { pw.println(" help"); pw.println(" Print this help text."); pw.println(""); - pw.println(" list"); + pw.println(" list [-v] [-all]"); pw.println(" Prints all users on the system."); } } diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index f56231fc02af..9e86a4bd2880 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -229,6 +229,7 @@ public class DexManager { // If the dex file is the primary apk (or a split) and not isUsedByOtherApps // do not record it. This case does not bring any new usable information // and can be safely skipped. + dexPathIndex++; continue; } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 6fced8acf1df..d30afaac0967 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -48,6 +48,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.PermissionWhitelistFlags; import android.content.pm.PackageManagerInternal; @@ -2614,7 +2615,7 @@ public class PermissionManagerService { // Make sure all dynamic permissions have been assigned to a package, // and make sure there are no dangling permissions. - flags = updatePermissions(changingPkgName, changingPkg, flags); + flags = updatePermissions(changingPkgName, changingPkg, flags, callback); synchronized (mLock) { if (mBackgroundPermissions == null) { @@ -2664,7 +2665,8 @@ public class PermissionManagerService { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - private int updatePermissions(String packageName, PackageParser.Package pkg, int flags) { + private int updatePermissions(String packageName, PackageParser.Package pkg, int flags, + @Nullable PermissionCallback callback) { Set<BasePermission> needsUpdate = null; synchronized (mLock) { final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator(); @@ -2678,6 +2680,44 @@ public class PermissionManagerService { && (pkg == null || !hasPermission(pkg, bp.getName()))) { Slog.i(TAG, "Removing old permission tree: " + bp.getName() + " from package " + bp.getSourcePackageName()); + if (bp.isRuntime()) { + final int[] userIds = mUserManagerInt.getUserIds(); + final int numUserIds = userIds.length; + for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) { + final int userId = userIds[userIdNum]; + + mPackageManagerInt.forEachPackage((Package p) -> { + final String pName = p.packageName; + final ApplicationInfo appInfo = + mPackageManagerInt.getApplicationInfo(pName, 0, + Process.SYSTEM_UID, UserHandle.USER_SYSTEM); + if (appInfo != null + && appInfo.targetSdkVersion < Build.VERSION_CODES.M) { + return; + } + + final String permissionName = bp.getName(); + if (checkPermission(permissionName, pName, Process.SYSTEM_UID, + userId) == PackageManager.PERMISSION_GRANTED) { + try { + revokeRuntimePermission( + permissionName, + pName, + false, + userId, + callback); + } catch (IllegalArgumentException e) { + Slog.e(TAG, + "Failed to revoke " + + permissionName + + " from " + + pName, + e); + } + } + }); + } + } flags |= UPDATE_PERMISSIONS_ALL; it.remove(); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 48056b47e1bd..72706169cacb 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -23,6 +23,7 @@ import static android.app.AppOpsManager.OP_TOAST_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.Context.CONTEXT_RESTRICTED; import static android.content.Context.WINDOW_SERVICE; +import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE; import static android.content.pm.PackageManager.FEATURE_HDMI_CEC; import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; @@ -384,6 +385,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { BurnInProtectionHelper mBurnInProtectionHelper; private DisplayFoldController mDisplayFoldController; AppOpsManager mAppOpsManager; + private boolean mHasFeatureAuto; private boolean mHasFeatureWatch; private boolean mHasFeatureLeanback; private boolean mHasFeatureHdmiCec; @@ -1752,6 +1754,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mDisplayManager = mContext.getSystemService(DisplayManager.class); mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH); mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK); + mHasFeatureAuto = mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE); mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC); mAccessibilityShortcutController = new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId); @@ -5215,7 +5218,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { awakenDreams(); } - if (!isUserSetupComplete()) { + if (!mHasFeatureAuto && !isUserSetupComplete()) { Slog.i(TAG, "Not going home because user setup is in progress."); return; } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java index ff9129956a3b..a62bb74730f8 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -773,9 +773,9 @@ public class BatterySaverStateMachine { // Handle triggering the notification to show/hide when appropriate if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) { - runOnBgThread(this::triggerDynamicModeNotification); + triggerDynamicModeNotification(); } else if (!enable) { - runOnBgThread(this::hideDynamicModeNotification); + hideDynamicModeNotification(); } if (DEBUG) { @@ -787,33 +787,42 @@ public class BatterySaverStateMachine { @VisibleForTesting void triggerDynamicModeNotification() { - NotificationManager manager = mContext.getSystemService(NotificationManager.class); - ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID, - R.string.dynamic_mode_notification_channel_name); - - manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID, - buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID, - mContext.getResources().getString(R.string.dynamic_mode_notification_title), - R.string.dynamic_mode_notification_summary, - Intent.ACTION_POWER_USAGE_SUMMARY), - UserHandle.ALL); + // The current lock is the PowerManager lock, which sits very low in the service lock + // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock. + runOnBgThread(() -> { + NotificationManager manager = mContext.getSystemService(NotificationManager.class); + ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID, + R.string.dynamic_mode_notification_channel_name); + + manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID, + buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID, + mContext.getResources().getString( + R.string.dynamic_mode_notification_title), + R.string.dynamic_mode_notification_summary, + Intent.ACTION_POWER_USAGE_SUMMARY), + UserHandle.ALL); + }); } @VisibleForTesting void triggerStickyDisabledNotification() { - NotificationManager manager = mContext.getSystemService(NotificationManager.class); - ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID, - R.string.battery_saver_notification_channel_name); - - final String percentage = NumberFormat.getPercentInstance() - .format((double) mBatteryLevel / 100.0); - manager.notifyAsUser(TAG, STICKY_AUTO_DISABLED_NOTIFICATION_ID, - buildNotification(BATTERY_SAVER_NOTIF_CHANNEL_ID, - mContext.getResources().getString( - R.string.battery_saver_charged_notification_title, percentage), - R.string.battery_saver_off_notification_summary, - Settings.ACTION_BATTERY_SAVER_SETTINGS), - UserHandle.ALL); + // The current lock is the PowerManager lock, which sits very low in the service lock + // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock. + runOnBgThread(() -> { + NotificationManager manager = mContext.getSystemService(NotificationManager.class); + ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID, + R.string.battery_saver_notification_channel_name); + + final String percentage = NumberFormat.getPercentInstance() + .format((double) mBatteryLevel / 100.0); + manager.notifyAsUser(TAG, STICKY_AUTO_DISABLED_NOTIFICATION_ID, + buildNotification(BATTERY_SAVER_NOTIF_CHANNEL_ID, + mContext.getResources().getString( + R.string.battery_saver_charged_notification_title, percentage), + R.string.battery_saver_off_notification_summary, + Settings.ACTION_BATTERY_SAVER_SETTINGS), + UserHandle.ALL); + }); } private void ensureNotificationChannelExists(NotificationManager manager, @@ -854,8 +863,12 @@ public class BatterySaverStateMachine { } private void hideNotification(int notificationId) { - NotificationManager manager = mContext.getSystemService(NotificationManager.class); - manager.cancel(notificationId); + // The current lock is the PowerManager lock, which sits very low in the service lock + // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock. + runOnBgThread(() -> { + NotificationManager manager = mContext.getSystemService(NotificationManager.class); + manager.cancelAsUser(TAG, notificationId, UserHandle.ALL); + }); } private void setStickyActive(boolean active) { diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 83891f60d4f7..ce6a954e8fa2 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -16,6 +16,13 @@ package com.android.server.rollback; +import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; +import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; +import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; +import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; +import static android.util.StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; + +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; @@ -41,6 +48,7 @@ import android.util.StatsLog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.server.PackageWatchdog; +import com.android.server.PackageWatchdog.FailureReasons; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; @@ -106,11 +114,19 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } @Override - public boolean execute(VersionedPackage failedPackage) { + public boolean execute(VersionedPackage failedPackage, @FailureReasons int rollbackReason) { RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); VersionedPackage moduleMetadataPackage = getModuleMetadataPackage(); RollbackInfo rollback = getAvailableRollback(rollbackManager, failedPackage); + int reasonToLog = mapFailureReasonToMetric(rollbackReason); + final String failedPackageToLog; + if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + failedPackageToLog = SystemProperties.get( + "ro.init.updatable_crashing_process_name", ""); + } else { + failedPackageToLog = failedPackage.getPackageName(); + } if (rollback == null) { Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ " + failedPackage.getPackageName() + "] with versionCode: [" @@ -119,7 +135,8 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } logEvent(moduleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE, + reasonToLog, failedPackageToLog); LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> { int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, RollbackManager.STATUS_FAILURE); @@ -136,11 +153,13 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve moduleMetadataPackage); } else { logEvent(moduleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, + reasonToLog, failedPackageToLog); } } else { logEvent(moduleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, + reasonToLog, failedPackageToLog); } }); @@ -219,12 +238,14 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve } if (sessionInfo.isStagedSessionApplied()) { logEvent(oldModuleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); } else if (sessionInfo.isStagedSessionReady()) { // TODO: What do for staged session ready but not applied } else { logEvent(oldModuleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); } } @@ -303,12 +324,16 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve saveLastStagedRollbackId(rollbackId); logEvent(moduleMetadataPackage, StatsLog - .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED); + .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, + ""); mContext.getSystemService(PowerManager.class).reboot("Rollback staged install"); } else if (sessionInfo.isStagedSessionFailed() && markStagedSessionHandled(rollbackId)) { logEvent(moduleMetadataPackage, - StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE); + StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, + ""); mContext.unregisterReceiver(listener); } } @@ -355,11 +380,12 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve return rollbackId; } - private static void logEvent(@Nullable VersionedPackage moduleMetadataPackage, int type) { + private static void logEvent(@Nullable VersionedPackage moduleMetadataPackage, int type, + int rollbackReason, @NonNull String failingPackageName) { Slog.i(TAG, "Watchdog event occurred of type: " + type); if (moduleMetadataPackage != null) { StatsLog.logWatchdogRollbackOccurred(type, moduleMetadataPackage.getPackageName(), - moduleMetadataPackage.getVersionCode()); + moduleMetadataPackage.getVersionCode(), rollbackReason, failingPackageName); } } @@ -371,7 +397,7 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve mNumberOfNativeCrashPollsRemaining--; // Check if native watchdog reported a crash if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) { - execute(getModuleMetadataPackage()); + execute(getModuleMetadataPackage(), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); // we stop polling after an attempt to execute rollback, regardless of whether the // attempt succeeds or not } else { @@ -392,4 +418,20 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve + "and mitigate native crashes"); mHandler.post(()->checkAndMitigateNativeCrashes()); } + + private int mapFailureReasonToMetric(@FailureReasons int failureReason) { + switch (failureReason) { + case PackageWatchdog.FAILURE_REASON_NATIVE_CRASH: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; + case PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; + case PackageWatchdog.FAILURE_REASON_APP_CRASH: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; + case PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; + default: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; + } + } + } diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 0d28b463a622..cb5ddfea41b8 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -41,6 +41,7 @@ import android.app.AppOpsManager.HistoricalOps; import android.app.AppOpsManager.HistoricalOpsRequest; import android.app.AppOpsManager.HistoricalPackageOps; import android.app.AppOpsManager.HistoricalUidOps; +import android.app.INotificationManager; import android.app.ProcessMemoryState; import android.app.StatsManager; import android.bluetooth.BluetoothActivityEnergyInfo; @@ -140,6 +141,7 @@ import com.android.server.SystemService; import com.android.server.SystemServiceManager; import com.android.server.am.MemoryStatUtil.IonAllocations; import com.android.server.am.MemoryStatUtil.MemoryStat; +import com.android.server.notification.NotificationManagerService; import com.android.server.role.RoleManagerInternal; import com.android.server.storage.DiskStatsFileLogger; import com.android.server.storage.DiskStatsLoggingService; @@ -1624,14 +1626,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { if (statsFiles.size() != 1) { return; } - InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream( - statsFiles.get(0)); - int[] len = new int[1]; - byte[] stats = readFully(stream, len); - StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, - wallClockNanos); - e.writeStorage(Arrays.copyOf(stats, len[0])); - pulledData.add(e); + unpackStreamedData(tagId, elapsedNanos, wallClockNanos, pulledData, statsFiles); new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark).delete(); new File( @@ -1647,6 +1642,52 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } + private INotificationManager mNotificationManager = + INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + + private void pullNotificationStats(int reportId, int tagId, long elapsedNanos, + long wallClockNanos, + List<StatsLogEventWrapper> pulledData) { + final long callingToken = Binder.clearCallingIdentity(); + try { + // determine last pull tine. Copy file trick from pullProcessStats? + long lastNotificationStatsNs = wallClockNanos - + TimeUnit.NANOSECONDS.convert(1, TimeUnit.DAYS); + + List<ParcelFileDescriptor> statsFiles = new ArrayList<>(); + long notificationStatsNs = mNotificationManager.pullStats( + lastNotificationStatsNs, reportId, true, statsFiles); + if (statsFiles.size() != 1) { + return; + } + unpackStreamedData(tagId, elapsedNanos, wallClockNanos, pulledData, statsFiles); + } catch (IOException e) { + Log.e(TAG, "Getting notistats failed: ", e); + + } catch (RemoteException e) { + Log.e(TAG, "Getting notistats failed: ", e); + } catch (SecurityException e) { + Log.e(TAG, "Getting notistats failed: ", e); + } finally { + Binder.restoreCallingIdentity(callingToken); + } + + } + + static void unpackStreamedData(int tagId, long elapsedNanos, long wallClockNanos, + List<StatsLogEventWrapper> pulledData, List<ParcelFileDescriptor> statsFiles) + throws IOException { + InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream( + statsFiles.get(0)); + int[] len = new int[1]; + byte[] stats = readFully(stream, len); + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, + wallClockNanos); + e.writeStorage(Arrays.copyOf(stats, len[0])); + pulledData.add(e); + } + static byte[] readFully(InputStream stream, int[] outLen) throws IOException { int pos = 0; final int initialAvail = stream.available(); @@ -2476,6 +2517,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullAppOps(elapsedNanos, wallClockNanos, ret); break; } + case StatsLog.NOTIFICATION_REMOTE_VIEWS: { + pullNotificationStats(NotificationManagerService.REPORT_REMOTE_VIEWS, + tagId, elapsedNanos, wallClockNanos, ret); + break; + } default: Slog.w(TAG, "No such tagId data as " + tagId); return null; diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index 5f00148335a7..89a530514263 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -30,6 +30,7 @@ import android.os.UserHandle; import android.service.textclassifier.ITextClassifierCallback; import android.service.textclassifier.ITextClassifierService; import android.service.textclassifier.TextClassifierService; +import android.util.ArrayMap; import android.util.Slog; import android.util.SparseArray; import android.view.textclassifier.ConversationActions; @@ -54,6 +55,7 @@ import com.android.server.SystemService; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayDeque; +import java.util.Map; import java.util.Queue; /** @@ -119,6 +121,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi private final Object mLock; @GuardedBy("mLock") final SparseArray<UserState> mUserStates = new SparseArray<>(); + @GuardedBy("mLock") + private final Map<TextClassificationSessionId, Integer> mSessionUserIds = new ArrayMap<>(); private TextClassificationManagerService(Context context) { mContext = Preconditions.checkNotNull(context); @@ -127,15 +131,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onSuggestSelection( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, TextSelection.Request request, ITextClassifierCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); - validateInput(mContext, request.getCallingPackageName()); + final int userId = request.getUserId(); + validateInput(mContext, request.getCallingPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (!userState.bindLocked()) { callback.onFailure(); } else if (userState.isBoundLocked()) { @@ -150,15 +155,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onClassifyText( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, TextClassification.Request request, ITextClassifierCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); - validateInput(mContext, request.getCallingPackageName()); + final int userId = request.getUserId(); + validateInput(mContext, request.getCallingPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (!userState.bindLocked()) { callback.onFailure(); } else if (userState.isBoundLocked()) { @@ -173,15 +179,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onGenerateLinks( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, TextLinks.Request request, ITextClassifierCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); - validateInput(mContext, request.getCallingPackageName()); + final int userId = request.getUserId(); + validateInput(mContext, request.getCallingPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (!userState.bindLocked()) { callback.onFailure(); } else if (userState.isBoundLocked()) { @@ -196,12 +203,14 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onSelectionEvent( - TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException { + @Nullable TextClassificationSessionId sessionId, SelectionEvent event) + throws RemoteException { Preconditions.checkNotNull(event); - validateInput(mContext, event.getPackageName()); + final int userId = event.getUserId(); + validateInput(mContext, event.getPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (userState.isBoundLocked()) { userState.mService.onSelectionEvent(sessionId, event); } else { @@ -213,16 +222,19 @@ public final class TextClassificationManagerService extends ITextClassifierServi } @Override public void onTextClassifierEvent( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, TextClassifierEvent event) throws RemoteException { Preconditions.checkNotNull(event); final String packageName = event.getEventContext() == null ? null : event.getEventContext().getPackageName(); - validateInput(mContext, packageName); + final int userId = event.getEventContext() == null + ? UserHandle.getCallingUserId() + : event.getEventContext().getUserId(); + validateInput(mContext, packageName, userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (userState.isBoundLocked()) { userState.mService.onTextClassifierEvent(sessionId, event); } else { @@ -235,15 +247,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onDetectLanguage( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, TextLanguage.Request request, ITextClassifierCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); - validateInput(mContext, request.getCallingPackageName()); + final int userId = request.getUserId(); + validateInput(mContext, request.getCallingPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (!userState.bindLocked()) { callback.onFailure(); } else if (userState.isBoundLocked()) { @@ -258,15 +271,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onSuggestConversationActions( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, ConversationActions.Request request, ITextClassifierCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); - validateInput(mContext, request.getCallingPackageName()); + final int userId = request.getUserId(); + validateInput(mContext, request.getCallingPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (!userState.bindLocked()) { callback.onFailure(); } else if (userState.isBoundLocked()) { @@ -285,13 +299,15 @@ public final class TextClassificationManagerService extends ITextClassifierServi throws RemoteException { Preconditions.checkNotNull(sessionId); Preconditions.checkNotNull(classificationContext); - validateInput(mContext, classificationContext.getPackageName()); + final int userId = classificationContext.getUserId(); + validateInput(mContext, classificationContext.getPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (userState.isBoundLocked()) { userState.mService.onCreateTextClassificationSession( classificationContext, sessionId); + mSessionUserIds.put(sessionId, userId); } else { userState.mPendingRequests.add(new PendingRequest( () -> onCreateTextClassificationSession(classificationContext, sessionId), @@ -306,9 +322,15 @@ public final class TextClassificationManagerService extends ITextClassifierServi Preconditions.checkNotNull(sessionId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + final int userId = mSessionUserIds.containsKey(sessionId) + ? mSessionUserIds.get(sessionId) + : UserHandle.getCallingUserId(); + validateInput(mContext, null /* packageName */, userId); + + UserState userState = getUserStateLocked(userId); if (userState.isBoundLocked()) { userState.mService.onDestroyTextClassificationSession(sessionId); + mSessionUserIds.remove(sessionId); } else { userState.mPendingRequests.add(new PendingRequest( () -> onDestroyTextClassificationSession(sessionId), @@ -318,11 +340,6 @@ public final class TextClassificationManagerService extends ITextClassifierServi } @GuardedBy("mLock") - private UserState getCallingUserStateLocked() { - return getUserStateLocked(UserHandle.getCallingUserId()); - } - - @GuardedBy("mLock") private UserState getUserStateLocked(int userId) { UserState result = mUserStates.get(userId); if (result == null) { @@ -356,6 +373,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi pw.decreaseIndent(); } } + pw.println("Number of active sessions: " + mSessionUserIds.size()); } } @@ -420,20 +438,32 @@ public final class TextClassificationManagerService extends ITextClassifierServi e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage())); } - private static void validateInput(Context context, @Nullable String packageName) + private static void validateInput( + Context context, @Nullable String packageName, @UserIdInt int userId) throws RemoteException { - if (packageName == null) return; try { - final int packageUid = context.getPackageManager() - .getPackageUidAsUser(packageName, UserHandle.getCallingUserId()); - final int callingUid = Binder.getCallingUid(); - Preconditions.checkArgument(callingUid == packageUid - // Trust the system process: - || callingUid == android.os.Process.SYSTEM_UID); + if (packageName != null) { + final int packageUid = context.getPackageManager() + .getPackageUidAsUser(packageName, UserHandle.getCallingUserId()); + final int callingUid = Binder.getCallingUid(); + Preconditions.checkArgument(callingUid == packageUid + // Trust the system process: + || callingUid == android.os.Process.SYSTEM_UID, + "Invalid package name. Package=" + packageName + + ", CallingUid=" + callingUid); + } + + Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId"); + final int callingUserId = UserHandle.getCallingUserId(); + if (callingUserId != userId) { + context.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId); + } } catch (Exception e) { - throw new RemoteException( - String.format("Invalid package: name=%s, error=%s", packageName, e)); + throw new RemoteException("Invalid request: " + e.getMessage(), e, + /* enableSuppression */ true, /* writableStackTrace */ true); } } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index c96479543b3a..468b806d6dce 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -61,10 +61,10 @@ public interface TimeDetectorStrategy { /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */ void acquireWakeLock(); - /** Returns the elapsedRealtimeMillis clock value. The WakeLock must be held. */ + /** Returns the elapsedRealtimeMillis clock value. */ long elapsedRealtimeMillis(); - /** Returns the system clock value. The WakeLock must be held. */ + /** Returns the system clock value. */ long systemClockMillis(); /** Sets the device system clock. The WakeLock must be held. */ diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java index 9b89d9437fc3..19484db149b1 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java @@ -88,13 +88,11 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat @Override public long elapsedRealtimeMillis() { - checkWakeLockHeld(); return SystemClock.elapsedRealtime(); } @Override public long systemClockMillis() { - checkWakeLockHeld(); return System.currentTimeMillis(); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 991c09a97bf5..4e136af0fdc3 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1286,6 +1286,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub saveSettingsLocked(mWallpaper.userId); } FgThread.getHandler().removeCallbacks(mResetRunnable); + mContext.getMainThreadHandler().removeCallbacks(this::tryToRebind); } } } @@ -1328,6 +1329,34 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + private void tryToRebind() { + synchronized (mLock) { + if (mWallpaper.wallpaperUpdating) { + return; + } + final ComponentName wpService = mWallpaper.wallpaperComponent; + // The broadcast of package update could be delayed after service disconnected. Try + // to re-bind the service for 10 seconds. + if (bindWallpaperComponentLocked( + wpService, true, false, mWallpaper, null)) { + mWallpaper.connection.scheduleTimeoutLocked(); + } else if (SystemClock.uptimeMillis() - mWallpaper.lastDiedTime + < WALLPAPER_RECONNECT_TIMEOUT_MS) { + // Bind fail without timeout, schedule rebind + Slog.w(TAG, "Rebind fail! Try again later"); + mContext.getMainThreadHandler().postDelayed(this::tryToRebind, 1000); + } else { + // Timeout + Slog.w(TAG, "Reverting to built-in wallpaper!"); + clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null); + final String flattened = wpService.flattenToString(); + EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED, + flattened.substring(0, Math.min(flattened.length(), + MAX_WALLPAPER_COMPONENT_LOG_LENGTH))); + } + } + } + private void processDisconnect(final ServiceConnection connection) { synchronized (mLock) { // The wallpaper disappeared. If this isn't a system-default one, track @@ -1351,20 +1380,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null); } else { mWallpaper.lastDiedTime = SystemClock.uptimeMillis(); - - clearWallpaperComponentLocked(mWallpaper); - if (bindWallpaperComponentLocked( - wpService, false, false, mWallpaper, null)) { - mWallpaper.connection.scheduleTimeoutLocked(); - } else { - Slog.w(TAG, "Reverting to built-in wallpaper!"); - clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null); - } + tryToRebind(); } - final String flattened = wpService.flattenToString(); - EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED, - flattened.substring(0, Math.min(flattened.length(), - MAX_WALLPAPER_COMPONENT_LOG_LENGTH))); } } else { if (DEBUG_LIVE) { diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 99a9db316c63..2d835d3bb1b1 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -865,6 +865,8 @@ public class DisplayPolicy { if (canToastShowWhenLocked(callingPid)) { attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; } + // Toasts can't be clickable + attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; break; } @@ -2812,7 +2814,11 @@ public class DisplayPolicy { mHandler.post(() -> { final int displayId = getDisplayId(); getStatusBarManagerInternal().onDisplayReady(displayId); - LocalServices.getService(WallpaperManagerInternal.class).onDisplayReady(displayId); + final WallpaperManagerInternal wpMgr = LocalServices + .getService(WallpaperManagerInternal.class); + if (wpMgr != null) { + wpMgr.onDisplayReady(displayId); + } }); } diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index f8f6334b04dc..d5f403f85621 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -113,7 +113,7 @@ class DragDropController { final WindowState callingWin = mService.windowForClientLocked( null, window, false); - if (callingWin == null) { + if (callingWin == null || callingWin.cantReceiveTouchInput()) { Slog.w(TAG_WM, "Bad requesting window " + window); return null; // !!! TODO: throw here? } @@ -167,8 +167,7 @@ class DragDropController { final SurfaceControl surfaceControl = mDragState.mSurfaceControl; if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> OPEN TRANSACTION performDrag"); - final SurfaceControl.Transaction transaction = - callingWin.getPendingTransaction(); + final SurfaceControl.Transaction transaction = mDragState.mTransaction; transaction.setAlpha(surfaceControl, mDragState.mOriginalAlpha); transaction.setPosition( surfaceControl, touchX - thumbCenterX, touchY - thumbCenterY); diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 553b0ffa6999..bdb706e47439 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -120,7 +120,7 @@ class DragState { // A surface used to catch input events for the drag-and-drop operation. SurfaceControl mInputSurface; - private final SurfaceControl.Transaction mTransaction; + final SurfaceControl.Transaction mTransaction; private final Rect mTmpClipRect = new Rect(); @@ -129,7 +129,6 @@ class DragState { * {@code true} when {@link #closeLocked()} is called. */ private boolean mIsClosing; - IBinder mTransferTouchFromToken; DragState(WindowManagerService service, DragDropController controller, IBinder token, SurfaceControl surface, int flags, IBinder localWin) { @@ -173,10 +172,9 @@ class DragState { mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y); mTransaction.setWindowCrop(mInputSurface, mTmpClipRect); - mTransaction.transferTouchFocus(mTransferTouchFromToken, h.token); - mTransferTouchFromToken = null; - // syncInputWindows here to ensure the input channel isn't removed before the transfer. + // syncInputWindows here to ensure the input window info is sent before the + // transferTouchFocus is called. mTransaction.syncInputWindows(); mTransaction.apply(); } diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index 2fc64eaf8c97..7d50ac668740 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -50,6 +50,7 @@ import android.view.InputDevice; import android.view.InputEvent; import android.view.InputWindowHandle; import android.view.MotionEvent; +import android.view.SurfaceControl; import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; @@ -307,7 +308,8 @@ class TaskPositioner implements IBinder.DeathRecipient { mDisplayContent.pauseRotationLocked(); // Notify InputMonitor to take mDragWindowHandle. - mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/); + mDisplayContent.getInputMonitor().updateInputWindowsImmediately(); + new SurfaceControl.Transaction().syncInputWindows().apply(); mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics); mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics); diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java index 2441954012e5..56b3bba2a431 100644 --- a/services/core/java/com/android/server/wm/TaskPositioningController.java +++ b/services/core/java/com/android/server/wm/TaskPositioningController.java @@ -24,7 +24,6 @@ import android.app.IActivityTaskManager; import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Slog; @@ -51,7 +50,6 @@ class TaskPositioningController { private @Nullable TaskPositioner mTaskPositioner; private final Rect mTmpClipRect = new Rect(); - private IBinder mTransferTouchFromToken; boolean isPositioningLocked() { return mTaskPositioner != null; @@ -104,8 +102,6 @@ class TaskPositioningController { mTmpClipRect.set(0, 0, p.x, p.y); t.setWindowCrop(mInputSurface, mTmpClipRect); - t.transferTouchFocus(mTransferTouchFromToken, h.token); - mTransferTouchFromToken = null; } boolean startMovingTask(IWindow window, float startX, float startY) { @@ -168,6 +164,7 @@ class TaskPositioningController { mPositioningDisplay = displayContent; mTaskPositioner = TaskPositioner.create(mService); + mTaskPositioner.register(displayContent); // We need to grab the touch focus so that the touch events during the // resizing/scrolling are not sent to the app. 'win' is the main window @@ -178,8 +175,12 @@ class TaskPositioningController { && displayContent.mCurrentFocus.mAppToken == win.mAppToken) { transferFocusFromWin = displayContent.mCurrentFocus; } - mTransferTouchFromToken = transferFocusFromWin.mInputChannel.getToken(); - mTaskPositioner.register(displayContent); + if (!mInputManager.transferTouchFocus( + transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) { + Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus"); + cleanUpTaskPositioner(); + return false; + } mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY); return true; diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 40bec148b33b..b407ac5e60cf 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -156,9 +156,8 @@ public abstract class WindowManagerInternal { default boolean registerInputChannel( DragState state, Display display, InputManagerService service, InputChannel source) { - state.mTransferTouchFromToken = source.getToken(); state.register(display); - return true; + return service.transferTouchFocus(source, state.getInputChannel()); } /** diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index fb3076ba9ddd..d331d1f58b2d 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -1571,6 +1571,27 @@ static void nativeSetSystemUiVisibility(JNIEnv* /* env */, im->setSystemUiVisibility(visibility); } +static jboolean nativeTransferTouchFocus(JNIEnv* env, + jclass /* clazz */, jlong ptr, jobject fromChannelObj, jobject toChannelObj) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + sp<InputChannel> fromChannel = + android_view_InputChannel_getInputChannel(env, fromChannelObj); + sp<InputChannel> toChannel = + android_view_InputChannel_getInputChannel(env, toChannelObj); + + if (fromChannel == nullptr || toChannel == nullptr) { + return JNI_FALSE; + } + + if (im->getInputManager()->getDispatcher()-> + transferTouchFocus(fromChannel->getToken(), toChannel->getToken())) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } +} + static void nativeSetPointerSpeed(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint speed) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); @@ -1770,6 +1791,8 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*) nativeSetInputDispatchMode }, { "nativeSetSystemUiVisibility", "(JI)V", (void*) nativeSetSystemUiVisibility }, + { "nativeTransferTouchFocus", "(JLandroid/view/InputChannel;Landroid/view/InputChannel;)Z", + (void*) nativeTransferTouchFocus }, { "nativeSetPointerSpeed", "(JI)V", (void*) nativeSetPointerSpeed }, { "nativeSetShowTouches", "(JZ)V", diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b5b21f4189b4..e58f51325351 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1513,6 +1513,8 @@ public final class SystemServer { traceBeginAndSlog("StartWallpaperManagerService"); mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS); traceEnd(); + } else { + Slog.i(TAG, "Wallpaper service disabled by config"); } traceBeginAndSlog("StartAudioService"); diff --git a/services/net/Android.bp b/services/net/Android.bp index 9c7cfc197bba..cf84bdfb5b6f 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -23,7 +23,6 @@ filegroup { name: "services-tethering-shared-srcs", srcs: [ ":framework-annotations", - "java/android/net/util/NetdService.java", "java/android/net/util/NetworkConstants.java", ], visibility: ["//frameworks/base/packages/Tethering"], diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index 32d7d026ff10..1200c0c8c7bd 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -20,6 +20,7 @@ <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> <uses-permission android:name="android.permission.HARDWARE_TEST"/> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <application android:testOnly="true" android:debuggable="true"> diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java index 1e29ed6ba5f3..fafd4e83c542 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -24,6 +24,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow; @@ -35,6 +36,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.AlarmManagerService.ACTIVE_INDEX; import static com.android.server.AlarmManagerService.AlarmHandler.APP_STANDBY_BUCKET_CHANGED; import static com.android.server.AlarmManagerService.AlarmHandler.APP_STANDBY_PAROLE_CHANGED; +import static com.android.server.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED; import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_LONG_TIME; import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_SHORT_TIME; import static com.android.server.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION; @@ -79,9 +81,9 @@ import android.provider.Settings; import android.util.Log; import android.util.SparseArray; -import androidx.test.filters.FlakyTest; import androidx.test.runner.AndroidJUnit4; +import com.android.dx.mockito.inline.extended.MockedVoidMethod; import com.android.internal.annotations.GuardedBy; import org.junit.After; @@ -166,7 +168,6 @@ public class AlarmManagerServiceTest { } public class Injector extends AlarmManagerService.Injector { - boolean mIsAutomotiveOverride; Injector(Context context) { super(context); @@ -256,6 +257,9 @@ public class AlarmManagerServiceTest { .when(() -> LocalServices.getService(DeviceIdleController.LocalService.class)); doReturn(mUsageStatsManagerInternal).when( () -> LocalServices.getService(UsageStatsManagerInternal.class)); + doCallRealMethod().when((MockedVoidMethod) () -> + LocalServices.addService(eq(AlarmManagerInternal.class), any())); + doCallRealMethod().when(() -> LocalServices.getService(AlarmManagerInternal.class)); when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong())) .thenReturn(STANDBY_BUCKET_ACTIVE); @@ -443,7 +447,6 @@ public class AlarmManagerServiceTest { assertEquals(expectedTriggerTime, mTestTimer.getElapsed()); } - @FlakyTest(bugId = 130313408) @Test public void testEarliestAlarmSet() { final PendingIntent pi6 = getNewMockPendingIntent(); @@ -661,11 +664,15 @@ public class AlarmManagerServiceTest { anyLong())).thenReturn(bucket); mAppStandbyListener.onAppIdleStateChanged(TEST_CALLING_PACKAGE, UserHandle.getUserId(TEST_CALLING_UID), false, bucket, 0); + assertAndHandleMessageSync(APP_STANDBY_BUCKET_CHANGED); + } + + private void assertAndHandleMessageSync(int what) { final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); verify(mService.mHandler, atLeastOnce()).sendMessage(messageCaptor.capture()); final Message lastMessage = messageCaptor.getValue(); assertEquals("Unexpected message send to handler", lastMessage.what, - APP_STANDBY_BUCKET_CHANGED); + what); mService.mHandler.handleMessage(lastMessage); } @@ -725,12 +732,7 @@ public class AlarmManagerServiceTest { private void assertAndHandleParoleChanged(boolean parole) { mAppStandbyListener.onParoleStateChanged(parole); - final ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); - verify(mService.mHandler, atLeastOnce()).sendMessage(messageCaptor.capture()); - final Message lastMessage = messageCaptor.getValue(); - assertEquals("Unexpected message send to handler", lastMessage.what, - APP_STANDBY_PAROLE_CHANGED); - mService.mHandler.handleMessage(lastMessage); + assertAndHandleMessageSync(APP_STANDBY_PAROLE_CHANGED); } @Test @@ -1033,12 +1035,13 @@ public class AlarmManagerServiceTest { } @Test - public void alarmCountOnPendingIntentCancel() { + public void alarmCountOnRemoveForCanceled() { + final AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class); final PendingIntent pi = getNewMockPendingIntent(); - setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 123, pi); - verify(pi).registerCancelListener(mService.mOperationCancelListener); + setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + 12345, pi); assertEquals(1, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); - mService.mOperationCancelListener.onCancelled(pi); + ami.remove(pi); + assertAndHandleMessageSync(REMOVE_FOR_CANCELED); assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); } @@ -1047,5 +1050,6 @@ public class AlarmManagerServiceTest { if (mMockingSession != null) { mMockingSession.finishMocking(); } + LocalServices.removeServiceForTest(AlarmManagerInternal.class); } } diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index e51ee947cba1..fe15ff423a86 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -17,6 +17,7 @@ package com.android.server; import static androidx.test.InstrumentationRegistry.getContext; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; @@ -31,6 +32,7 @@ import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE; import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE; import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE; import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK; +import static com.android.server.DeviceIdleController.MSG_REPORT_STATIONARY_STATUS; import static com.android.server.DeviceIdleController.STATE_ACTIVE; import static com.android.server.DeviceIdleController.STATE_IDLE; import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE; @@ -51,9 +53,11 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import android.app.ActivityManagerInternal; @@ -63,13 +67,18 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.hardware.TriggerEvent; +import android.hardware.TriggerEventListener; import android.location.LocationManager; import android.location.LocationProvider; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Handler; import android.os.Looper; +import android.os.Message; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; @@ -85,11 +94,13 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Answers; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoSession; +import org.mockito.invocation.InvocationOnMock; import org.mockito.quality.Strictness; +import org.mockito.stubbing.Answer; /** * Tests for {@link com.android.server.DeviceIdleController}. @@ -97,6 +108,7 @@ import org.mockito.quality.Strictness; @RunWith(AndroidJUnit4.class) public class DeviceIdleControllerTest { private DeviceIdleController mDeviceIdleController; + private DeviceIdleController.MyHandler mHandler; private AnyMotionDetectorForTest mAnyMotionDetector; private AppStateTrackerForTest mAppStateTracker; private DeviceIdleController.Constants mConstants; @@ -110,8 +122,6 @@ public class DeviceIdleControllerTest { @Mock private ContentResolver mContentResolver; @Mock - private DeviceIdleController.MyHandler mHandler; - @Mock private IActivityManager mIActivityManager; @Mock private LocationManager mLocationManager; @@ -122,12 +132,16 @@ public class DeviceIdleControllerTest { @Mock private PowerManagerInternal mPowerManagerInternal; @Mock + private Sensor mMotionSensor; + @Mock private SensorManager mSensorManager; class InjectorForTest extends DeviceIdleController.Injector { ConnectivityService connectivityService; LocationManager locationManager; ConstraintController constraintController; + // Freeze time for testing. + long nowElapsed; InjectorForTest(Context ctx) { super(ctx); @@ -155,16 +169,43 @@ public class DeviceIdleControllerTest { } @Override + long getElapsedRealtime() { + return nowElapsed; + } + + @Override LocationManager getLocationManager() { return locationManager; } @Override DeviceIdleController.MyHandler getHandler(DeviceIdleController controller) { + if (mHandler == null) { + mHandler = controller.new MyHandler(getContext().getMainLooper()); + spyOn(mHandler); + doNothing().when(mHandler).handleMessage(argThat((message) -> + message.what != MSG_REPORT_STATIONARY_STATUS)); + doAnswer(new Answer<Boolean>() { + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + Message msg = invocation.getArgument(0); + mHandler.handleMessage(msg); + return true; + } + }).when(mHandler).sendMessageDelayed( + argThat((message) -> message.what == MSG_REPORT_STATIONARY_STATUS), + anyLong()); + } + return mHandler; } @Override + Sensor getMotionSensor() { + return mMotionSensor; + } + + @Override PowerManager getPowerManager() { return mPowerManager; } @@ -226,6 +267,19 @@ public class DeviceIdleControllerTest { } } + private class StationaryListenerForTest implements DeviceIdleController.StationaryListener { + boolean motionExpected = false; + boolean isStationary = false; + + @Override + public void onDeviceStationaryChanged(boolean isStationary) { + if (isStationary == motionExpected) { + fail("Unexpected device stationary status: " + isStationary); + } + this.isStationary = isStationary; + } + } + @Before public void setUp() { mMockingSession = mockitoSession() @@ -255,8 +309,6 @@ public class DeviceIdleControllerTest { doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt()); mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper()); mAnyMotionDetector = new AnyMotionDetectorForTest(); - mHandler = mock(DeviceIdleController.MyHandler.class, Answers.RETURNS_DEEP_STUBS); - doNothing().when(mHandler).handleMessage(any()); mInjector = new InjectorForTest(getContext()); doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any()); @@ -1607,6 +1659,202 @@ public class DeviceIdleControllerTest { 1.0f, curfactor, delta); } + @Test + public void testStationaryDetection_QuickDozeOff() { + setQuickDozeEnabled(false); + enterDeepState(STATE_IDLE); + // Regular progression through states, so time should have increased appropriately. + mInjector.nowElapsed += mConstants.IDLE_AFTER_INACTIVE_TIMEOUT + mConstants.SENSING_TIMEOUT + + mConstants.LOCATING_TIMEOUT; + + StationaryListenerForTest stationaryListener = new StationaryListenerForTest(); + + mDeviceIdleController.registerStationaryListener(stationaryListener); + + // Go to IDLE_MAINTENANCE + mDeviceIdleController.stepIdleStateLocked("testing"); + + // Back to IDLE + mDeviceIdleController.stepIdleStateLocked("testing"); + assertTrue(stationaryListener.isStationary); + + // Test motion + stationaryListener.motionExpected = true; + mDeviceIdleController.mMotionListener.onTrigger(null); + assertFalse(stationaryListener.isStationary); + } + + @Test + public void testStationaryDetection_QuickDozeOn_NoMotion() { + // Short timeout for testing. + mConstants.MOTION_INACTIVE_TIMEOUT = 6000L; + doReturn(Sensor.REPORTING_MODE_ONE_SHOT).when(mMotionSensor).getReportingMode(); + doReturn(true).when(mSensorManager) + .requestTriggerSensor(eq(mDeviceIdleController.mMotionListener), eq(mMotionSensor)); + setAlarmSoon(false); + enterDeepState(STATE_QUICK_DOZE_DELAY); + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE); + // Quick doze progression through states, so time should have increased appropriately. + mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT; + final ArgumentCaptor<AlarmManager.OnAlarmListener> motionAlarmListener = ArgumentCaptor + .forClass(AlarmManager.OnAlarmListener.class); + final ArgumentCaptor<AlarmManager.OnAlarmListener> motionRegistrationAlarmListener = + ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); + doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), + motionAlarmListener.capture(), any()); + doNothing().when(mAlarmManager).set(anyInt(), anyLong(), + eq("DeviceIdleController.motion_registration"), + motionRegistrationAlarmListener.capture(), any()); + + StationaryListenerForTest stationaryListener = new StationaryListenerForTest(); + spyOn(stationaryListener); + InOrder inOrder = inOrder(stationaryListener); + + stationaryListener.motionExpected = true; + mDeviceIdleController.registerStationaryListener(stationaryListener); + inOrder.verify(stationaryListener, timeout(1000L).times(1)) + .onDeviceStationaryChanged(eq(false)); + assertFalse(stationaryListener.isStationary); + + // Go to IDLE_MAINTENANCE + mDeviceIdleController.stepIdleStateLocked("testing"); + + mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2; + + // Back to IDLE + mDeviceIdleController.stepIdleStateLocked("testing"); + + // Now enough time has passed. + mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT; + stationaryListener.motionExpected = false; + motionAlarmListener.getValue().onAlarm(); + inOrder.verify(stationaryListener, timeout(1000L).times(1)) + .onDeviceStationaryChanged(eq(true)); + assertTrue(stationaryListener.isStationary); + + stationaryListener.motionExpected = true; + mDeviceIdleController.mMotionListener.onTrigger(null); + inOrder.verify(stationaryListener, timeout(1000L).times(1)) + .onDeviceStationaryChanged(eq(false)); + assertFalse(stationaryListener.isStationary); + + // Since we're in quick doze, the device shouldn't stop idling. + verifyStateConditions(STATE_IDLE); + + // Go to IDLE_MAINTENANCE + mDeviceIdleController.stepIdleStateLocked("testing"); + + motionRegistrationAlarmListener.getValue().onAlarm(); + mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT / 2; + + // Back to IDLE + stationaryListener.motionExpected = false; + mDeviceIdleController.stepIdleStateLocked("testing"); + verify(mSensorManager, + timeout(mConstants.MOTION_INACTIVE_TIMEOUT).times(2)) + .requestTriggerSensor(eq(mDeviceIdleController.mMotionListener), eq(mMotionSensor)); + + // Now enough time has passed. + mInjector.nowElapsed += mConstants.MOTION_INACTIVE_TIMEOUT; + motionAlarmListener.getValue().onAlarm(); + inOrder.verify(stationaryListener, + timeout(mConstants.MOTION_INACTIVE_TIMEOUT).times(1)) + .onDeviceStationaryChanged(eq(true)); + assertTrue(stationaryListener.isStationary); + } + + @Test + public void testStationaryDetection_QuickDozeOn_OneShot() { + // Short timeout for testing. + mConstants.MOTION_INACTIVE_TIMEOUT = 6000L; + doReturn(Sensor.REPORTING_MODE_ONE_SHOT).when(mMotionSensor).getReportingMode(); + setAlarmSoon(false); + enterDeepState(STATE_QUICK_DOZE_DELAY); + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE); + // Quick doze progression through states, so time should have increased appropriately. + mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT; + final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor + .forClass(AlarmManager.OnAlarmListener.class); + doNothing().when(mAlarmManager) + .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any()); + doNothing().when(mAlarmManager).set(anyInt(), anyLong(), + eq("DeviceIdleController.motion_registration"), + alarmListener.capture(), any()); + ArgumentCaptor<TriggerEventListener> listenerCaptor = + ArgumentCaptor.forClass(TriggerEventListener.class); + + StationaryListenerForTest stationaryListener = new StationaryListenerForTest(); + spyOn(stationaryListener); + InOrder inOrder = inOrder(stationaryListener, mSensorManager); + + stationaryListener.motionExpected = true; + mDeviceIdleController.registerStationaryListener(stationaryListener); + inOrder.verify(stationaryListener, timeout(1000L).times(1)) + .onDeviceStationaryChanged(eq(false)); + assertFalse(stationaryListener.isStationary); + inOrder.verify(mSensorManager) + .requestTriggerSensor(listenerCaptor.capture(), eq(mMotionSensor)); + final TriggerEventListener listener = listenerCaptor.getValue(); + + // Trigger motion + listener.onTrigger(mock(TriggerEvent.class)); + inOrder.verify(stationaryListener, timeout(1000L).times(1)) + .onDeviceStationaryChanged(eq(false)); + + // Make sure the listener is re-registered. + alarmListener.getValue().onAlarm(); + inOrder.verify(mSensorManager).requestTriggerSensor(eq(listener), eq(mMotionSensor)); + } + + @Test + public void testStationaryDetection_QuickDozeOn_MultiShot() { + // Short timeout for testing. + mConstants.MOTION_INACTIVE_TIMEOUT = 6000L; + doReturn(Sensor.REPORTING_MODE_CONTINUOUS).when(mMotionSensor).getReportingMode(); + setAlarmSoon(false); + enterDeepState(STATE_QUICK_DOZE_DELAY); + mDeviceIdleController.stepIdleStateLocked("testing"); + verifyStateConditions(STATE_IDLE); + // Quick doze progression through states, so time should have increased appropriately. + mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT; + final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor + .forClass(AlarmManager.OnAlarmListener.class); + doNothing().when(mAlarmManager) + .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any()); + doNothing().when(mAlarmManager).set(anyInt(), anyLong(), + eq("DeviceIdleController.motion_registration"), + alarmListener.capture(), any()); + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + + StationaryListenerForTest stationaryListener = new StationaryListenerForTest(); + spyOn(stationaryListener); + InOrder inOrder = inOrder(stationaryListener, mSensorManager); + + stationaryListener.motionExpected = true; + mDeviceIdleController.registerStationaryListener(stationaryListener); + inOrder.verify(stationaryListener, timeout(1000L).times(1)) + .onDeviceStationaryChanged(eq(false)); + assertFalse(stationaryListener.isStationary); + inOrder.verify(mSensorManager) + .registerListener(listenerCaptor.capture(), eq(mMotionSensor), + eq(SensorManager.SENSOR_DELAY_NORMAL)); + final SensorEventListener listener = listenerCaptor.getValue(); + + // Trigger motion + listener.onSensorChanged(mock(SensorEvent.class)); + inOrder.verify(stationaryListener, timeout(1000L).times(1)) + .onDeviceStationaryChanged(eq(false)); + + // Make sure the listener is re-registered. + alarmListener.getValue().onAlarm(); + inOrder.verify(mSensorManager) + .registerListener(eq(listener), eq(mMotionSensor), + eq(SensorManager.SENSOR_DELAY_NORMAL)); + } + private void enterDeepState(int state) { switch (state) { case STATE_ACTIVE: diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java new file mode 100644 index 000000000000..3975f0baa22a --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.app.AppGlobals; +import android.app.PendingIntent; +import android.content.Intent; +import android.content.pm.IPackageManager; +import android.os.Looper; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.AlarmManagerInternal; +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.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +@RunWith(AndroidJUnit4.class) +public class PendingIntentControllerTest { + private static final String TEST_PACKAGE_NAME = "test-package-1"; + private static final int TEST_CALLING_UID = android.os.Process.myUid(); + private static final Intent[] TEST_INTENTS = new Intent[]{new Intent("com.test.intent")}; + + @Mock + private UserController mUserController; + @Mock + private AlarmManagerInternal mAlarmManagerInternal; + @Mock + private ActivityManagerInternal mActivityManagerInternal; + @Mock + private IPackageManager mIPackageManager; + + private MockitoSession mMockingSession; + private PendingIntentController mPendingIntentController; + + @Before + public void setUp() throws Exception { + mMockingSession = mockitoSession() + .initMocks(this) + .mockStatic(LocalServices.class) + .mockStatic(AppGlobals.class) + .strictness(Strictness.LENIENT) // Needed to stub LocalServices.getService twice + .startMocking(); + doReturn(mAlarmManagerInternal).when( + () -> LocalServices.getService(AlarmManagerInternal.class)); + doReturn(mActivityManagerInternal).when( + () -> LocalServices.getService(ActivityManagerInternal.class)); + doReturn(mIPackageManager).when(() -> AppGlobals.getPackageManager()); + when(mIPackageManager.getPackageUid(eq(TEST_PACKAGE_NAME), anyInt(), anyInt())).thenReturn( + TEST_CALLING_UID); + mPendingIntentController = new PendingIntentController(Looper.getMainLooper(), + mUserController); + mPendingIntentController.onActivityManagerInternalAdded(); + } + + private PendingIntentRecord createPendingIntentRecord(int flags) { + return mPendingIntentController.getIntentSender(ActivityManager.INTENT_SENDER_BROADCAST, + TEST_PACKAGE_NAME, TEST_CALLING_UID, 0, null, null, 0, TEST_INTENTS, null, flags, + null); + } + + @Test + public void alarmsRemovedOnCancel() { + final PendingIntentRecord pir = createPendingIntentRecord(0); + mPendingIntentController.cancelIntentSender(pir); + final ArgumentCaptor<PendingIntent> piCaptor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAlarmManagerInternal).remove(piCaptor.capture()); + assertEquals("Wrong target for pending intent passed to alarm manager", pir, + piCaptor.getValue().getTarget()); + } + + @Test + public void alarmsRemovedOnRecreateWithCancelCurrent() { + final PendingIntentRecord pir = createPendingIntentRecord(0); + createPendingIntentRecord(PendingIntent.FLAG_CANCEL_CURRENT); + final ArgumentCaptor<PendingIntent> piCaptor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAlarmManagerInternal).remove(piCaptor.capture()); + assertEquals("Wrong target for pending intent passed to alarm manager", pir, + piCaptor.getValue().getTarget()); + } + + @Test + public void alarmsRemovedOnSendingOneShot() { + final PendingIntentRecord pir = createPendingIntentRecord(PendingIntent.FLAG_ONE_SHOT); + pir.send(0, null, null, null, null, null, null); + final ArgumentCaptor<PendingIntent> piCaptor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAlarmManagerInternal).remove(piCaptor.capture()); + assertEquals("Wrong target for pending intent passed to alarm manager", pir, + piCaptor.getValue().getTarget()); + } + + @After + public void tearDown() { + if (mMockingSession != null) { + mMockingSession.finishMocking(); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java index 71661452d800..698e491a8926 100644 --- a/services/tests/servicestests/src/com/android/server/appop/AppOpsServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java @@ -25,14 +25,27 @@ import static android.app.AppOpsManager.OP_READ_SMS; import static android.app.AppOpsManager.OP_WIFI_SCAN; import static android.app.AppOpsManager.OP_WRITE_SMS; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; + import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.AppOpsManager.OpEntry; import android.app.AppOpsManager.PackageOps; import android.content.Context; +import android.content.pm.PackageManagerInternal; import android.os.Handler; import android.os.HandlerThread; import android.os.Process; @@ -43,12 +56,17 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.server.LocalServices; + +import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.quality.Strictness; import java.io.File; import java.util.List; @@ -69,21 +87,46 @@ public class AppOpsServiceTest { // State will be persisted into this XML file. private static final String APP_OPS_FILENAME = "appops-service-test.xml"; + private static final Context sContext = InstrumentationRegistry.getTargetContext(); + private static final String sMyPackageName = sContext.getOpPackageName(); + private File mAppOpsFile; - private Context mContext; private Handler mHandler; - private AppOpsManager mAppOpsManager; private AppOpsService mAppOpsService; - private String mMyPackageName; private int mMyUid; private long mTestStartMillis; + private StaticMockitoSession mMockingSession; + + @Before + public void mockPackageManagerInternalGetApplicationInfo() { + mMockingSession = mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(LocalServices.class) + .startMocking(); + + // Mock LocalServices.getService(PackageManagerInternal.class).getApplicationInfo dependency + // needed by AppOpsService + PackageManagerInternal mockPackageManagerInternal = mock(PackageManagerInternal.class); + when(mockPackageManagerInternal.getApplicationInfo(eq(sMyPackageName), anyInt(), anyInt(), + anyInt())).thenReturn(sContext.getApplicationInfo()); + doReturn(mockPackageManagerInternal).when( + () -> LocalServices.getService(PackageManagerInternal.class)); + } + + private void setupAppOpsService() { + mAppOpsService = new AppOpsService(mAppOpsFile, mHandler); + mAppOpsService.mContext = spy(sContext); + + // Always approve all permission checks + doNothing().when(mAppOpsService.mContext).enforcePermission(anyString(), anyInt(), + anyInt(), nullable(String.class)); + } private static String sDefaultAppopHistoryParameters; @Before public void setUp() { - mContext = InstrumentationRegistry.getTargetContext(); - mAppOpsFile = new File(mContext.getFilesDir(), APP_OPS_FILENAME); + mAppOpsFile = new File(sContext.getFilesDir(), APP_OPS_FILENAME); if (mAppOpsFile.exists()) { // Start with a clean state (persisted into XML). mAppOpsFile.delete(); @@ -92,13 +135,10 @@ public class AppOpsServiceTest { HandlerThread handlerThread = new HandlerThread(TAG); handlerThread.start(); mHandler = new Handler(handlerThread.getLooper()); - mMyPackageName = mContext.getOpPackageName(); mMyUid = Process.myUid(); - mAppOpsManager = mContext.getSystemService(AppOpsManager.class); - mAppOpsService = new AppOpsService(mAppOpsFile, mHandler); - mAppOpsService.mHistoricalRegistry.systemReady(mContext.getContentResolver()); - mAppOpsService.mContext = mContext; + setupAppOpsService(); + mAppOpsService.mHistoricalRegistry.systemReady(sContext.getContentResolver()); mTestStartMillis = System.currentTimeMillis(); } @@ -118,6 +158,11 @@ public class AppOpsServiceTest { sDefaultAppopHistoryParameters); } + @After + public void resetStaticMocks() { + mMockingSession.finishMocking(); + } + @Test public void testGetOpsForPackage_noOpsLogged() { assertThat(getLoggedOps()).isNull(); @@ -125,16 +170,16 @@ public class AppOpsServiceTest { @Test public void testNoteOperationAndGetOpsForPackage() { - mAppOpsService.setMode(OP_READ_SMS, mMyUid, mMyPackageName, MODE_ALLOWED); - mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, mMyPackageName, MODE_ERRORED); + mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED); + mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED); // Note an op that's allowed. - mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, mMyPackageName); + mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName); List<PackageOps> loggedOps = getLoggedOps(); assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED); // Note another op that's not allowed. - mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, mMyPackageName); + mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName); loggedOps = getLoggedOps(); assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED); assertContainsOp(loggedOps, OP_WRITE_SMS, -1, mTestStartMillis, MODE_ERRORED); @@ -148,17 +193,17 @@ public class AppOpsServiceTest { @Test public void testNoteOperationAndGetOpsForPackage_controlledByDifferentOp() { // This op controls WIFI_SCAN - mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, mMyPackageName, MODE_ALLOWED); + mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ALLOWED); - assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName)) .isEqualTo(MODE_ALLOWED); assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, -1, MODE_ALLOWED /* default for WIFI_SCAN; this is not changed or used in this test */); // Now set COARSE_LOCATION to ERRORED -> this will make WIFI_SCAN disabled as well. - mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, mMyPackageName, MODE_ERRORED); - assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, mMyPackageName)) + mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ERRORED); + assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName)) .isEqualTo(MODE_ERRORED); assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, mTestStartMillis, @@ -168,15 +213,14 @@ public class AppOpsServiceTest { // Tests the dumping and restoring of the in-memory state to/from XML. @Test public void testStatePersistence() { - mAppOpsService.setMode(OP_READ_SMS, mMyUid, mMyPackageName, MODE_ALLOWED); - mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, mMyPackageName, MODE_ERRORED); - mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, mMyPackageName); - mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, mMyPackageName); + mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED); + mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED); + mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName); + mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName); mAppOpsService.writeState(); // Create a new app ops service, and initialize its state from XML. - mAppOpsService = new AppOpsService(mAppOpsFile, mHandler); - mAppOpsService.mContext = mContext; + setupAppOpsService(); mAppOpsService.readState(); // Query the state of the 2nd service. @@ -188,13 +232,12 @@ public class AppOpsServiceTest { // Tests that ops are persisted during shutdown. @Test public void testShutdown() { - mAppOpsService.setMode(OP_READ_SMS, mMyUid, mMyPackageName, MODE_ALLOWED); - mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, mMyPackageName); + mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED); + mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName); mAppOpsService.shutdown(); // Create a new app ops service, and initialize its state from XML. - mAppOpsService = new AppOpsService(mAppOpsFile, mHandler); - mAppOpsService.mContext = mContext; + setupAppOpsService(); mAppOpsService.readState(); // Query the state of the 2nd service. @@ -204,21 +247,21 @@ public class AppOpsServiceTest { @Test public void testGetOpsForPackage() { - mAppOpsService.setMode(OP_READ_SMS, mMyUid, mMyPackageName, MODE_ALLOWED); - mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, mMyPackageName); + mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED); + mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName); // Query all ops List<PackageOps> loggedOps = mAppOpsService.getOpsForPackage( - mMyUid, mMyPackageName, null /* all ops */); + mMyUid, sMyPackageName, null /* all ops */); assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED); // Query specific ops loggedOps = mAppOpsService.getOpsForPackage( - mMyUid, mMyPackageName, new int[]{OP_READ_SMS, OP_WRITE_SMS}); + mMyUid, sMyPackageName, new int[]{OP_READ_SMS, OP_WRITE_SMS}); assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED); // Query unknown UID - loggedOps = mAppOpsService.getOpsForPackage(mMyUid + 1, mMyPackageName, null /* all ops */); + loggedOps = mAppOpsService.getOpsForPackage(mMyUid + 1, sMyPackageName, null /* all ops */); assertThat(loggedOps).isNull(); // Query unknown package name @@ -226,31 +269,31 @@ public class AppOpsServiceTest { assertThat(loggedOps).isNull(); // Query op code that's not been logged - loggedOps = mAppOpsService.getOpsForPackage(mMyUid, mMyPackageName, + loggedOps = mAppOpsService.getOpsForPackage(mMyUid, sMyPackageName, new int[]{OP_WRITE_SMS}); assertThat(loggedOps).isNull(); } @Test public void testPackageRemoved() { - mAppOpsService.setMode(OP_READ_SMS, mMyUid, mMyPackageName, MODE_ALLOWED); - mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, mMyPackageName); + mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED); + mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName); List<PackageOps> loggedOps = getLoggedOps(); assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED); - mAppOpsService.packageRemoved(mMyUid, mMyPackageName); + mAppOpsService.packageRemoved(mMyUid, sMyPackageName); assertThat(getLoggedOps()).isNull(); } @Ignore("Historical appops are disabled in Android Q") @Test public void testPackageRemovedHistoricalOps() throws InterruptedException { - mAppOpsService.setMode(OP_READ_SMS, mMyUid, mMyPackageName, MODE_ALLOWED); - mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, mMyPackageName); + mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED); + mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName); AppOpsManager.HistoricalOps historicalOps = new AppOpsManager.HistoricalOps(0, 15000); - historicalOps.increaseAccessCount(OP_READ_SMS, mMyUid, mMyPackageName, + historicalOps.increaseAccessCount(OP_READ_SMS, mMyUid, sMyPackageName, AppOpsManager.UID_STATE_PERSISTENT, 0, 1); mAppOpsService.addHistoricalOps(historicalOps); @@ -263,7 +306,7 @@ public class AppOpsServiceTest { }); // First, do a fetch to ensure it's written - mAppOpsService.getHistoricalOps(mMyUid, mMyPackageName, null, 0, Long.MAX_VALUE, 0, + mAppOpsService.getHistoricalOps(mMyUid, sMyPackageName, null, 0, Long.MAX_VALUE, 0, callback); latchRef.get().await(5, TimeUnit.SECONDS); @@ -271,11 +314,11 @@ public class AppOpsServiceTest { assertThat(resultOpsRef.get().isEmpty()).isFalse(); // Then, check it's deleted on removal - mAppOpsService.packageRemoved(mMyUid, mMyPackageName); + mAppOpsService.packageRemoved(mMyUid, sMyPackageName); latchRef.set(new CountDownLatch(1)); - mAppOpsService.getHistoricalOps(mMyUid, mMyPackageName, null, 0, Long.MAX_VALUE, 0, + mAppOpsService.getHistoricalOps(mMyUid, sMyPackageName, null, 0, Long.MAX_VALUE, 0, callback); latchRef.get().await(5, TimeUnit.SECONDS); @@ -285,8 +328,8 @@ public class AppOpsServiceTest { @Test public void testUidRemoved() { - mAppOpsService.setMode(OP_READ_SMS, mMyUid, mMyPackageName, MODE_ALLOWED); - mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, mMyPackageName); + mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED); + mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName); List<PackageOps> loggedOps = getLoggedOps(); assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED); @@ -297,7 +340,7 @@ public class AppOpsServiceTest { private void setupProcStateTests() { // For the location proc state tests - mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, mMyPackageName, MODE_FOREGROUND); + mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_FOREGROUND); mAppOpsService.mConstants.FG_SERVICE_STATE_SETTLE_TIME = 0; mAppOpsService.mConstants.TOP_STATE_SETTLE_TIME = 0; mAppOpsService.mConstants.BG_STATE_SETTLE_TIME = 0; @@ -308,18 +351,18 @@ public class AppOpsServiceTest { setupProcStateTests(); mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isNotEqualTo(MODE_ALLOWED); mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isEqualTo(MODE_ALLOWED); mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); // Second time to make sure that settle time is overcome Thread.sleep(50); mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isNotEqualTo(MODE_ALLOWED); } @@ -328,11 +371,11 @@ public class AppOpsServiceTest { setupProcStateTests(); mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isNotEqualTo(MODE_ALLOWED); mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isNotEqualTo(MODE_ALLOWED); } @@ -341,12 +384,12 @@ public class AppOpsServiceTest { setupProcStateTests(); mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isNotEqualTo(MODE_ALLOWED); mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE_LOCATION); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isEqualTo(MODE_ALLOWED); } @@ -355,18 +398,18 @@ public class AppOpsServiceTest { setupProcStateTests(); mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isNotEqualTo(MODE_ALLOWED); mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isEqualTo(MODE_ALLOWED); mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE); // Second time to make sure that settle time is overcome Thread.sleep(50); mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isNotEqualTo(MODE_ALLOWED); } @@ -375,30 +418,30 @@ public class AppOpsServiceTest { setupProcStateTests(); mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isNotEqualTo(MODE_ALLOWED); mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isEqualTo(MODE_ALLOWED); mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE_LOCATION); // Second time to make sure that settle time is overcome Thread.sleep(50); mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE_LOCATION); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isEqualTo(MODE_ALLOWED); mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE); // Second time to make sure that settle time is overcome Thread.sleep(50); mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE); - assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, mMyPackageName)) + assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName)) .isNotEqualTo(MODE_ALLOWED); } private List<PackageOps> getLoggedOps() { - return mAppOpsService.getOpsForPackage(mMyUid, mMyPackageName, null /* all ops */); + return mAppOpsService.getOpsForPackage(mMyUid, sMyPackageName, null /* all ops */); } private void assertContainsOp(List<PackageOps> loggedOps, int opCode, long minMillis, @@ -407,7 +450,7 @@ public class AppOpsServiceTest { boolean opLogged = false; for (PackageOps pkgOps : loggedOps) { assertWithMessage("Unexpected UID").that(mMyUid).isEqualTo(pkgOps.getUid()); - assertWithMessage("Unexpected package name").that(mMyPackageName).isEqualTo( + assertWithMessage("Unexpected package name").that(sMyPackageName).isEqualTo( pkgOps.getPackageName()); for (OpEntry opEntry : pkgOps.getOps()) { diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java index 19369dbe5f44..6c29f6050276 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java @@ -530,6 +530,46 @@ public class TimeControllerTest { } @Test + public void testJobDelayWakeupAlarmToggling() { + final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + + JobStatus job = createJobStatus( + "testMaybeStartTrackingJobLocked_DeadlineReverseOrder", + createJob().setMinimumLatency(HOUR_IN_MILLIS)); + + doReturn(true).when(mTimeController) + .wouldBeReadyWithConstraintLocked(eq(job), anyInt()); + + // Starting off with using a wakeup alarm. + mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false; + InOrder inOrder = inOrder(mAlarmManager); + + mTimeController.maybeStartTrackingJobLocked(job, null); + inOrder.verify(mAlarmManager, times(1)) + .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(), + anyLong(), + eq(TAG_DELAY), any(), any(), any()); + + // Use a non wakeup alarm. + mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = true; + + mTimeController.maybeStartTrackingJobLocked(job, null); + inOrder.verify(mAlarmManager, times(1)) + .set(eq(AlarmManager.ELAPSED_REALTIME), eq(now + HOUR_IN_MILLIS), anyLong(), + anyLong(), eq(TAG_DELAY), + any(), any(), any()); + + // Back off, use a wakeup alarm. + mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false; + + mTimeController.maybeStartTrackingJobLocked(job, null); + inOrder.verify(mAlarmManager, times(1)) + .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(), + anyLong(), + eq(TAG_DELAY), any(), any(), any()); + } + + @Test public void testCheckExpiredDelaysAndResetAlarm_WithSkipping_AllReady() { mConstants.SKIP_NOT_READY_JOBS = true; mTimeController.recheckAlarmsLocked(); diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index d4dd24580aad..5c75a4690a0e 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -22,6 +22,7 @@ android_test { "services.appwidget", "services.autofill", "services.backup", + "services.contentsuggestions", "services.core", "services.devicepolicy", "services.net", diff --git a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java index 50437b4d5f3e..d367f71de921 100644 --- a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java @@ -36,7 +36,7 @@ public class DynamicSystemServiceTest extends AndroidTestCase { public void test1() { assertTrue("dynamic_system service available", mService != null); try { - mService.startInstallation(); + mService.startInstallation("dsu"); fail("DynamicSystemService did not throw SecurityException as expected"); } catch (SecurityException e) { // expected diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java index 07b17ebdeb63..ca5dfb133345 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java @@ -16,7 +16,7 @@ package com.android.server; -import static android.net.NetworkScoreManager.CACHE_FILTER_NONE; +import static android.net.NetworkScoreManager.SCORE_FILTER_NONE; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -304,7 +304,7 @@ public class NetworkScoreServiceTest { bindToScorer(true /*callerIsScorer*/); mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, - mNetworkScoreCache, CACHE_FILTER_NONE); + mNetworkScoreCache, SCORE_FILTER_NONE); mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK}); @@ -319,9 +319,9 @@ public class NetworkScoreServiceTest { bindToScorer(true /*callerIsScorer*/); mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, - mNetworkScoreCache, CACHE_FILTER_NONE); + mNetworkScoreCache, SCORE_FILTER_NONE); mNetworkScoreService.registerNetworkScoreCache( - NetworkKey.TYPE_WIFI, mNetworkScoreCache2, CACHE_FILTER_NONE); + NetworkKey.TYPE_WIFI, mNetworkScoreCache2, SCORE_FILTER_NONE); // updateScores should update both caches mNetworkScoreService.updateScores(new ScoredNetwork[]{SCORED_NETWORK}); @@ -376,7 +376,7 @@ public class NetworkScoreServiceTest { bindToScorer(true /*callerIsScorer*/); mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache, - CACHE_FILTER_NONE); + SCORE_FILTER_NONE); mNetworkScoreService.clearScores(); verify(mNetworkScoreCache).clearScores(); @@ -390,7 +390,7 @@ public class NetworkScoreServiceTest { .thenReturn(PackageManager.PERMISSION_GRANTED); mNetworkScoreService.registerNetworkScoreCache(NetworkKey.TYPE_WIFI, mNetworkScoreCache, - CACHE_FILTER_NONE); + SCORE_FILTER_NONE); mNetworkScoreService.clearScores(); verify(mNetworkScoreCache).clearScores(); @@ -470,7 +470,7 @@ public class NetworkScoreServiceTest { try { mNetworkScoreService.registerNetworkScoreCache( - NetworkKey.TYPE_WIFI, mNetworkScoreCache, CACHE_FILTER_NONE); + NetworkKey.TYPE_WIFI, mNetworkScoreCache, SCORE_FILTER_NONE); fail("SecurityException expected"); } catch (SecurityException e) { // expected @@ -613,7 +613,7 @@ public class NetworkScoreServiceTest { new ArrayList<>(scoredNetworkList), NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter); - consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_NONE); + consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE); verify(mNetworkScoreCache).updateScores(scoredNetworkList); verifyZeroInteractions(mCurrentNetworkFilter, mScanResultsFilter); @@ -654,7 +654,7 @@ public class NetworkScoreServiceTest { Collections.emptyList(), NetworkKey.TYPE_WIFI, mCurrentNetworkFilter, mScanResultsFilter); - consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_NONE); + consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_NONE); verifyZeroInteractions(mNetworkScoreCache, mCurrentNetworkFilter, mScanResultsFilter); } @@ -671,7 +671,7 @@ public class NetworkScoreServiceTest { List<ScoredNetwork> filteredList = new ArrayList<>(scoredNetworkList); filteredList.remove(SCORED_NETWORK); when(mCurrentNetworkFilter.apply(scoredNetworkList)).thenReturn(filteredList); - consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK); + consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_CURRENT_NETWORK); verify(mNetworkScoreCache).updateScores(filteredList); verifyZeroInteractions(mScanResultsFilter); @@ -689,7 +689,7 @@ public class NetworkScoreServiceTest { List<ScoredNetwork> filteredList = new ArrayList<>(scoredNetworkList); filteredList.remove(SCORED_NETWORK); when(mScanResultsFilter.apply(scoredNetworkList)).thenReturn(filteredList); - consumer.accept(mNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_SCAN_RESULTS); + consumer.accept(mNetworkScoreCache, NetworkScoreManager.SCORE_FILTER_SCAN_RESULTS); verify(mNetworkScoreCache).updateScores(filteredList); verifyZeroInteractions(mCurrentNetworkFilter); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java index f3364974231b..ab23b29605ae 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java @@ -39,6 +39,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; +import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; import org.junit.After; @@ -67,6 +68,7 @@ public class AccessibilityServiceConnectionTest { @Mock AccessibilityServiceInfo mMockServiceInfo; @Mock ResolveInfo mMockResolveInfo; @Mock AccessibilityManagerService.SecurityPolicy mMockSecurityPolicy; + @Mock ActivityTaskManagerInternal mMockActivityTaskManagerInternal; @Mock AbstractAccessibilityServiceConnection.SystemSupport mMockSystemSupport; @Mock WindowManagerInternal mMockWindowManagerInternal; @Mock GlobalActionPerformer mMockGlobalActionPerformer; @@ -89,7 +91,8 @@ public class AccessibilityServiceConnectionTest { mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext, COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(), mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal, - mMockGlobalActionPerformer); + mMockGlobalActionPerformer, mMockActivityTaskManagerInternal); + when(mMockSecurityPolicy.canPerformGestures(mConnection)).thenReturn(true); } @After diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 231025c61c8a..a7c943e0a2ac 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -25,8 +25,8 @@ import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG; import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG; import static com.android.server.am.UserController.REPORT_USER_SWITCH_COMPLETE_MSG; import static com.android.server.am.UserController.REPORT_USER_SWITCH_MSG; -import static com.android.server.am.UserController.SYSTEM_USER_CURRENT_MSG; -import static com.android.server.am.UserController.SYSTEM_USER_START_MSG; +import static com.android.server.am.UserController.USER_CURRENT_MSG; +import static com.android.server.am.UserController.USER_START_MSG; import static com.android.server.am.UserController.USER_SWITCH_TIMEOUT_MSG; import static com.google.android.collect.Lists.newArrayList; @@ -53,11 +53,13 @@ import static org.mockito.Mockito.validateMockitoUsage; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.UserIdInt; import android.app.IUserSwitchObserver; import android.content.Context; import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.UserInfo; +import android.content.pm.UserInfo.UserInfoFlag; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -107,6 +109,10 @@ public class UserControllerTest { private static final int TEST_USER_ID1 = 101; private static final int TEST_USER_ID2 = 102; private static final int NONEXIST_USER_ID = 2; + private static final int TEST_PRE_CREATED_USER_ID = 103; + + private static final int NO_USERINFO_FLAGS = 0; + private static final String TAG = UserControllerTest.class.getSimpleName(); private static final long HANDLER_WAIT_TIME_MS = 100; @@ -128,11 +134,11 @@ public class UserControllerTest { private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet( REPORT_USER_SWITCH_MSG, USER_SWITCH_TIMEOUT_MSG, - SYSTEM_USER_START_MSG, - SYSTEM_USER_CURRENT_MSG); + USER_START_MSG, + USER_CURRENT_MSG); private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet( - SYSTEM_USER_START_MSG, + USER_START_MSG, REPORT_LOCKED_BOOT_COMPLETE_MSG); @Before @@ -149,7 +155,8 @@ public class UserControllerTest { doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt()); doNothing().when(mInjector).stackSupervisorRemoveUser(anyInt()); mUserController = new UserController(mInjector); - setUpUser(TEST_USER_ID, 0); + setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS); + setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated=*/ true); }); } @@ -190,6 +197,31 @@ public class UserControllerTest { startForegroundUserAssertions(); } + @Test + public void testStartPreCreatedUser_foreground() { + assertFalse(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ true)); + } + + @Test + public void testStartPreCreatedUser_background() throws Exception { + assertTrue(mUserController.startUser(TEST_PRE_CREATED_USER_ID, /* foreground= */ false)); + + verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt()); + verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean()); + verify(mInjector, never()).clearAllLockedTasks(anyString()); + + assertWithMessage("should not have received intents") + .that(getActions(mInjector.mSentIntents)).isEmpty(); + // TODO(b/140868593): should have received a USER_UNLOCK_MSG message as well, but it doesn't + // because StorageManager.isUserKeyUnlocked(TEST_PRE_CREATED_USER_ID) returns false - to + // properly fix it, we'd need to move this class to FrameworksMockingServicesTests so we can + // mock static methods (but moving this class would involve changing the presubmit tests, + // and the cascade effect goes on...). In fact, a better approach would to not assert the + // binder calls, but their side effects (in this case, that the user is stopped right away) + assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes()) + .containsExactly(USER_START_MSG); + } + private void startUserAssertions( List<String> expectedActions, Set<Integer> expectedMessageCodes) { assertEquals(expectedActions, getActions(mInjector.mSentIntents)); @@ -469,9 +501,15 @@ public class UserControllerTest { continueUserSwitchAssertions(newUserId, expectOldUserStopping); } - private void setUpUser(int userId, int flags) { + private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) { + setUpUser(userId, flags, /* preCreated= */ false); + } + + private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags, boolean preCreated) { UserInfo userInfo = new UserInfo(userId, "User" + userId, flags); + userInfo.preCreated = preCreated; when(mInjector.mUserManagerMock.getUserInfo(eq(userId))).thenReturn(userInfo); + when(mInjector.mUserManagerMock.isPreCreated(userId)).thenReturn(preCreated); } private static List<String> getActions(List<Intent> intents) { diff --git a/services/tests/servicestests/src/com/android/server/contentsuggestions/ContentSuggestionsPerUserServiceTest.java b/services/tests/servicestests/src/com/android/server/contentsuggestions/ContentSuggestionsPerUserServiceTest.java new file mode 100644 index 000000000000..80cf6ad6d88e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/contentsuggestions/ContentSuggestionsPerUserServiceTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.contentsuggestions; + +import static androidx.test.InstrumentationRegistry.getContext; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.contentsuggestions.ContentSuggestionsManager; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.os.UserManagerInternal; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.server.LocalServices; +import com.android.server.wm.ActivityTaskManagerInternal; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class ContentSuggestionsPerUserServiceTest { + private int mUserId; + private ContentSuggestionsPerUserService mPerUserService; + private ActivityTaskManagerInternal mActivityTaskManagerInternal; + + @Before + public void setup() { + UserManagerInternal umi = mock(UserManagerInternal.class); + LocalServices.removeServiceForTest(UserManagerInternal.class); + LocalServices.addService(UserManagerInternal.class, umi); + + mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class); + LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); + LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInternal); + + ContentSuggestionsManagerService contentSuggestionsManagerService = + new ContentSuggestionsManagerService(getContext()); + mUserId = 1; + mPerUserService = new ContentSuggestionsPerUserService(contentSuggestionsManagerService, + new Object(), + mUserId); + } + + // Tests TaskSnapshot is taken when the key ContentSuggestionsManager.EXTRA_BITMAP is missing + // from imageContextRequestExtras provided. + @Test + public void testProvideContextImageLocked_noBitmapInBundle() { + Bundle imageContextRequestExtras = Bundle.EMPTY; + mPerUserService.provideContextImageLocked(mUserId, imageContextRequestExtras); + verify(mActivityTaskManagerInternal, times(1)).getTaskSnapshotNoRestore(anyInt(), + anyBoolean()); + } + + // Tests TaskSnapshot is not taken when the key ContentSuggestionsManager.EXTRA_BITMAP is + // provided in imageContextRequestExtras. + @Test + public void testProvideContextImageLocked_bitmapInBundle() { + Bundle imageContextRequestExtras = new Bundle(); + imageContextRequestExtras.putParcelable(ContentSuggestionsManager.EXTRA_BITMAP, + Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)); + mPerUserService.provideContextImageLocked(mUserId, imageContextRequestExtras); + verify(mActivityTaskManagerInternal, times(0)) + .getTaskSnapshotNoRestore(anyInt(), anyBoolean()); + } +} + + diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java new file mode 100644 index 000000000000..f6c4d3aa5f5f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyFloat; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.Sensor; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; +import android.os.Handler; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AutomaticBrightnessControllerTest { + + private static final int BRIGHTNESS_MIN = 1; + private static final int BRIGHTNESS_MAX = 255; + private static final int LIGHT_SENSOR_RATE = 20; + private static final int INITIAL_LIGHT_SENSOR_RATE = 20; + private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 0; + private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 0; + private static final int SHORT_TERM_MODEL_TIMEOUT = 0; + private static final float DOZE_SCALE_FACTOR = 0.0f; + private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false; + + private Context mContext; + @Mock SensorManager mSensorManager; + @Mock BrightnessMappingStrategy mBrightnessMappingStrategy; + @Mock HysteresisLevels mAmbientBrightnessThresholds; + @Mock HysteresisLevels mScreenBrightnessThresholds; + @Mock PackageManager mPackageManager; + @Mock Handler mNoopHandler; + + private static final int LIGHT_SENSOR_WARMUP_TIME = 0; + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = InstrumentationRegistry.getContext(); + } + + private AutomaticBrightnessController setupController(Sensor lightSensor) { + AutomaticBrightnessController controller = new AutomaticBrightnessController( + new AutomaticBrightnessController.Injector() { + @Override + public Handler getBackgroundThreadHandler() { + return mNoopHandler; + } + }, + () -> { }, mContext.getMainLooper(), mSensorManager, lightSensor, + mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN, + BRIGHTNESS_MAX, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, INITIAL_LIGHT_SENSOR_RATE, + BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, DARKENING_LIGHT_DEBOUNCE_CONFIG, + RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, mAmbientBrightnessThresholds, + mScreenBrightnessThresholds, SHORT_TERM_MODEL_TIMEOUT, mPackageManager); + controller.setLoggingEnabled(true); + + // Configure the brightness controller and grab an instance of the sensor listener, + // through which we can deliver fake (for test) sensor values. + controller.configure(true /* enable */, null /* configuration */, + 0 /* brightness */, false /* userChangedBrightness */, 0 /* adjustment */, + false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT); + + return controller; + } + + @Test + public void testNoHysteresisAtMinBrightness() throws Exception { + Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); + AutomaticBrightnessController controller = setupController(lightSensor); + + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor), + eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + // Set up system to return 5 as a brightness value + float lux1 = 100.0f; + float normalizedBrightness1 = 0.02f; + when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1)) + .thenReturn(lux1); + when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1)) + .thenReturn(lux1); + when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt())) + .thenReturn(normalizedBrightness1); + + // This is the important bit: When the new brightness is set, make sure the new + // brightening threshold is beyond the maximum brightness value...so that we can test that + // our threshold clamping works. + when(mScreenBrightnessThresholds.getBrighteningThreshold(5)).thenReturn(1.0f); + + // Send new sensor value and verify + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1)); + assertEquals(5, controller.getAutomaticScreenBrightness()); + + + // Set up system to return 255 as a brightness value + float lux2 = 10.0f; + float normalizedBrightness2 = 0.0f; + when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2)) + .thenReturn(lux2); + when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2)) + .thenReturn(lux2); + when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt())) + .thenReturn(normalizedBrightness2); + + // Send new sensor value and verify + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2)); + assertEquals(1, controller.getAutomaticScreenBrightness()); + } + + @Test + public void testNoHysteresisAtMaxBrightness() throws Exception { + Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); + AutomaticBrightnessController controller = setupController(lightSensor); + + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor), + eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + // Set up system to return 250 as a brightness value + float lux1 = 100.0f; + float normalizedBrightness1 = 0.98f; + when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1)) + .thenReturn(lux1); + when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1)) + .thenReturn(lux1); + when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt())) + .thenReturn(normalizedBrightness1); + + // This is the important bit: When the new brightness is set, make sure the new + // brightening threshold is beyond the maximum brightness value...so that we can test that + // our threshold clamping works. + when(mScreenBrightnessThresholds.getBrighteningThreshold(250)).thenReturn(260.0f); + + // Send new sensor value and verify + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1)); + assertEquals(250, controller.getAutomaticScreenBrightness()); + + + // Set up system to return 255 as a brightness value + float lux2 = 110.0f; + float normalizedBrightness2 = 1.0f; + when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2)) + .thenReturn(lux2); + when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2)) + .thenReturn(lux2); + when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt())) + .thenReturn(normalizedBrightness2); + + // Send new sensor value and verify + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2)); + assertEquals(255, controller.getAutomaticScreenBrightness()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java index 7081d2e3b370..75fac7c62213 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java @@ -120,7 +120,7 @@ public class BrightnessTrackerTest { assertTrue(mInjector.mIdleScheduled); mInjector.sendScreenChange(/*screen on */ true); assertNotNull(mInjector.mSensorListener); - assertTrue(mInjector.mColorSamplingEnabled); + assertEquals(BrightnessTracker.ENABLE_COLOR_SAMPLING, mInjector.mColorSamplingEnabled); mInjector.sendScreenChange(/*screen on */ false); assertNull(mInjector.mSensorListener); @@ -141,7 +141,7 @@ public class BrightnessTrackerTest { // Turn on screen while brightness mode is automatic. mInjector.sendScreenChange(/*screen on */ true); assertNotNull(mInjector.mSensorListener); - assertTrue(mInjector.mColorSamplingEnabled); + assertEquals(BrightnessTracker.ENABLE_COLOR_SAMPLING, mInjector.mColorSamplingEnabled); mTracker.stop(); assertNull(mInjector.mSensorListener); @@ -184,6 +184,9 @@ public class BrightnessTrackerTest { @Test public void testColorSampling_FrameRateChange() { + if (!BrightnessTracker.ENABLE_COLOR_SAMPLING) { + return; + } startTracker(mTracker); assertTrue(mInjector.mColorSamplingEnabled); assertNotNull(mInjector.mDisplayListener); @@ -211,8 +214,10 @@ public class BrightnessTrackerTest { mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true); assertNotNull(mInjector.mSensorListener); - assertTrue(mInjector.mColorSamplingEnabled); - assertNotNull(mInjector.mDisplayListener); + assertEquals(BrightnessTracker.ENABLE_COLOR_SAMPLING, mInjector.mColorSamplingEnabled); + if (BrightnessTracker.ENABLE_COLOR_SAMPLING) { + assertNotNull(mInjector.mDisplayListener); + } SensorEventListener listener = mInjector.mSensorListener; DisplayManager.DisplayListener displayListener = mInjector.mDisplayListener; @@ -226,8 +231,10 @@ public class BrightnessTrackerTest { assertFalse(mInjector.mColorSamplingEnabled); assertNull(mInjector.mDisplayListener); mInjector.mSensorListener = listener; - mInjector.mDisplayListener = displayListener; - mInjector.mColorSamplingEnabled = true; + if (BrightnessTracker.ENABLE_COLOR_SAMPLING) { + mInjector.mDisplayListener = displayListener; + mInjector.mColorSamplingEnabled = true; + } mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ false); assertNull(mInjector.mSensorListener); @@ -301,8 +308,11 @@ public class BrightnessTrackerTest { assertEquals(3333, event.colorTemperature); assertEquals("a.package", event.packageName); assertEquals(0, event.userId); - assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets); - assertEquals(10000, event.colorSampleDuration); + if (BrightnessTracker.ENABLE_COLOR_SAMPLING) { + assertArrayEquals(new long[]{1, 10, 100, 1000, 300, 30, 10, 1}, + event.colorValueBuckets); + assertEquals(10000, event.colorSampleDuration); + } assertEquals(1, eventsNoPackage.size()); assertNull(eventsNoPackage.get(0).packageName); @@ -559,8 +569,11 @@ public class BrightnessTrackerTest { assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA); assertTrue(event.isUserSetBrightness); assertFalse(event.isDefaultBrightnessConfig); - assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets); - assertEquals(10000, event.colorSampleDuration); + if (BrightnessTracker.ENABLE_COLOR_SAMPLING) { + assertArrayEquals(new long[]{1, 10, 100, 1000, 300, 30, 10, 1}, + event.colorValueBuckets); + assertEquals(10000, event.colorSampleDuration); + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 4742a73b17a8..8d5939ad6ef6 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -11,7 +11,7 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.server.display; diff --git a/services/tests/servicestests/src/com/android/server/display/TestUtils.java b/services/tests/servicestests/src/com/android/server/display/TestUtils.java new file mode 100644 index 000000000000..859dfe3c3fa4 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/TestUtils.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.os.SystemClock; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public final class TestUtils { + + public static SensorEvent createSensorEvent(Sensor sensor, int lux) throws Exception { + final Constructor<SensorEvent> constructor = + SensorEvent.class.getDeclaredConstructor(int.class); + constructor.setAccessible(true); + final SensorEvent event = constructor.newInstance(1); + event.sensor = sensor; + event.values[0] = lux; + event.timestamp = SystemClock.elapsedRealtimeNanos(); + return event; + } + + + public static void setSensorType(Sensor sensor, int type, String strType) throws Exception { + Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE); + setter.setAccessible(true); + setter.invoke(sensor, type); + if (strType != null) { + Field f = sensor.getClass().getDeclaredField("mStringType"); + f.setAccessible(true); + f.set(sensor, strType); + } + } + + public static Sensor createSensor(int type, String strType) throws Exception { + Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); + constr.setAccessible(true); + Sensor sensor = constr.newInstance(); + setSensorType(sensor, type, strType); + return sensor; + } + +} diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java index 6b0798bdce22..e25c1c6c7291 100644 --- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java +++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java @@ -16,29 +16,19 @@ package com.android.server.display.whitebalance; -import com.android.internal.R; -import com.google.common.collect.ImmutableList; - import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.eq; -import org.mockito.stubbing.Answer; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.Spy; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import android.content.ContextWrapper; import android.content.res.Resources; import android.content.res.TypedArray; import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Handler; import android.os.Looper; @@ -46,15 +36,21 @@ import android.util.TypedValue; import androidx.test.InstrumentationRegistry; +import com.android.internal.R; +import com.android.server.display.TestUtils; +import com.android.server.display.whitebalance.AmbientFilter; + +import com.google.common.collect.ImmutableList; + import org.junit.Before; -import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.List; @RunWith(JUnit4.class) @@ -80,8 +76,8 @@ public final class AmbientLuxTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mLightSensor = createSensor(Sensor.TYPE_LIGHT, null); - mAmbientColorSensor = createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR); + mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, null); + mAmbientColorSensor = TestUtils.createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); mResourcesSpy = spy(mContextSpy.getResources()); when(mContextSpy.getResources()).thenReturn(mResourcesSpy); @@ -460,25 +456,6 @@ public final class AmbientLuxTest { } } - private void setSensorType(Sensor sensor, int type, String strType) throws Exception { - Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE); - setter.setAccessible(true); - setter.invoke(sensor, type); - if (strType != null) { - Field f = sensor.getClass().getDeclaredField("mStringType"); - f.setAccessible(true); - f.set(sensor, strType); - } - } - - private Sensor createSensor(int type, String strType) throws Exception { - Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); - constr.setAccessible(true); - Sensor sensor = constr.newInstance(); - setSensorType(sensor, type, strType); - return sensor; - } - private TypedArray createTypedArray() throws Exception { TypedArray mockArray = mock(TypedArray.class); return mockArray; diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java index 6ff4f3b22b9c..3e3e535df986 100644 --- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java @@ -28,15 +28,14 @@ import static org.mockito.Mockito.when; import android.content.ContextWrapper; import android.content.res.Resources; import android.hardware.Sensor; -import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Handler; import android.os.Looper; -import android.os.SystemClock; import androidx.test.InstrumentationRegistry; +import com.android.server.display.TestUtils; import com.android.server.display.whitebalance.AmbientSensor.AmbientBrightnessSensor; import com.android.server.display.whitebalance.AmbientSensor.AmbientColorTemperatureSensor; @@ -50,9 +49,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -73,8 +69,8 @@ public final class AmbientSensorTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mLightSensor = createSensor(Sensor.TYPE_LIGHT, null); - mAmbientColorSensor = createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR); + mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, null); + mAmbientColorSensor = TestUtils.createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); mResourcesSpy = spy(mContextSpy.getResources()); when(mContextSpy.getResources()).thenReturn(mResourcesSpy); @@ -96,7 +92,7 @@ public final class AmbientSensorTest { // There should be no issues when we callback the listener, even if there is no callback // set. SensorEventListener listener = captor.getValue(); - listener.onSensorChanged(createSensorEvent(mLightSensor, 100)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 100)); } @Test @@ -122,7 +118,7 @@ public final class AmbientSensorTest { verify(mSensorManagerMock).registerListener(captor.capture(), eq(mLightSensor), anyInt(), eq(mHandler)); SensorEventListener listener = captor.getValue(); - listener.onSensorChanged(createSensorEvent(mLightSensor, luxValue)); + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, luxValue)); assertTrue(changeSignal.await(5, TimeUnit.SECONDS)); assertEquals(luxValue, luxReturned[0]); } @@ -155,39 +151,8 @@ public final class AmbientSensorTest { verify(mSensorManagerMock).registerListener(captor.capture(), eq(mAmbientColorSensor), anyInt(), eq(mHandler)); SensorEventListener listener = captor.getValue(); - listener.onSensorChanged(createSensorEvent(mAmbientColorSensor, colorTempValue)); + listener.onSensorChanged(TestUtils.createSensorEvent(mAmbientColorSensor, colorTempValue)); assertTrue(changeSignal.await(5, TimeUnit.SECONDS)); assertEquals(colorTempValue, colorTempReturned[0]); } - - private SensorEvent createSensorEvent(Sensor sensor, int lux) throws Exception { - final Constructor<SensorEvent> constructor = - SensorEvent.class.getDeclaredConstructor(int.class); - constructor.setAccessible(true); - final SensorEvent event = constructor.newInstance(1); - event.sensor = sensor; - event.values[0] = lux; - event.timestamp = SystemClock.elapsedRealtimeNanos(); - return event; - } - - - private void setSensorType(Sensor sensor, int type, String strType) throws Exception { - Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE); - setter.setAccessible(true); - setter.invoke(sensor, type); - if (strType != null) { - Field f = sensor.getClass().getDeclaredField("mStringType"); - f.setAccessible(true); - f.set(sensor, strType); - } - } - - private Sensor createSensor(int type, String strType) throws Exception { - Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); - constr.setAccessible(true); - Sensor sensor = constr.newInstance(); - setSensorType(sensor, type, strType); - return sensor; - } } diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 2c941c604f3f..2e58ad68fc7c 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -112,7 +112,7 @@ import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; -import android.net.StringNetworkSpecifier; +import android.net.TelephonyNetworkSpecifier; import android.os.Binder; import android.os.Handler; import android.os.INetworkManagementService; @@ -1729,9 +1729,11 @@ public class NetworkPolicyManagerServiceTest { .getService(NetworkPolicyManagerInternal.class); npmi.onStatsProviderLimitReached("TEST"); - // Verifies that the limit reached leads to a force update. + // Verifies that the limit reached leads to a force update and new limit should be set. postMsgAndWaitForCompletion(); verify(mStatsService).forceUpdate(); + postMsgAndWaitForCompletion(); + verify(mStatsService).setStatsProviderLimit(TEST_IFACE, 10000L - 4999L - 1999L); } /** @@ -1836,7 +1838,8 @@ public class NetworkPolicyManagerServiceTest { if (!roaming) { nc.addCapability(NET_CAPABILITY_NOT_ROAMING); } - nc.setNetworkSpecifier(new StringNetworkSpecifier(String.valueOf(subId))); + nc.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(subId).build()); return nc; } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 806c71a7a9b8..6d5b994a63bb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -132,6 +132,7 @@ public class UserManagerServiceUserInfoTest { user.profileBadge = 2; user.partial = true; user.guestToRemove = true; + user.preCreated = true; return user; } @@ -147,5 +148,6 @@ public class UserManagerServiceUserInfoTest { assertEquals("profile badge not preseved", one.profileBadge, two.profileBadge); assertEquals("partial not preseved", one.partial, two.partial); assertEquals("guestToRemove not preseved", one.guestToRemove, two.guestToRemove); + assertEquals("preCreated not preseved", one.preCreated, two.preCreated); } } diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index 0196279cbf56..a4ba056b96a8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -160,6 +160,31 @@ public class DexManagerTests { } @Test + public void testNotifyPrimaryAndSecondary() { + List<String> dexFiles = mFooUser0.getBaseAndSplitDexPaths(); + List<String> secondaries = mFooUser0.getSecondaryDexPaths(); + int baseAndSplitCount = dexFiles.size(); + dexFiles.addAll(secondaries); + + notifyDexLoad(mFooUser0, dexFiles, mUser0); + + PackageUseInfo pui = getPackageUseInfo(mFooUser0); + assertIsUsedByOtherApps(mFooUser0, pui, false); + assertEquals(secondaries.size(), pui.getDexUseInfoMap().size()); + + String[] allExpectedContexts = DexoptUtils.processContextForDexLoad( + Arrays.asList(mFooUser0.mClassLoader), + Arrays.asList(String.join(File.pathSeparator, dexFiles))); + String[] secondaryExpectedContexts = Arrays.copyOfRange(allExpectedContexts, + baseAndSplitCount, dexFiles.size()); + + assertSecondaryUse(mFooUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0, + secondaryExpectedContexts); + + assertHasDclInfo(mFooUser0, mFooUser0, secondaries); + } + + @Test public void testNotifySecondaryForeign() { // Foo loads bar secondary files. List<String> barSecondaries = mBarUser0.getSecondaryDexPaths(); diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index 8a3183f7abbd..d940a6a320f2 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -651,7 +651,6 @@ public class TimeDetectorStrategyImplTest { @Override public long systemClockMillis() { - assertWakeLockAcquired(); return mSystemClockMillis; } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java index 36504ac7ec65..4a13dce5642b 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java @@ -28,7 +28,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.app.usage.UsageStatsManager; import android.os.FileUtils; import android.test.AndroidTestCase; @@ -150,4 +149,21 @@ public class AppIdleHistoryTests extends AndroidTestCase { aih = new AppIdleHistory(mStorageDir, 5000); assertEquals(REASON_MAIN_TIMEOUT, aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000)); } + + public void testNullPackage() throws Exception { + AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000); + // Report usage of a package + aih.reportUsage(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_SUB_USAGE_MOVE_TO_FOREGROUND, 2000, 0); + // "Accidentally" report usage against a null named package + aih.reportUsage(null, USER_ID, STANDBY_BUCKET_ACTIVE, + REASON_SUB_USAGE_MOVE_TO_FOREGROUND, 2000, 0); + // Persist data + aih.writeAppIdleTimes(USER_ID); + // Recover data from disk + aih = new AppIdleHistory(mStorageDir, 5000); + // Verify data is intact + assertEquals(REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND, + aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000)); + } }
\ No newline at end of file diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp index 92198fa8cb0c..f608babd062c 100644 --- a/services/tests/uiservicestests/Android.bp +++ b/services/tests/uiservicestests/Android.bp @@ -20,6 +20,7 @@ android_test { "androidx.test.rules", "hamcrest-library", "mockito-target-inline-minus-junit4", "platform-test-annotations", + "platformprotosnano", "hamcrest-library", "testables", "truth-prebuilt", diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java new file mode 100644 index 000000000000..338f837b9b44 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 20019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.IUiModeManager; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.PowerManager; +import android.os.RemoteException; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import com.android.server.twilight.TwilightManager; +import com.android.server.wm.WindowManagerInternal; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.util.HashSet; +import java.util.Set; + +import static android.app.UiModeManager.MODE_NIGHT_AUTO; +import static android.app.UiModeManager.MODE_NIGHT_NO; +import static android.app.UiModeManager.MODE_NIGHT_YES; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class UiModeManagerServiceTest extends UiServiceTestCase { + private UiModeManagerService mUiManagerService; + private IUiModeManager mService; + @Mock + private ContentResolver mContentResolver; + @Mock + private WindowManagerInternal mWindowManager; + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + TwilightManager mTwilightManager; + @Mock + PowerManager.WakeLock mWakeLock; + private Set<BroadcastReceiver> mScreenOffRecievers; + + @Before + public void setUp() { + mUiManagerService = new UiModeManagerService(mContext, mWindowManager, mWakeLock, + mTwilightManager, true); + mScreenOffRecievers = new HashSet<>(); + mService = mUiManagerService.getService(); + when(mContext.checkCallingOrSelfPermission(anyString())) + .thenReturn(PackageManager.PERMISSION_GRANTED); + when(mContext.getResources()).thenReturn(mResources); + when(mContext.getContentResolver()).thenReturn(mContentResolver); + when(mContext.registerReceiver(any(), any())).then(inv -> { + mScreenOffRecievers.add(inv.getArgument(0)); + return null; + }); + } + + @Test + public void setAutoMode_screenOffRegistered() throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_NO); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + mService.setNightMode(MODE_NIGHT_AUTO); + verify(mContext).registerReceiver(any(BroadcastReceiver.class), any()); + } + + @Test + public void setAutoMode_screenOffUnRegistered() throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_AUTO); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + try { + mService.setNightMode(MODE_NIGHT_NO); + } catch (SecurityException e) { /*we should ignore this update config exception*/ } + given(mContext.registerReceiver(any(), any())).willThrow(SecurityException.class); + verify(mContext).unregisterReceiver(any(BroadcastReceiver.class)); + } + + @Test + public void setNightModeActive_fromNightModeYesToNoWhenFalse() throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_YES); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + try { + mService.setNightModeActivated(false); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + assertEquals(MODE_NIGHT_NO, mService.getNightMode()); + } + + @Test + public void setNightModeActive_fromNightModeNoToYesWhenTrue() throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_NO); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + try { + mService.setNightModeActivated(true); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + assertEquals(MODE_NIGHT_YES, mService.getNightMode()); + } + + @Test + public void setNightModeActive_autoNightModeNoChanges() throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_AUTO); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + try { + mService.setNightModeActivated(true); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + assertEquals(MODE_NIGHT_AUTO, mService.getNightMode()); + } + + @Test + public void isNightModeActive_nightModeYes() throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_YES); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + assertTrue(isNightModeActivated()); + } + + @Test + public void isNightModeActive_nightModeNo() throws RemoteException { + try { + mService.setNightMode(MODE_NIGHT_NO); + } catch (SecurityException e) { /* we should ignore this update config exception*/ } + assertFalse(isNightModeActivated()); + } + + private boolean isNightModeActivated() { + return (mUiManagerService.getConfiguration().uiMode + & Configuration.UI_MODE_NIGHT_YES) != 0; + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java index e15af3dbecc4..0b4760d89686 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java @@ -68,6 +68,7 @@ public class NotificationComparatorTest extends UiServiceTestCase { private final int uid2 = 1111111; private static final String TEST_CHANNEL_ID = "test_channel_id"; + private NotificationRecord mRecordMinCallNonInterruptive; private NotificationRecord mRecordMinCall; private NotificationRecord mRecordHighCall; private NotificationRecord mRecordDefaultMedia; @@ -105,6 +106,18 @@ public class NotificationComparatorTest extends UiServiceTestCase { smsPkg = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.SMS_DEFAULT_APPLICATION); + Notification nonInterruptiveNotif = new Notification.Builder(mContext, TEST_CHANNEL_ID) + .setCategory(Notification.CATEGORY_CALL) + .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) + .build(); + mRecordMinCallNonInterruptive = new NotificationRecord(mContext, + new StatusBarNotification(callPkg, + callPkg, 1, "mRecordMinCallNonInterruptive", callUid, callUid, + nonInterruptiveNotif, + new UserHandle(userId), "", 2000), getDefaultChannel()); + mRecordMinCallNonInterruptive.setSystemImportance(NotificationManager.IMPORTANCE_MIN); + mRecordMinCallNonInterruptive.setInterruptive(false); + Notification n1 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setCategory(Notification.CATEGORY_CALL) .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true) @@ -113,6 +126,7 @@ public class NotificationComparatorTest extends UiServiceTestCase { callPkg, 1, "minCall", callUid, callUid, n1, new UserHandle(userId), "", 2000), getDefaultChannel()); mRecordMinCall.setSystemImportance(NotificationManager.IMPORTANCE_MIN); + mRecordMinCall.setInterruptive(true); Notification n2 = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setCategory(Notification.CATEGORY_CALL) @@ -245,6 +259,7 @@ public class NotificationComparatorTest extends UiServiceTestCase { expected.add(mRecordCheater); expected.add(mRecordCheaterColorized); expected.add(mRecordMinCall); + expected.add(mRecordMinCallNonInterruptive); List<NotificationRecord> actual = new ArrayList<>(); actual.addAll(expected); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java index 397d2155beeb..a9fe1a62b558 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -51,6 +51,8 @@ import android.service.notification.NotificationRankingUpdate; import android.service.notification.SnoozeCriterion; import android.test.suitebuilder.annotation.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.server.UiServiceTestCase; import org.junit.After; @@ -61,8 +63,6 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; -import androidx.test.runner.AndroidJUnit4; - @SmallTest @RunWith(AndroidJUnit4.class) public class NotificationListenerServiceTest extends UiServiceTestCase { @@ -116,6 +116,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertActionsEqual(getSmartActions(key, i), ranking.getSmartActions()); assertEquals(getSmartReplies(key, i), ranking.getSmartReplies()); assertEquals(canBubble(i), ranking.canBubble()); + assertEquals(visuallyInterruptive(i), ranking.visuallyInterruptive()); } } @@ -182,7 +183,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { tweak.isNoisy(), (ArrayList) tweak.getSmartActions(), (ArrayList) tweak.getSmartReplies(), - tweak.canBubble() + tweak.canBubble(), + tweak.visuallyInterruptive() ); assertNotEquals(nru, nru2); } @@ -258,7 +260,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { getNoisy(i), getSmartActions(key, i), getSmartReplies(key, i), - canBubble(i) + canBubble(i), + visuallyInterruptive(i) ); rankings[i] = ranking; } @@ -363,6 +366,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { return index % 4 == 0; } + private boolean visuallyInterruptive(int index) { + return index % 4 == 0; + } + private void assertActionsEqual( List<Notification.Action> expecteds, List<Notification.Action> actuals) { assertEquals(expecteds.size(), actuals.size()); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index 7c2235050caf..fab6b7fd0d77 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -52,11 +52,13 @@ import android.graphics.drawable.Icon; import android.media.AudioAttributes; import android.metrics.LogMaker; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; import android.service.notification.Adjustment; import android.service.notification.StatusBarNotification; +import android.widget.RemoteViews; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -114,7 +116,9 @@ public class NotificationRecordTest extends UiServiceTestCase { when(mMockContext.getResources()).thenReturn(getContext().getResources()); when(mMockContext.getPackageManager()).thenReturn(mPm); - when(mMockContext.getApplicationInfo()).thenReturn(new ApplicationInfo()); + ApplicationInfo appInfo = new ApplicationInfo(); + appInfo.targetSdkVersion = Build.VERSION_CODES.O; + when(mMockContext.getApplicationInfo()).thenReturn(appInfo); } private StatusBarNotification getNotification(String pkg, boolean noisy, boolean defaultSound, @@ -168,6 +172,28 @@ public class NotificationRecordTest extends UiServiceTestCase { return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid); } + private StatusBarNotification getStyledNotification(boolean customContent, boolean customBig, + boolean customHeadsUp, Notification.Style style) { + final Builder builder = new Builder(mMockContext) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon); + if (style != null) { + builder.setStyle(style); + } + if (customContent) { + builder.setCustomContentView(mock(RemoteViews.class)); + } + if (customBig) { + builder.setCustomBigContentView(mock(RemoteViews.class)); + } + if (customHeadsUp) { + builder.setCustomHeadsUpContentView(mock(RemoteViews.class)); + } + + Notification n = builder.build(); + return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid); + } + // // Tests // @@ -999,4 +1025,74 @@ public class NotificationRecordTest extends UiServiceTestCase { assertEquals(IMPORTANCE_LOW, record.getImportance()); } + + @Test + public void testHasUndecoratedRemoteViews_NoRemoteViews() { + StatusBarNotification sbn = getStyledNotification(false, false, false, null); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertFalse("false positive detection", record.hasUndecoratedRemoteView()); + } + + @Test + public void testHasUndecoratedRemoteViews_NoRemoteViewsWithStyle() { + StatusBarNotification sbn = getStyledNotification(false, false, false, + new Notification.BigPictureStyle()); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertFalse("false positive detection", record.hasUndecoratedRemoteView()); + } + + @Test + public void testHasUndecoratedRemoteViews_UndecoratedContent() { + StatusBarNotification sbn = getStyledNotification(true, false, false, null); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertTrue("false negative detection", record.hasUndecoratedRemoteView()); + } + + + @Test + public void testHasUndecoratedRemoteViews_UndecoratedBig() { + StatusBarNotification sbn = getStyledNotification(false, true, false, null); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertTrue("false negative detection", record.hasUndecoratedRemoteView()); + } + + + @Test + public void testHasUndecoratedRemoteViews_UndecoratedHeadsup() { + StatusBarNotification sbn = getStyledNotification(false, false, true, null); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertTrue("false negative detection", record.hasUndecoratedRemoteView()); + } + + @Test + public void testHasUndecoratedRemoteViews_DecoratedRemoteViews() { + StatusBarNotification sbn = getStyledNotification(true, true, true, + new Notification.DecoratedCustomViewStyle()); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertFalse("false positive detection", record.hasUndecoratedRemoteView()); + } + + @Test + public void testHasUndecoratedRemoteViews_DecoratedMediaRemoteViews() { + StatusBarNotification sbn = getStyledNotification(true, true, true, + new Notification.DecoratedMediaCustomViewStyle()); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertFalse("false positive detection", record.hasUndecoratedRemoteView()); + } + + @Test + public void testHasUndecoratedRemoteViews_UndecoratedWrongStyle() { + StatusBarNotification sbn = getStyledNotification(true, true, true, + new Notification.BigPictureStyle()); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertTrue("false negative detection", record.hasUndecoratedRemoteView()); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java index fa90b291eeee..0d44318e4aaa 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java @@ -116,8 +116,8 @@ public class NotificationShellCmdTest extends UiServiceTestCase { ArgumentCaptor<Notification> notificationCaptor = ArgumentCaptor.forClass(Notification.class); verify(mMockBinderService).enqueueNotificationWithTag( - eq(NotificationShellCmd.NOTIFICATION_PACKAGE), - eq("android"), + eq(getContext().getPackageName()), + eq(getContext().getPackageName()), eq(aTag), eq(NotificationShellCmd.NOTIFICATION_ID), notificationCaptor.capture(), diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PulledStatsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PulledStatsTest.java new file mode 100644 index 000000000000..f685c68f4160 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/PulledStatsTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.server.notification; + +import static com.android.server.notification.NotificationManagerService.REPORT_REMOTE_VIEWS; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotSame; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.service.notification.nano.NotificationRemoteViewsProto; +import android.test.MoreAsserts; +import android.util.proto.ProtoOutputStream; + +import androidx.test.filters.SmallTest; + +import com.android.server.UiServiceTestCase; + +import com.google.protobuf.nano.InvalidProtocolBufferNanoException; + +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.List; + +@SmallTest +public class PulledStatsTest extends UiServiceTestCase { + + @Test + public void testPulledStats_Empty() { + PulledStats stats = new PulledStats(0L); + assertEquals(0L, stats.endTimeMs()); + } + + @Test + public void testPulledStats_UnknownReport() { + PulledStats stats = new PulledStats(0L); + stats.addUndecoratedPackage("foo", 456); + stats.addUndecoratedPackage("bar", 123); + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + final ProtoOutputStream proto = new ProtoOutputStream(bytes); + stats.writeToProto(1023123, proto); // a very large number + proto.flush(); + + // expect empty output in response to an unrecognized request + assertEquals(0L, bytes.size()); + } + + @Test + public void testPulledStats_RemoteViewReportPackages() { + List<String> expectedPkgs = new ArrayList<>(2); + expectedPkgs.add("foo"); + expectedPkgs.add("bar"); + + PulledStats stats = new PulledStats(0L); + for(String pkg: expectedPkgs) { + stats.addUndecoratedPackage(pkg, 111); + } + + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + final ProtoOutputStream protoStream = new ProtoOutputStream(bytes); + stats.writeToProto(REPORT_REMOTE_VIEWS, protoStream); + protoStream.flush(); + + try { + NotificationRemoteViewsProto proto = + NotificationRemoteViewsProto.parseFrom(bytes.toByteArray()); + List<String> actualPkgs = new ArrayList<>(2); + for(int i = 0 ; i < proto.packageRemoteViewInfo.length; i++) { + actualPkgs.add(proto.packageRemoteViewInfo[i].packageName); + } + assertEquals(2, actualPkgs.size()); + assertTrue("missing packages", actualPkgs.containsAll(expectedPkgs)); + assertTrue("unexpected packages", expectedPkgs.containsAll(actualPkgs)); + } catch (InvalidProtocolBufferNanoException e) { + e.printStackTrace(); + fail("writeToProto generated unparsable output"); + } + + } + @Test + public void testPulledStats_RemoteViewReportEndTime() { + List<String> expectedPkgs = new ArrayList<>(2); + expectedPkgs.add("foo"); + expectedPkgs.add("bar"); + + PulledStats stats = new PulledStats(0L); + long t = 111; + for(String pkg: expectedPkgs) { + t += 1000; + stats.addUndecoratedPackage(pkg, t); + } + assertEquals(t, stats.endTimeMs()); + } + +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 0110e94eb1cd..bb80e5e4ddde 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -20,9 +20,11 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -124,6 +126,7 @@ public class DragDropControllerTests extends WindowTestsBase { mDisplayContent = spy(mDisplayContent); mWindow = createDropTargetWindow("Drag test window", 0); doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0); + when(mWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true); synchronized (mWm.mGlobalLock) { mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow); @@ -177,6 +180,7 @@ public class DragDropControllerTests extends WindowTestsBase { .setFormat(PixelFormat.TRANSLUCENT) .build(); + assertTrue(mWm.mInputManager.transferTouchFocus(null, null)); mToken = mTarget.performDrag( new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0, data); diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java index dec88f0aaa1a..af262b71df20 100644 --- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java +++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java @@ -97,11 +97,6 @@ public class StubTransaction extends SurfaceControl.Transaction { } @Override - public SurfaceControl.Transaction transferTouchFocus(IBinder fromToken, IBinder toToken) { - return this; - } - - @Override public SurfaceControl.Transaction setGeometry(SurfaceControl sc, Rect sourceCrop, Rect destFrame, @Surface.Rotation int orientation) { return this; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java index 714d2f2f94a1..eb351b63a469 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; @@ -57,6 +58,10 @@ public class TaskPositioningControllerTests extends WindowTestsBase { assertNotNull(mWm.mTaskPositioningController); mTarget = mWm.mTaskPositioningController; + when(mWm.mInputManager.transferTouchFocus( + any(InputChannel.class), + any(InputChannel.class))).thenReturn(true); + mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window"); mWindow.mInputChannel = new InputChannel(); synchronized (mWm.mGlobalLock) { diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java index 1e4861a89694..82292cfeea09 100644 --- a/services/usage/java/com/android/server/usage/AppIdleHistory.java +++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java @@ -578,7 +578,7 @@ public class AppIdleHistory { } } } catch (IOException | XmlPullParserException e) { - Slog.e(TAG, "Unable to read app idle file for user " + userId); + Slog.e(TAG, "Unable to read app idle file for user " + userId, e); } finally { IoUtils.closeQuietly(fis); } @@ -608,6 +608,11 @@ public class AppIdleHistory { final int N = userHistory.size(); for (int i = 0; i < N; i++) { String packageName = userHistory.keyAt(i); + // Skip any unexpected null package names + if (packageName == null) { + Slog.w(TAG, "Skipping App Idle write for unexpected null package"); + continue; + } AppUsageHistory history = userHistory.valueAt(i); xml.startTag(null, TAG_PACKAGE); xml.attribute(null, ATTR_NAME, packageName); @@ -641,7 +646,7 @@ public class AppIdleHistory { appIdleFile.finishWrite(fos); } catch (Exception e) { appIdleFile.failWrite(fos); - Slog.e(TAG, "Error writing app idle file for user " + userId); + Slog.e(TAG, "Error writing app idle file for user " + userId, e); } } diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 11c0e4abdb35..9fc1949183f8 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -136,17 +136,16 @@ class UserUsageStatsService { } // During system reboot, add a DEVICE_SHUTDOWN event to the end of event list, the timestamp - // is last time UsageStatsDatabase is persisted to disk. + // is last time UsageStatsDatabase is persisted to disk or the last event's time whichever + // is higher (because the file system timestamp is round down to integral seconds). // Also add a DEVICE_STARTUP event with current system timestamp. final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY]; if (currentDailyStats != null) { - // File system timestamp only has precision of 1 second, add 1000ms to make up - // for the loss of round up. - final Event shutdownEvent = - new Event(DEVICE_SHUTDOWN, currentDailyStats.lastTimeSaved + 1000); + final Event shutdownEvent = new Event(DEVICE_SHUTDOWN, + Math.max(currentDailyStats.lastTimeSaved, currentDailyStats.endTime)); shutdownEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME; currentDailyStats.addEvent(shutdownEvent); - final Event startupEvent = new Event(DEVICE_STARTUP, currentTimeMillis); + final Event startupEvent = new Event(DEVICE_STARTUP, System.currentTimeMillis()); startupEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME; currentDailyStats.addEvent(startupEvent); } diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java index 00c75480ba80..812237489063 100644 --- a/services/usb/java/com/android/server/usb/UsbHostManager.java +++ b/services/usb/java/com/android/server/usb/UsbHostManager.java @@ -486,7 +486,7 @@ public class UsbHostManager { /* Opens the specified USB device */ public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings, - String packageName, int uid) { + String packageName, int pid, int uid) { synchronized (mLock) { if (isBlackListed(deviceAddress)) { throw new SecurityException("USB device is on a restricted bus"); @@ -498,7 +498,7 @@ public class UsbHostManager { "device " + deviceAddress + " does not exist or is restricted"); } - settings.checkPermission(device, packageName, uid); + settings.checkPermission(device, packageName, pid, uid); return nativeOpenDevice(deviceAddress); } } diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java index 8ca77f0c63dc..077d6b9bd62d 100644 --- a/services/usb/java/com/android/server/usb/UsbSerialReader.java +++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java @@ -93,7 +93,7 @@ class UsbSerialReader extends IUsbSerialReader.Stub { UserHandle.getUserId(uid)); if (mDevice instanceof UsbDevice) { - settings.checkPermission((UsbDevice) mDevice, packageName, uid); + settings.checkPermission((UsbDevice) mDevice, packageName, pid, uid); } else { settings.checkPermission((UsbAccessory) mDevice, uid); } diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 4be68b83dbcb..13275f34ee1a 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -249,6 +249,7 @@ public class UsbService extends IUsbManager.Stub { if (mHostManager != null) { if (deviceName != null) { int uid = Binder.getCallingUid(); + int pid = Binder.getCallingPid(); int user = UserHandle.getUserId(uid); long ident = clearCallingIdentity(); @@ -256,7 +257,7 @@ public class UsbService extends IUsbManager.Stub { synchronized (mLock) { if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) { fd = mHostManager.openDevice(deviceName, getSettingsForUser(user), - packageName, uid); + packageName, pid, uid); } else { Slog.w(TAG, "Cannot open " + deviceName + " for user " + user + " as user is not active."); @@ -350,11 +351,12 @@ public class UsbService extends IUsbManager.Stub { @Override public boolean hasDevicePermission(UsbDevice device, String packageName) { final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); final int userId = UserHandle.getUserId(uid); final long token = Binder.clearCallingIdentity(); try { - return getSettingsForUser(userId).hasPermission(device, packageName, uid); + return getSettingsForUser(userId).hasPermission(device, packageName, pid, uid); } finally { Binder.restoreCallingIdentity(token); } @@ -376,11 +378,12 @@ public class UsbService extends IUsbManager.Stub { @Override public void requestDevicePermission(UsbDevice device, String packageName, PendingIntent pi) { final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); final int userId = UserHandle.getUserId(uid); final long token = Binder.clearCallingIdentity(); try { - getSettingsForUser(userId).requestPermission(device, packageName, pi, uid); + getSettingsForUser(userId).requestPermission(device, packageName, pi, pid, uid); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java index 84add88cc84c..e1bfb8a7c6d0 100644 --- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java @@ -127,11 +127,12 @@ class UsbUserSettingsManager { * Check for camera permission of the calling process. * * @param packageName Package name of the caller. + * @param pid Linux pid of the calling process. * @param uid Linux uid of the calling process. * * @return True in case camera permission is available, False otherwise. */ - private boolean isCameraPermissionGranted(String packageName, int uid) { + private boolean isCameraPermissionGranted(String packageName, int pid, int uid) { int targetSdkVersion = android.os.Build.VERSION_CODES.P; try { ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0); @@ -147,7 +148,8 @@ class UsbUserSettingsManager { } if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) { - int allowed = mUserContext.checkCallingPermission(android.Manifest.permission.CAMERA); + int allowed = mUserContext.checkPermission(android.Manifest.permission.CAMERA, pid, + uid); if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) { Slog.i(TAG, "Camera permission required for USB video class devices"); return false; @@ -157,9 +159,9 @@ class UsbUserSettingsManager { return true; } - public boolean hasPermission(UsbDevice device, String packageName, int uid) { + public boolean hasPermission(UsbDevice device, String packageName, int pid, int uid) { if (isCameraDevicePresent(device)) { - if (!isCameraPermissionGranted(packageName, uid)) { + if (!isCameraPermissionGranted(packageName, pid, uid)) { return false; } } @@ -171,8 +173,8 @@ class UsbUserSettingsManager { return mUsbPermissionManager.hasPermission(accessory, uid); } - public void checkPermission(UsbDevice device, String packageName, int uid) { - if (!hasPermission(device, packageName, uid)) { + public void checkPermission(UsbDevice device, String packageName, int pid, int uid) { + if (!hasPermission(device, packageName, pid, uid)) { throw new SecurityException("User has not given " + uid + "/" + packageName + " permission to access device " + device.getDeviceName()); } @@ -206,11 +208,12 @@ class UsbUserSettingsManager { accessory, canBeDefault, packageName, uid, mUserContext, pi); } - public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) { + public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid, + int uid) { Intent intent = new Intent(); // respond immediately if permission has already been granted - if (hasPermission(device, packageName, uid)) { + if (hasPermission(device, packageName, pid, uid)) { intent.putExtra(UsbManager.EXTRA_DEVICE, device); intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); try { @@ -221,7 +224,7 @@ class UsbUserSettingsManager { return; } if (isCameraDevicePresent(device)) { - if (!isCameraPermissionGranted(packageName, uid)) { + if (!isCameraPermissionGranted(packageName, pid, uid)) { intent.putExtra(UsbManager.EXTRA_DEVICE, device); intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false); try { diff --git a/telecomm/java/android/telecom/AudioState.java b/telecomm/java/android/telecom/AudioState.java index 8b8c86be7b0a..ea641f866b98 100644 --- a/telecomm/java/android/telecom/AudioState.java +++ b/telecomm/java/android/telecom/AudioState.java @@ -19,7 +19,7 @@ package android.telecom; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 86ad795b9ea2..826a89eb38bb 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -20,12 +20,11 @@ import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; -import android.os.IBinder; import android.os.ParcelFileDescriptor; import com.android.internal.telecom.IVideoProvider; @@ -569,6 +568,7 @@ public final class Call { private final Bundle mExtras; private final Bundle mIntentExtras; private final long mCreationTimeMillis; + private final String mContactDisplayName; private final @CallDirection int mCallDirection; private final @Connection.VerificationStatus int mCallerNumberVerificationStatus; @@ -873,6 +873,17 @@ public final class Call { } /** + * Returns the name of the caller on the remote end, as derived from a + * {@link android.provider.ContactsContract} lookup of the call's handle. + * @return The name of the caller, or {@code null} if the lookup is not yet complete, if + * there's no contacts entry for the caller, or if the {@link InCallService} does + * not hold the {@link android.Manifest.permission#READ_CONTACTS} permission. + */ + public @Nullable String getContactDisplayName() { + return mContactDisplayName; + } + + /** * Indicates whether the call is an incoming or outgoing call. * @return The call's direction. */ @@ -910,6 +921,7 @@ public final class Call { areBundlesEqual(mExtras, d.mExtras) && areBundlesEqual(mIntentExtras, d.mIntentExtras) && Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis) && + Objects.equals(mContactDisplayName, d.mContactDisplayName) && Objects.equals(mCallDirection, d.mCallDirection) && Objects.equals(mCallerNumberVerificationStatus, d.mCallerNumberVerificationStatus); @@ -934,6 +946,7 @@ public final class Call { mExtras, mIntentExtras, mCreationTimeMillis, + mContactDisplayName, mCallDirection, mCallerNumberVerificationStatus); } @@ -956,6 +969,7 @@ public final class Call { Bundle extras, Bundle intentExtras, long creationTimeMillis, + String contactDisplayName, int callDirection, int callerNumberVerificationStatus) { mTelecomCallId = telecomCallId; @@ -974,6 +988,7 @@ public final class Call { mExtras = extras; mIntentExtras = intentExtras; mCreationTimeMillis = creationTimeMillis; + mContactDisplayName = contactDisplayName; mCallDirection = callDirection; mCallerNumberVerificationStatus = callerNumberVerificationStatus; } @@ -997,6 +1012,7 @@ public final class Call { parcelableCall.getExtras(), parcelableCall.getIntentExtras(), parcelableCall.getCreationTimeMillis(), + parcelableCall.getContactDisplayName(), parcelableCall.getCallDirection(), parcelableCall.getCallerNumberVerificationStatus()); } @@ -1446,6 +1462,7 @@ public final class Call { private boolean mChildrenCached; private String mParentId = null; + private String mActiveGenericConferenceChild = null; private int mState; private List<String> mCannedTextResponses = null; private String mCallingPackage; @@ -1944,6 +1961,20 @@ public final class Call { } /** + * Returns the child {@link Call} in a generic conference that is currently active. + * For calls that are not generic conferences, or when the generic conference has more than + * 2 children, returns {@code null}. + * @see Details#PROPERTY_GENERIC_CONFERENCE + * @return The active child call. + */ + public @Nullable Call getGenericConferenceActiveChildCall() { + if (mActiveGenericConferenceChild != null) { + return mPhone.internalGetCallByTelecomId(mActiveGenericConferenceChild); + } + return null; + } + + /** * Obtains a list of canned, pre-configured message responses to present to the user as * ways of rejecting this {@code Call} using via a text message. * @@ -2191,6 +2222,13 @@ public final class Call { mChildrenCached = false; } + String activeChildCallId = parcelableCall.getActiveChildCallId(); + boolean activeChildChanged = !Objects.equals(activeChildCallId, + mActiveGenericConferenceChild); + if (activeChildChanged) { + mActiveGenericConferenceChild = activeChildCallId; + } + List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds(); List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size()); for (String otherId : conferenceableCallIds) { @@ -2250,7 +2288,7 @@ public final class Call { if (parentChanged) { fireParentChanged(getParent()); } - if (childrenChanged) { + if (childrenChanged || activeChildChanged) { fireChildrenChanged(getChildren()); } if (isRttChanged) { diff --git a/telecomm/java/android/telecom/CallerInfo.java b/telecomm/java/android/telecom/CallerInfo.java index a5d25e2ce4bb..fb6f99405759 100644 --- a/telecomm/java/android/telecom/CallerInfo.java +++ b/telecomm/java/android/telecom/CallerInfo.java @@ -17,7 +17,7 @@ package android.telecom; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -41,8 +41,8 @@ import com.android.i18n.phonenumbers.NumberParseException; import com.android.i18n.phonenumbers.PhoneNumberUtil; import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber; import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder; - import com.android.internal.annotations.VisibleForTesting; + import java.util.Locale; diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 8808339b1664..f205ec64f49b 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -21,9 +21,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; import android.app.Notification; import android.bluetooth.BluetoothDevice; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.hardware.camera2.CameraManager; import android.net.Uri; diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java index 7d4ee7686512..4f6a9d6450f8 100644 --- a/telecomm/java/android/telecom/Log.java +++ b/telecomm/java/android/telecom/Log.java @@ -16,7 +16,7 @@ package android.telecom; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.net.Uri; import android.os.Build; diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java index a234bb0af8fa..415a817b58d5 100644 --- a/telecomm/java/android/telecom/ParcelableCall.java +++ b/telecomm/java/android/telecom/ParcelableCall.java @@ -16,27 +16,286 @@ package android.telecom; -import android.annotation.UnsupportedAppUsage; +import android.annotation.Nullable; +import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.telecom.Call.Details.CallDirection; +import com.android.internal.telecom.IVideoProvider; + import java.util.ArrayList; import java.util.Collections; import java.util.List; -import com.android.internal.telecom.IVideoProvider; - /** * Information about a call that is used between InCallService and Telecom. * @hide */ public final class ParcelableCall implements Parcelable { + + public static class ParcelableCallBuilder { + private String mId; + private int mState; + private DisconnectCause mDisconnectCause; + private List<String> mCannedSmsResponses; + private int mCapabilities; + private int mProperties; + private int mSupportedAudioRoutes; + private long mConnectTimeMillis; + private Uri mHandle; + private int mHandlePresentation; + private String mCallerDisplayName; + private int mCallerDisplayNamePresentation; + private GatewayInfo mGatewayInfo; + private PhoneAccountHandle mAccountHandle; + private boolean mIsVideoCallProviderChanged; + private IVideoProvider mVideoCallProvider; + private boolean mIsRttCallChanged; + private ParcelableRttCall mRttCall; + private String mParentCallId; + private List<String> mChildCallIds; + private StatusHints mStatusHints; + private int mVideoState; + private List<String> mConferenceableCallIds; + private Bundle mIntentExtras; + private Bundle mExtras; + private long mCreationTimeMillis; + private int mCallDirection; + private int mCallerNumberVerificationStatus; + private String mContactDisplayName; + private String mActiveChildCallId; + + public ParcelableCallBuilder setId(String id) { + mId = id; + return this; + } + + public ParcelableCallBuilder setState(int state) { + mState = state; + return this; + } + + public ParcelableCallBuilder setDisconnectCause(DisconnectCause disconnectCause) { + mDisconnectCause = disconnectCause; + return this; + } + + public ParcelableCallBuilder setCannedSmsResponses(List<String> cannedSmsResponses) { + mCannedSmsResponses = cannedSmsResponses; + return this; + } + + public ParcelableCallBuilder setCapabilities(int capabilities) { + mCapabilities = capabilities; + return this; + } + + public ParcelableCallBuilder setProperties(int properties) { + mProperties = properties; + return this; + } + + public ParcelableCallBuilder setSupportedAudioRoutes(int supportedAudioRoutes) { + mSupportedAudioRoutes = supportedAudioRoutes; + return this; + } + + public ParcelableCallBuilder setConnectTimeMillis(long connectTimeMillis) { + mConnectTimeMillis = connectTimeMillis; + return this; + } + + public ParcelableCallBuilder setHandle(Uri handle) { + mHandle = handle; + return this; + } + + public ParcelableCallBuilder setHandlePresentation(int handlePresentation) { + mHandlePresentation = handlePresentation; + return this; + } + + public ParcelableCallBuilder setCallerDisplayName(String callerDisplayName) { + mCallerDisplayName = callerDisplayName; + return this; + } + + public ParcelableCallBuilder setCallerDisplayNamePresentation( + int callerDisplayNamePresentation) { + mCallerDisplayNamePresentation = callerDisplayNamePresentation; + return this; + } + + public ParcelableCallBuilder setGatewayInfo(GatewayInfo gatewayInfo) { + mGatewayInfo = gatewayInfo; + return this; + } + + public ParcelableCallBuilder setAccountHandle(PhoneAccountHandle accountHandle) { + mAccountHandle = accountHandle; + return this; + } + + public ParcelableCallBuilder setIsVideoCallProviderChanged( + boolean isVideoCallProviderChanged) { + mIsVideoCallProviderChanged = isVideoCallProviderChanged; + return this; + } + + public ParcelableCallBuilder setVideoCallProvider(IVideoProvider videoCallProvider) { + mVideoCallProvider = videoCallProvider; + return this; + } + + public ParcelableCallBuilder setIsRttCallChanged(boolean isRttCallChanged) { + mIsRttCallChanged = isRttCallChanged; + return this; + } + + public ParcelableCallBuilder setRttCall(ParcelableRttCall rttCall) { + mRttCall = rttCall; + return this; + } + + public ParcelableCallBuilder setParentCallId(String parentCallId) { + mParentCallId = parentCallId; + return this; + } + + public ParcelableCallBuilder setChildCallIds(List<String> childCallIds) { + mChildCallIds = childCallIds; + return this; + } + + public ParcelableCallBuilder setStatusHints(StatusHints statusHints) { + mStatusHints = statusHints; + return this; + } + + public ParcelableCallBuilder setVideoState(int videoState) { + mVideoState = videoState; + return this; + } + + public ParcelableCallBuilder setConferenceableCallIds( + List<String> conferenceableCallIds) { + mConferenceableCallIds = conferenceableCallIds; + return this; + } + + public ParcelableCallBuilder setIntentExtras(Bundle intentExtras) { + mIntentExtras = intentExtras; + return this; + } + + public ParcelableCallBuilder setExtras(Bundle extras) { + mExtras = extras; + return this; + } + + public ParcelableCallBuilder setCreationTimeMillis(long creationTimeMillis) { + mCreationTimeMillis = creationTimeMillis; + return this; + } + + public ParcelableCallBuilder setCallDirection(int callDirection) { + mCallDirection = callDirection; + return this; + } + + public ParcelableCallBuilder setCallerNumberVerificationStatus( + int callerNumberVerificationStatus) { + mCallerNumberVerificationStatus = callerNumberVerificationStatus; + return this; + } + + public ParcelableCallBuilder setContactDisplayName(String contactDisplayName) { + mContactDisplayName = contactDisplayName; + return this; + } + + public ParcelableCallBuilder setActiveChildCallId(String activeChildCallId) { + mActiveChildCallId = activeChildCallId; + return this; + } + + public ParcelableCall createParcelableCall() { + return new ParcelableCall( + mId, + mState, + mDisconnectCause, + mCannedSmsResponses, + mCapabilities, + mProperties, + mSupportedAudioRoutes, + mConnectTimeMillis, + mHandle, + mHandlePresentation, + mCallerDisplayName, + mCallerDisplayNamePresentation, + mGatewayInfo, + mAccountHandle, + mIsVideoCallProviderChanged, + mVideoCallProvider, + mIsRttCallChanged, + mRttCall, + mParentCallId, + mChildCallIds, + mStatusHints, + mVideoState, + mConferenceableCallIds, + mIntentExtras, + mExtras, + mCreationTimeMillis, + mCallDirection, + mCallerNumberVerificationStatus, + mContactDisplayName, + mActiveChildCallId); + } + + public static ParcelableCallBuilder fromParcelableCall(ParcelableCall parcelableCall) { + ParcelableCallBuilder newBuilder = new ParcelableCallBuilder(); + newBuilder.mId = parcelableCall.mId; + newBuilder.mState = parcelableCall.mState; + newBuilder.mDisconnectCause = parcelableCall.mDisconnectCause; + newBuilder.mCannedSmsResponses = parcelableCall.mCannedSmsResponses; + newBuilder.mCapabilities = parcelableCall.mCapabilities; + newBuilder.mProperties = parcelableCall.mProperties; + newBuilder.mSupportedAudioRoutes = parcelableCall.mSupportedAudioRoutes; + newBuilder.mConnectTimeMillis = parcelableCall.mConnectTimeMillis; + newBuilder.mHandle = parcelableCall.mHandle; + newBuilder.mHandlePresentation = parcelableCall.mHandlePresentation; + newBuilder.mCallerDisplayName = parcelableCall.mCallerDisplayName; + newBuilder.mCallerDisplayNamePresentation = + parcelableCall.mCallerDisplayNamePresentation; + newBuilder.mGatewayInfo = parcelableCall.mGatewayInfo; + newBuilder.mAccountHandle = parcelableCall.mAccountHandle; + newBuilder.mIsVideoCallProviderChanged = parcelableCall.mIsVideoCallProviderChanged; + newBuilder.mVideoCallProvider = parcelableCall.mVideoCallProvider; + newBuilder.mIsRttCallChanged = parcelableCall.mIsRttCallChanged; + newBuilder.mRttCall = parcelableCall.mRttCall; + newBuilder.mParentCallId = parcelableCall.mParentCallId; + newBuilder.mChildCallIds = parcelableCall.mChildCallIds; + newBuilder.mStatusHints = parcelableCall.mStatusHints; + newBuilder.mVideoState = parcelableCall.mVideoState; + newBuilder.mConferenceableCallIds = parcelableCall.mConferenceableCallIds; + newBuilder.mIntentExtras = parcelableCall.mIntentExtras; + newBuilder.mExtras = parcelableCall.mExtras; + newBuilder.mCreationTimeMillis = parcelableCall.mCreationTimeMillis; + newBuilder.mCallDirection = parcelableCall.mCallDirection; + newBuilder.mCallerNumberVerificationStatus = + parcelableCall.mCallerNumberVerificationStatus; + newBuilder.mContactDisplayName = parcelableCall.mContactDisplayName; + newBuilder.mActiveChildCallId = parcelableCall.mActiveChildCallId; + return newBuilder; + } + } + private final String mId; private final int mState; private final DisconnectCause mDisconnectCause; @@ -66,6 +325,8 @@ public final class ParcelableCall implements Parcelable { private final long mCreationTimeMillis; private final int mCallDirection; private final int mCallerNumberVerificationStatus; + private final String mContactDisplayName; + private final String mActiveChildCallId; // Only valid for CDMA conferences public ParcelableCall( String id, @@ -95,7 +356,10 @@ public final class ParcelableCall implements Parcelable { Bundle extras, long creationTimeMillis, int callDirection, - int callerNumberVerificationStatus) { + int callerNumberVerificationStatus, + String contactDisplayName, + String activeChildCallId + ) { mId = id; mState = state; mDisconnectCause = disconnectCause; @@ -124,6 +388,8 @@ public final class ParcelableCall implements Parcelable { mCreationTimeMillis = creationTimeMillis; mCallDirection = callDirection; mCallerNumberVerificationStatus = callerNumberVerificationStatus; + mContactDisplayName = contactDisplayName; + mActiveChildCallId = activeChildCallId; } /** The unique ID of the call. */ @@ -333,6 +599,21 @@ public final class ParcelableCall implements Parcelable { return mCallerNumberVerificationStatus; } + /** + * @return the name of the remote party as derived from a contacts DB lookup. + */ + public @Nullable String getContactDisplayName() { + return mContactDisplayName; + } + + /** + * @return On a CDMA conference with two participants, returns the ID of the child call that's + * currently active. + */ + public @Nullable String getActiveChildCallId() { + return mActiveChildCallId; + } + /** Responsible for creating ParcelableCall objects for deserialized Parcels. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public static final @android.annotation.NonNull Parcelable.Creator<ParcelableCall> CREATOR = @@ -372,35 +653,40 @@ public final class ParcelableCall implements Parcelable { long creationTimeMillis = source.readLong(); int callDirection = source.readInt(); int callerNumberVerificationStatus = source.readInt(); - return new ParcelableCall( - id, - state, - disconnectCause, - cannedSmsResponses, - capabilities, - properties, - supportedAudioRoutes, - connectTimeMillis, - handle, - handlePresentation, - callerDisplayName, - callerDisplayNamePresentation, - gatewayInfo, - accountHandle, - isVideoCallProviderChanged, - videoCallProvider, - isRttCallChanged, - rttCall, - parentCallId, - childCallIds, - statusHints, - videoState, - conferenceableCallIds, - intentExtras, - extras, - creationTimeMillis, - callDirection, - callerNumberVerificationStatus); + String contactDisplayName = source.readString(); + String activeChildCallId = source.readString(); + return new ParcelableCallBuilder() + .setId(id) + .setState(state) + .setDisconnectCause(disconnectCause) + .setCannedSmsResponses(cannedSmsResponses) + .setCapabilities(capabilities) + .setProperties(properties) + .setSupportedAudioRoutes(supportedAudioRoutes) + .setConnectTimeMillis(connectTimeMillis) + .setHandle(handle) + .setHandlePresentation(handlePresentation) + .setCallerDisplayName(callerDisplayName) + .setCallerDisplayNamePresentation(callerDisplayNamePresentation) + .setGatewayInfo(gatewayInfo) + .setAccountHandle(accountHandle) + .setIsVideoCallProviderChanged(isVideoCallProviderChanged) + .setVideoCallProvider(videoCallProvider) + .setIsRttCallChanged(isRttCallChanged) + .setRttCall(rttCall) + .setParentCallId(parentCallId) + .setChildCallIds(childCallIds) + .setStatusHints(statusHints) + .setVideoState(videoState) + .setConferenceableCallIds(conferenceableCallIds) + .setIntentExtras(intentExtras) + .setExtras(extras) + .setCreationTimeMillis(creationTimeMillis) + .setCallDirection(callDirection) + .setCallerNumberVerificationStatus(callerNumberVerificationStatus) + .setContactDisplayName(contactDisplayName) + .setActiveChildCallId(activeChildCallId) + .createParcelableCall(); } @Override @@ -447,6 +733,8 @@ public final class ParcelableCall implements Parcelable { destination.writeLong(mCreationTimeMillis); destination.writeInt(mCallDirection); destination.writeInt(mCallerNumberVerificationStatus); + destination.writeString(mContactDisplayName); + destination.writeString(mActiveChildCallId); } @Override diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java index 61a639a1a235..a427ed612b31 100644 --- a/telecomm/java/android/telecom/Phone.java +++ b/telecomm/java/android/telecom/Phone.java @@ -17,8 +17,8 @@ package android.telecom; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; import android.bluetooth.BluetoothDevice; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Bundle; import android.util.ArrayMap; diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java index eb568e04ebf3..e1bcb5fbdf00 100644 --- a/telecomm/java/android/telecom/PhoneAccountHandle.java +++ b/telecomm/java/android/telecom/PhoneAccountHandle.java @@ -18,7 +18,7 @@ package android.telecom; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.os.Build; import android.os.Parcel; diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index af3c55abf00c..9cf4803966c6 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -26,7 +26,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java index 4a1aa0a8ffa4..109e7f829f2e 100644 --- a/telecomm/java/android/telecom/VideoCallImpl.java +++ b/telecomm/java/android/telecom/VideoCallImpl.java @@ -16,7 +16,7 @@ package android.telecom; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Build; import android.os.Handler; diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java index 64e6ca3416e3..4197f3cfc6c3 100644 --- a/telecomm/java/android/telecom/VideoProfile.java +++ b/telecomm/java/android/telecom/VideoProfile.java @@ -19,7 +19,6 @@ package android.telecom; import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.IntRange; -import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; diff --git a/telephony/common/com/android/internal/telephony/GsmAlphabet.java b/telephony/common/com/android/internal/telephony/GsmAlphabet.java index f7383ab1f632..60cd40094950 100644 --- a/telephony/common/com/android/internal/telephony/GsmAlphabet.java +++ b/telephony/common/com/android/internal/telephony/GsmAlphabet.java @@ -16,7 +16,7 @@ package com.android.internal.telephony; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.os.Build; import android.util.Log; diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java index 39ff44772cc9..3a900d9c224f 100644 --- a/telephony/common/com/android/internal/telephony/SmsApplication.java +++ b/telephony/common/com/android/internal/telephony/SmsApplication.java @@ -20,6 +20,7 @@ import android.Manifest.permission; import android.annotation.Nullable; import android.app.AppOpsManager; import android.app.role.RoleManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -48,8 +49,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import dalvik.annotation.compat.UnsupportedAppUsage; - import java.util.Collection; import java.util.HashMap; import java.util.List; diff --git a/telephony/common/com/android/internal/telephony/SmsConstants.java b/telephony/common/com/android/internal/telephony/SmsConstants.java index 19f52b0ef429..3aa8bbf607d1 100644 --- a/telephony/common/com/android/internal/telephony/SmsConstants.java +++ b/telephony/common/com/android/internal/telephony/SmsConstants.java @@ -15,7 +15,7 @@ */ package com.android.internal.telephony; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * SMS Constants and must be the same as the corresponding diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 72ad4cd83d01..c8e2b8962072 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -27,8 +27,6 @@ import android.content.pm.PackageManager; import android.os.Binder; import android.os.Build; import android.os.Process; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -41,7 +39,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; /** Utility class for Telephony permission enforcement. */ public final class TelephonyPermissions { @@ -49,9 +46,6 @@ public final class TelephonyPermissions { private static final boolean DBG = false; - private static final Supplier<ITelephony> TELEPHONY_SUPPLIER = () -> - ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE)); - /** * Whether to disable the new device identifier access restrictions. */ @@ -137,49 +131,6 @@ public final class TelephonyPermissions { public static boolean checkReadPhoneState( Context context, int subId, int pid, int uid, String callingPackage, @Nullable String callingFeatureId, String message) { - return checkReadPhoneState( - context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, callingFeatureId, - message); - } - - /** - * Check whether the calling packages has carrier privileges for the passing subscription. - * @return {@code true} if the caller has carrier privileges, {@false} otherwise. - */ - public static boolean checkCarrierPrivilegeForSubId(int subId) { - if (SubscriptionManager.isValidSubscriptionId(subId) - && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, Binder.getCallingUid()) - == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { - return true; - } - return false; - } - - /** - * Check whether the app with the given pid/uid can read phone state. - * - * <p>This method behaves in one of the following ways: - * <ul> - * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the - * READ_PHONE_STATE runtime permission, or carrier privileges on the given subId. - * <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for - * apps which support runtime permissions, if the caller does not currently have any of - * these permissions. - * <li>return false: if the caller lacks all of these permissions and doesn't support runtime - * permissions. This implies that the user revoked the ability to read phone state - * manually (via AppOps). In this case we can't throw as it would break app compatibility, - * so we return false to indicate that the calling function should return dummy data. - * </ul> - * - * <p>Note: for simplicity, this method always returns false for callers using legacy - * permissions and who have had READ_PHONE_STATE revoked, even if they are carrier-privileged. - * Such apps should migrate to runtime permissions or stop requiring READ_PHONE_STATE on P+ - * devices. - */ - @VisibleForTesting - public static boolean checkReadPhoneState( - Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid, - String callingPackage, @Nullable String callingFeatureId, String message) { try { context.enforcePermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message); @@ -194,7 +145,7 @@ public final class TelephonyPermissions { // If we don't have the runtime permission, but do have carrier privileges, that // suffices for reading phone state. if (SubscriptionManager.isValidSubscriptionId(subId)) { - enforceCarrierPrivilege(telephonySupplier, subId, uid, message); + enforceCarrierPrivilege(context, subId, uid, message); return true; } throw phoneStateException; @@ -209,23 +160,16 @@ public final class TelephonyPermissions { } /** - * Check whether the app with the given pid/uid can read phone state, or has carrier - * privileges on any active subscription. - * - * <p>If the app does not have carrier privilege, this method will return {@code false} instead - * of throwing a SecurityException. Therefore, the callers cannot tell the difference - * between M+ apps which declare the runtime permission but do not have it, and pre-M apps - * which declare the static permission but had access revoked via AppOps. Apps in the former - * category expect SecurityExceptions; apps in the latter don't. So this method is suitable for - * use only if the behavior in both scenarios is meant to be identical. - * - * @return {@code true} if the app can read phone state or has carrier privilege; - * {@code false} otherwise. + * Check whether the calling packages has carrier privileges for the passing subscription. + * @return {@code true} if the caller has carrier privileges, {@false} otherwise. */ - public static boolean checkReadPhoneStateOnAnyActiveSub(Context context, int pid, int uid, - String callingPackage, @Nullable String callingFeatureId, String message) { - return checkReadPhoneStateOnAnyActiveSub(context, TELEPHONY_SUPPLIER, pid, uid, - callingPackage, callingFeatureId, message); + public static boolean checkCarrierPrivilegeForSubId(Context context, int subId) { + if (SubscriptionManager.isValidSubscriptionId(subId) + && getCarrierPrivilegeStatus(context, subId, Binder.getCallingUid()) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return true; + } + return false; } /** @@ -242,9 +186,7 @@ public final class TelephonyPermissions { * @return {@code true} if the app can read phone state or has carrier privilege; * {@code false} otherwise. */ - @VisibleForTesting - public static boolean checkReadPhoneStateOnAnyActiveSub( - Context context, Supplier<ITelephony> telephonySupplier, int pid, int uid, + public static boolean checkReadPhoneStateOnAnyActiveSub(Context context, int pid, int uid, String callingPackage, @Nullable String callingFeatureId, String message) { try { context.enforcePermission( @@ -259,7 +201,7 @@ public final class TelephonyPermissions { } catch (SecurityException phoneStateException) { // If we don't have the runtime permission, but do have carrier privileges, that // suffices for reading phone state. - return checkCarrierPrivilegeForAnySubId(context, telephonySupplier, uid); + return checkCarrierPrivilegeForAnySubId(context, uid); } } @@ -374,12 +316,11 @@ public final class TelephonyPermissions { } // If the calling package has carrier privileges for specified sub, then allow access. - if (checkCarrierPrivilegeForSubId(subId)) return true; + if (checkCarrierPrivilegeForSubId(context, subId)) return true; // If the calling package has carrier privileges for any subscription // and allowCarrierPrivilegeOnAnySub is set true, then allow access. - if (allowCarrierPrivilegeOnAnySub && checkCarrierPrivilegeForAnySubId( - context, TELEPHONY_SUPPLIER, uid)) { + if (allowCarrierPrivilegeOnAnySub && checkCarrierPrivilegeForAnySubId(context, uid)) { return true; } @@ -471,7 +412,7 @@ public final class TelephonyPermissions { uid) == PackageManager.PERMISSION_GRANTED) { return false; } - if (checkCarrierPrivilegeForSubId(subId)) { + if (checkCarrierPrivilegeForSubId(context, subId)) { return false; } } @@ -487,26 +428,12 @@ public final class TelephonyPermissions { public static boolean checkReadCallLog( Context context, int subId, int pid, int uid, String callingPackage, @Nullable String callingPackageName) { - return checkReadCallLog( - context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, callingPackageName); - } - - /** - * Check whether the app with the given pid/uid can read the call log. - * @return {@code true} if the specified app has the read call log permission and AppOpp granted - * to it, {@code false} otherwise. - */ - @VisibleForTesting - public static boolean checkReadCallLog( - Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid, - String callingPackage, @Nullable String callingFeatureId) { - if (context.checkPermission(Manifest.permission.READ_CALL_LOG, pid, uid) != PERMISSION_GRANTED) { // If we don't have the runtime permission, but do have carrier privileges, that // suffices for being able to see the call phone numbers. if (SubscriptionManager.isValidSubscriptionId(subId)) { - enforceCarrierPrivilege(telephonySupplier, subId, uid, "readCallLog"); + enforceCarrierPrivilege(context, subId, uid, "readCallLog"); return true; } return false; @@ -529,7 +456,7 @@ public final class TelephonyPermissions { Context context, int subId, String callingPackage, @Nullable String callingFeatureId, String message) { return checkReadPhoneNumber( - context, TELEPHONY_SUPPLIER, subId, Binder.getCallingPid(), Binder.getCallingUid(), + context, subId, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, callingFeatureId, message); } @@ -541,7 +468,7 @@ public final class TelephonyPermissions { */ @VisibleForTesting public static boolean checkReadPhoneNumber( - Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid, + Context context, int subId, int pid, int uid, String callingPackage, @Nullable String callingFeatureId, String message) { // Default SMS app can always read it. AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); @@ -556,7 +483,7 @@ public final class TelephonyPermissions { // First, check if we can read the phone state. try { return checkReadPhoneState( - context, telephonySupplier, subId, pid, uid, callingPackage, callingFeatureId, + context, subId, pid, uid, callingPackage, callingFeatureId, message); } catch (SecurityException readPhoneStateSecurityException) { } @@ -598,7 +525,7 @@ public final class TelephonyPermissions { } if (DBG) Log.d(LOG_TAG, "No modify permission, check carrier privilege next."); - enforceCallingOrSelfCarrierPrivilege(subId, message); + enforceCallingOrSelfCarrierPrivilege(context, subId, message); } /** @@ -618,7 +545,7 @@ public final class TelephonyPermissions { Log.d(LOG_TAG, "No READ_PHONE_STATE permission, check carrier privilege next."); } - enforceCallingOrSelfCarrierPrivilege(subId, message); + enforceCallingOrSelfCarrierPrivilege(context, subId, message); } /** @@ -639,7 +566,7 @@ public final class TelephonyPermissions { + "check carrier privilege next."); } - enforceCallingOrSelfCarrierPrivilege(subId, message); + enforceCallingOrSelfCarrierPrivilege(context, subId, message); } /** @@ -647,21 +574,18 @@ public final class TelephonyPermissions { * * @throws SecurityException if the caller does not have the required privileges */ - public static void enforceCallingOrSelfCarrierPrivilege(int subId, String message) { + public static void enforceCallingOrSelfCarrierPrivilege( + Context context, int subId, String message) { // NOTE: It's critical that we explicitly pass the calling UID here rather than call // TelephonyManager#hasCarrierPrivileges directly, as the latter only works when called from // the phone process. When called from another process, it will check whether that process // has carrier privileges instead. - enforceCarrierPrivilege(subId, Binder.getCallingUid(), message); - } - - private static void enforceCarrierPrivilege(int subId, int uid, String message) { - enforceCarrierPrivilege(TELEPHONY_SUPPLIER, subId, uid, message); + enforceCarrierPrivilege(context, subId, Binder.getCallingUid(), message); } private static void enforceCarrierPrivilege( - Supplier<ITelephony> telephonySupplier, int subId, int uid, String message) { - if (getCarrierPrivilegeStatus(telephonySupplier, subId, uid) + Context context, int subId, int uid, String message) { + if (getCarrierPrivilegeStatus(context, subId, uid) != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { if (DBG) Log.e(LOG_TAG, "No Carrier Privilege."); throw new SecurityException(message); @@ -669,13 +593,12 @@ public final class TelephonyPermissions { } /** Returns whether the provided uid has carrier privileges for any active subscription ID. */ - private static boolean checkCarrierPrivilegeForAnySubId( - Context context, Supplier<ITelephony> telephonySupplier, int uid) { + private static boolean checkCarrierPrivilegeForAnySubId(Context context, int uid) { SubscriptionManager sm = (SubscriptionManager) context.getSystemService( Context.TELEPHONY_SUBSCRIPTION_SERVICE); int[] activeSubIds = sm.getActiveSubscriptionIdList(/* visibleOnly */ false); for (int activeSubId : activeSubIds) { - if (getCarrierPrivilegeStatus(telephonySupplier, activeSubId, uid) + if (getCarrierPrivilegeStatus(context, activeSubId, uid) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { return true; } @@ -683,18 +606,15 @@ public final class TelephonyPermissions { return false; } - private static int getCarrierPrivilegeStatus( - Supplier<ITelephony> telephonySupplier, int subId, int uid) { - ITelephony telephony = telephonySupplier.get(); + private static int getCarrierPrivilegeStatus(Context context, int subId, int uid) { + final long identity = Binder.clearCallingIdentity(); try { - if (telephony != null) { - return telephony.getCarrierPrivilegeStatusForUid(subId, uid); - } - } catch (RemoteException e) { - // Fallback below. + TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService( + Context.TELEPHONY_SERVICE); + return telephonyManager.createForSubscriptionId(subId).getCarrierPrivilegeStatus(uid); + } finally { + Binder.restoreCallingIdentity(identity); } - Log.e(LOG_TAG, "Phone process is down, cannot check carrier privileges"); - return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } /** diff --git a/telephony/common/com/google/android/mms/ContentType.java b/telephony/common/com/google/android/mms/ContentType.java index 12e4b7e26e1e..4a971dd34c8f 100644 --- a/telephony/common/com/google/android/mms/ContentType.java +++ b/telephony/common/com/google/android/mms/ContentType.java @@ -17,7 +17,7 @@ package com.google.android.mms; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import java.util.ArrayList; diff --git a/telephony/common/com/google/android/mms/InvalidHeaderValueException.java b/telephony/common/com/google/android/mms/InvalidHeaderValueException.java index 2836c3075b3b..55087ff0fb1d 100644 --- a/telephony/common/com/google/android/mms/InvalidHeaderValueException.java +++ b/telephony/common/com/google/android/mms/InvalidHeaderValueException.java @@ -17,7 +17,7 @@ package com.google.android.mms; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * Thrown when an invalid header value was set. diff --git a/telephony/common/com/google/android/mms/MmsException.java b/telephony/common/com/google/android/mms/MmsException.java index 5be33ed1fac9..24bceb37f590 100644 --- a/telephony/common/com/google/android/mms/MmsException.java +++ b/telephony/common/com/google/android/mms/MmsException.java @@ -17,7 +17,7 @@ package com.google.android.mms; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** * A generic exception that is thrown by the Mms client. diff --git a/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java b/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java index ae447d7a7417..8693385bb032 100644 --- a/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java +++ b/telephony/common/com/google/android/mms/pdu/AcknowledgeInd.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/Base64.java b/telephony/common/com/google/android/mms/pdu/Base64.java index 483fa7f9842e..0d6a46a59fcc 100644 --- a/telephony/common/com/google/android/mms/pdu/Base64.java +++ b/telephony/common/com/google/android/mms/pdu/Base64.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; public class Base64 { /** diff --git a/telephony/common/com/google/android/mms/pdu/CharacterSets.java b/telephony/common/com/google/android/mms/pdu/CharacterSets.java index 27da35e2d928..5172b7b67f88 100644 --- a/telephony/common/com/google/android/mms/pdu/CharacterSets.java +++ b/telephony/common/com/google/android/mms/pdu/CharacterSets.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import java.io.UnsupportedEncodingException; import java.util.HashMap; diff --git a/telephony/common/com/google/android/mms/pdu/DeliveryInd.java b/telephony/common/com/google/android/mms/pdu/DeliveryInd.java index 7093ac63338c..8fb6a7545abf 100644 --- a/telephony/common/com/google/android/mms/pdu/DeliveryInd.java +++ b/telephony/common/com/google/android/mms/pdu/DeliveryInd.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java b/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java index 41662750842f..8c0380f77cdd 100644 --- a/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java +++ b/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java @@ -17,10 +17,9 @@ package com.google.android.mms.pdu; +import android.compat.annotation.UnsupportedAppUsage; import android.util.Log; -import dalvik.annotation.compat.UnsupportedAppUsage; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; diff --git a/telephony/common/com/google/android/mms/pdu/GenericPdu.java b/telephony/common/com/google/android/mms/pdu/GenericPdu.java index ebf16ac7e632..320b13ffed2b 100644 --- a/telephony/common/com/google/android/mms/pdu/GenericPdu.java +++ b/telephony/common/com/google/android/mms/pdu/GenericPdu.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java b/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java index e108f7600baf..42a89c69e873 100644 --- a/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java +++ b/telephony/common/com/google/android/mms/pdu/MultimediaMessagePdu.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/NotificationInd.java b/telephony/common/com/google/android/mms/pdu/NotificationInd.java index b561bd4ab3a7..ca4615c2e9fe 100644 --- a/telephony/common/com/google/android/mms/pdu/NotificationInd.java +++ b/telephony/common/com/google/android/mms/pdu/NotificationInd.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java b/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java index 3c70f86a0890..ebd81afc0173 100644 --- a/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java +++ b/telephony/common/com/google/android/mms/pdu/NotifyRespInd.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/PduBody.java b/telephony/common/com/google/android/mms/pdu/PduBody.java index 51914e4110b0..f7f285f653b9 100644 --- a/telephony/common/com/google/android/mms/pdu/PduBody.java +++ b/telephony/common/com/google/android/mms/pdu/PduBody.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import java.util.HashMap; import java.util.Map; diff --git a/telephony/common/com/google/android/mms/pdu/PduComposer.java b/telephony/common/com/google/android/mms/pdu/PduComposer.java index e24bf21a11b5..b8b212c493aa 100644 --- a/telephony/common/com/google/android/mms/pdu/PduComposer.java +++ b/telephony/common/com/google/android/mms/pdu/PduComposer.java @@ -17,12 +17,11 @@ package com.google.android.mms.pdu; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.content.Context; import android.text.TextUtils; -import dalvik.annotation.compat.UnsupportedAppUsage; - import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; diff --git a/telephony/common/com/google/android/mms/pdu/PduContentTypes.java b/telephony/common/com/google/android/mms/pdu/PduContentTypes.java index 8551b2f9b693..57141fedf1e0 100644 --- a/telephony/common/com/google/android/mms/pdu/PduContentTypes.java +++ b/telephony/common/com/google/android/mms/pdu/PduContentTypes.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; public class PduContentTypes { /** diff --git a/telephony/common/com/google/android/mms/pdu/PduHeaders.java b/telephony/common/com/google/android/mms/pdu/PduHeaders.java index b5244645fda1..3e6218480dc5 100644 --- a/telephony/common/com/google/android/mms/pdu/PduHeaders.java +++ b/telephony/common/com/google/android/mms/pdu/PduHeaders.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/PduParser.java b/telephony/common/com/google/android/mms/pdu/PduParser.java index f48399410723..5340245ae869 100755 --- a/telephony/common/com/google/android/mms/pdu/PduParser.java +++ b/telephony/common/com/google/android/mms/pdu/PduParser.java @@ -17,10 +17,9 @@ package com.google.android.mms.pdu; +import android.compat.annotation.UnsupportedAppUsage; import android.util.Log; -import dalvik.annotation.compat.UnsupportedAppUsage; - import com.google.android.mms.ContentType; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/PduPart.java b/telephony/common/com/google/android/mms/pdu/PduPart.java index 09b775118dc3..8dd976b2569f 100644 --- a/telephony/common/com/google/android/mms/pdu/PduPart.java +++ b/telephony/common/com/google/android/mms/pdu/PduPart.java @@ -17,10 +17,9 @@ package com.google.android.mms.pdu; +import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; -import dalvik.annotation.compat.UnsupportedAppUsage; - import java.util.HashMap; import java.util.Map; diff --git a/telephony/common/com/google/android/mms/pdu/PduPersister.java b/telephony/common/com/google/android/mms/pdu/PduPersister.java index 8efca0ea3909..fcd5b8ff57a8 100755 --- a/telephony/common/com/google/android/mms/pdu/PduPersister.java +++ b/telephony/common/com/google/android/mms/pdu/PduPersister.java @@ -17,6 +17,7 @@ package com.google.android.mms.pdu; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; @@ -40,8 +41,6 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; -import dalvik.annotation.compat.UnsupportedAppUsage; - import com.google.android.mms.ContentType; import com.google.android.mms.InvalidHeaderValueException; import com.google.android.mms.MmsException; diff --git a/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java b/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java index 9d6535c72e90..4e1d7f5775ec 100644 --- a/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java +++ b/telephony/common/com/google/android/mms/pdu/QuotedPrintable.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import java.io.ByteArrayOutputStream; diff --git a/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java b/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java index e38c62dde622..4ba3c71580e0 100644 --- a/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java +++ b/telephony/common/com/google/android/mms/pdu/ReadOrigInd.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/ReadRecInd.java b/telephony/common/com/google/android/mms/pdu/ReadRecInd.java index 9696bc259d00..37ccfb9c9b9b 100644 --- a/telephony/common/com/google/android/mms/pdu/ReadRecInd.java +++ b/telephony/common/com/google/android/mms/pdu/ReadRecInd.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/RetrieveConf.java b/telephony/common/com/google/android/mms/pdu/RetrieveConf.java index 03755af4189c..260adfc093f2 100644 --- a/telephony/common/com/google/android/mms/pdu/RetrieveConf.java +++ b/telephony/common/com/google/android/mms/pdu/RetrieveConf.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/SendConf.java b/telephony/common/com/google/android/mms/pdu/SendConf.java index b85982791ada..779923801bfa 100644 --- a/telephony/common/com/google/android/mms/pdu/SendConf.java +++ b/telephony/common/com/google/android/mms/pdu/SendConf.java @@ -17,7 +17,7 @@ package com.google.android.mms.pdu; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.InvalidHeaderValueException; diff --git a/telephony/common/com/google/android/mms/pdu/SendReq.java b/telephony/common/com/google/android/mms/pdu/SendReq.java index c1b7f934c0f7..6e2f2da01791 100644 --- a/telephony/common/com/google/android/mms/pdu/SendReq.java +++ b/telephony/common/com/google/android/mms/pdu/SendReq.java @@ -17,10 +17,9 @@ package com.google.android.mms.pdu; +import android.compat.annotation.UnsupportedAppUsage; import android.util.Log; -import dalvik.annotation.compat.UnsupportedAppUsage; - import com.google.android.mms.InvalidHeaderValueException; public class SendReq extends MultimediaMessagePdu { diff --git a/telephony/common/com/google/android/mms/util/AbstractCache.java b/telephony/common/com/google/android/mms/util/AbstractCache.java index ab5d48a4ce3d..25862e73581e 100644 --- a/telephony/common/com/google/android/mms/util/AbstractCache.java +++ b/telephony/common/com/google/android/mms/util/AbstractCache.java @@ -17,10 +17,9 @@ package com.google.android.mms.util; +import android.compat.annotation.UnsupportedAppUsage; import android.util.Log; -import dalvik.annotation.compat.UnsupportedAppUsage; - import java.util.HashMap; public abstract class AbstractCache<K, V> { diff --git a/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java b/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java index 118de465a518..0f9390daa725 100644 --- a/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java +++ b/telephony/common/com/google/android/mms/util/DownloadDrmHelper.java @@ -17,12 +17,11 @@ package com.google.android.mms.util; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.drm.DrmManagerClient; import android.util.Log; -import dalvik.annotation.compat.UnsupportedAppUsage; - public class DownloadDrmHelper { private static final String TAG = "DownloadDrmHelper"; diff --git a/telephony/common/com/google/android/mms/util/DrmConvertSession.java b/telephony/common/com/google/android/mms/util/DrmConvertSession.java index 0e8ec91f4ef6..156c7ad8baac 100644 --- a/telephony/common/com/google/android/mms/util/DrmConvertSession.java +++ b/telephony/common/com/google/android/mms/util/DrmConvertSession.java @@ -16,14 +16,13 @@ */ package com.google.android.mms.util; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.drm.DrmConvertedStatus; import android.drm.DrmManagerClient; import android.provider.Downloads; import android.util.Log; -import dalvik.annotation.compat.UnsupportedAppUsage; - import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; diff --git a/telephony/common/com/google/android/mms/util/PduCache.java b/telephony/common/com/google/android/mms/util/PduCache.java index 94e38946f632..c380d6b3e30f 100644 --- a/telephony/common/com/google/android/mms/util/PduCache.java +++ b/telephony/common/com/google/android/mms/util/PduCache.java @@ -17,14 +17,13 @@ package com.google.android.mms.util; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentUris; import android.content.UriMatcher; import android.net.Uri; import android.provider.Telephony.Mms; import android.util.Log; -import dalvik.annotation.compat.UnsupportedAppUsage; - import java.util.HashMap; import java.util.HashSet; diff --git a/telephony/common/com/google/android/mms/util/PduCacheEntry.java b/telephony/common/com/google/android/mms/util/PduCacheEntry.java index 1ecd1bf93e7f..a4a25d2471ff 100644 --- a/telephony/common/com/google/android/mms/util/PduCacheEntry.java +++ b/telephony/common/com/google/android/mms/util/PduCacheEntry.java @@ -17,7 +17,7 @@ package com.google.android.mms.util; -import dalvik.annotation.compat.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import com.google.android.mms.pdu.GenericPdu; diff --git a/telephony/common/com/google/android/mms/util/SqliteWrapper.java b/telephony/common/com/google/android/mms/util/SqliteWrapper.java index 2dd1dc11c2a9..31fe4d7683d6 100644 --- a/telephony/common/com/google/android/mms/util/SqliteWrapper.java +++ b/telephony/common/com/google/android/mms/util/SqliteWrapper.java @@ -18,6 +18,7 @@ package com.google.android.mms.util; import android.app.ActivityManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; @@ -27,8 +28,6 @@ import android.net.Uri; import android.util.Log; import android.widget.Toast; -import dalvik.annotation.compat.UnsupportedAppUsage; - public final class SqliteWrapper { private static final String TAG = "SqliteWrapper"; private static final String SQLITE_EXCEPTION_DETAIL_MESSAGE diff --git a/telephony/java/android/service/euicc/EuiccProfileInfo.java b/telephony/java/android/service/euicc/EuiccProfileInfo.java index 6c357ccdd03d..8450a9018634 100644 --- a/telephony/java/android/service/euicc/EuiccProfileInfo.java +++ b/telephony/java/android/service/euicc/EuiccProfileInfo.java @@ -19,7 +19,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.service.carrier.CarrierIdentifier; diff --git a/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java b/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java index c7a985160730..2382f657c9ee 100644 --- a/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java +++ b/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java @@ -17,7 +17,7 @@ package android.service.euicc; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.telephony.euicc.DownloadableSubscription; diff --git a/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java b/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java index abd4065c754a..d0fb51180c1d 100644 --- a/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java +++ b/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java @@ -17,7 +17,7 @@ package android.service.euicc; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.telephony.euicc.DownloadableSubscription; diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index e6e0f9ec455f..607b7d98a1de 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -16,8 +16,6 @@ package android.telephony; -import com.android.telephony.Rlog; - import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,7 +24,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; import android.os.PersistableBundle; @@ -37,6 +35,7 @@ import android.telecom.TelecomManager; import android.telephony.ims.ImsReasonInfo; import com.android.internal.telephony.ICarrierConfigLoader; +import com.android.telephony.Rlog; /** * Provides access to telephony configuration values that are carrier-specific. @@ -301,7 +300,6 @@ public class CarrierConfigManager { /** * A string array containing numbers that shouldn't be included in the call log. - * @hide */ public static final String KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY = "unloggable_numbers_string_array"; @@ -314,12 +312,11 @@ public class CarrierConfigManager { KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool"; /** - * Do only allow auto selection in Advanced Network Settings when in home network. + * Only allow auto selection in Advanced Network Settings when in home network. * Manual selection is allowed when in roaming network. - * @hide */ - public static final String - KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network"; + public static final String KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = + "only_auto_select_in_home_network"; /** * Control whether users receive a simplified network settings UI and improved network @@ -583,9 +580,6 @@ public class CarrierConfigManager { * registration state to change. That is, turning on or off mobile data will not cause VT to be * enabled or disabled. * When {@code false}, disabling mobile data will cause VT to be de-registered. - * <p> - * See also {@link #KEY_VILTE_DATA_IS_METERED_BOOL}. - * @hide */ public static final String KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS = "ignore_data_enabled_changed_for_video_calls"; @@ -649,7 +643,6 @@ public class CarrierConfigManager { /** * Default WFC_IMS_enabled: true VoWiFi by default is on * false VoWiFi by default is off - * @hide */ public static final String KEY_CARRIER_DEFAULT_WFC_IMS_ENABLED_BOOL = "carrier_default_wfc_ims_enabled_bool"; @@ -721,9 +714,7 @@ public class CarrierConfigManager { * * As of now, Verizon is the only carrier enforcing this dependency in their * WFC awareness and activation requirements. - * - * @hide - * */ + */ public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool"; @@ -855,9 +846,12 @@ public class CarrierConfigManager { "carrier_force_disable_etws_cmas_test_bool"; /** - * The default flag specifying whether "Turn on Notifications" option will be always shown in - * Settings->More->Emergency broadcasts menu regardless developer options is turned on or not. + * The default flag specifying whether "Allow alerts" option will be always shown in + * emergency alerts settings regardless developer options is turned on or not. + * + * @deprecated The allow alerts option is always shown now. No longer need a config for that. */ + @Deprecated public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; @@ -1081,10 +1075,6 @@ public class CarrierConfigManager { * * When {@code false}, the old behavior is used, where the toggle in accessibility settings is * used to set the IMS stack's RTT enabled state. - * - * @deprecated -- this flag no longer does anything. Remove once the new behavior is verified. - * - * @hide */ public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL = "ignore_rtt_mode_setting_bool"; @@ -1125,7 +1115,6 @@ public class CarrierConfigManager { * Determines whether the IMS conference merge process supports and returns its participants * data. When {@code true}, on merge complete, conference call would have a list of its * participants returned in XML format, {@code false otherwise}. - * @hide */ public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL = "support_ims_conference_event_package_bool"; @@ -1198,20 +1187,18 @@ public class CarrierConfigManager { public static final String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array"; /** - * Determine whether user can switch Wi-Fi preferred or Cellular preferred in calling preference. + * Determine whether user can switch Wi-Fi preferred or Cellular preferred + * in calling preference. * Some operators support Wi-Fi Calling only, not VoLTE. * They don't need "Cellular preferred" option. - * In this case, set uneditalbe attribute for preferred preference. - * @hide + * In this case, set uneditable attribute for preferred preference. */ public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool"; - /** - * Flag to indicate if Wi-Fi needs to be disabled in ECBM - * @hide - **/ - public static final String - KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm"; + /** + * Flag to indicate if Wi-Fi needs to be disabled in ECBM. + */ + public static final String KEY_CONFIG_WIFI_DISABLE_IN_ECBM = "config_wifi_disable_in_ecbm"; /** * List operator-specific error codes and indices of corresponding error strings in @@ -1275,9 +1262,8 @@ public class CarrierConfigManager { public static final String KEY_WFC_SPN_USE_ROOT_LOCALE = "wfc_spn_use_root_locale"; /** - * The Component Name of the activity that can setup the emergency addrees for WiFi Calling + * The Component Name of the activity that can setup the emergency address for WiFi Calling * as per carrier requirement. - * @hide */ public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string"; @@ -1441,22 +1427,19 @@ public class CarrierConfigManager { public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool"; /** - * APN types that user is not allowed to modify - * @hide + * APN types that user is not allowed to modify. */ public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY = "read_only_apn_types_string_array"; /** - * APN fields that user is not allowed to modify - * @hide + * APN fields that user is not allowed to modify. */ public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY = "read_only_apn_fields_string_array"; /** * Default value of APN types field if not specified by user when adding/modifying an APN. - * @hide */ public static final String KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY = "apn_settings_default_apn_types_string_array"; @@ -1487,29 +1470,25 @@ public class CarrierConfigManager { "hide_digits_helper_text_on_stk_input_screen_bool"; /** - * Boolean indicating if show data RAT icon on status bar even when data is disabled - * @hide + * Boolean indicating if show data RAT icon on status bar even when data is disabled. */ public static final String KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL = "always_show_data_rat_icon_bool"; /** - * Boolean indicating if default data account should show LTE or 4G icon - * @hide + * Boolean indicating if default data account should show LTE or 4G icon. */ public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool"; /** * Boolean indicating if default data account should show 4G icon when in 3G. - * @hide */ public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool"; /** - * Boolean indicating if lte+ icon should be shown if available - * @hide + * Boolean indicating if LTE+ icon should be shown if available. */ public static final String KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL = "hide_lte_plus_data_icon_bool"; @@ -1524,10 +1503,8 @@ public class CarrierConfigManager { "operator_name_filter_pattern_string"; /** - * The string is used to compare with operator name. If it matches the pattern then show - * specific data icon. - * - * @hide + * The string is used to compare with operator name. + * If it matches the pattern then show specific data icon. */ public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING = "show_carrier_data_icon_pattern_string"; @@ -1540,33 +1517,28 @@ public class CarrierConfigManager { "show_precise_failed_cause_bool"; /** - * Boolean to decide whether lte is enabled. - * @hide + * Boolean to decide whether LTE is enabled. */ public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool"; /** * Boolean to decide whether TD-SCDMA is supported. - * @hide */ public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool"; /** * A list of mcc/mnc that support TD-SCDMA for device when connect to the roaming network. - * @hide */ public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY = "support_tdscdma_roaming_networks_string_array"; /** * Boolean to decide whether world mode is enabled. - * @hide */ public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool"; /** * Flatten {@link android.content.ComponentName} of the carrier's settings activity. - * @hide */ public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string"; @@ -1620,25 +1592,23 @@ public class CarrierConfigManager { /** * Defines carrier-specific actions which act upon * com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED, used for customization of the - * default carrier app + * default carrier app. * Format: "CARRIER_ACTION_IDX, ..." * Where {@code CARRIER_ACTION_IDX} is an integer defined in - * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils} + * com.android.carrierdefaultapp.CarrierActionUtils * Example: - * {@link com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS - * disable_metered_apns} - * @hide + * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS + * disables metered APNs */ - @UnsupportedAppUsage + @SuppressLint("IntentName") public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY = "carrier_default_actions_on_redirection_string_array"; /** - * Defines carrier-specific actions which act upon - * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED + * Defines carrier-specific actions which act upon CARRIER_SIGNAL_REQUEST_NETWORK_FAILED * and configured signal args: - * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_APN_TYPE_KEY apnType}, - * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_ERROR_CODE_KEY errorCode} + * android.telephony.TelephonyManager#EXTRA_APN_TYPE, + * android.telephony.TelephonyManager#EXTRA_ERROR_CODE * used for customization of the default carrier app * Format: * { @@ -1646,42 +1616,41 @@ public class CarrierConfigManager { * "APN_1, ERROR_CODE_2 : CARRIER_ACTION_IDX_1 " * } * Where {@code APN_1} is a string defined in - * {@link com.android.internal.telephony.PhoneConstants PhoneConstants} + * com.android.internal.telephony.PhoneConstants * Example: "default" * - * {@code ERROR_CODE_1} is an integer defined in - * {@link DataFailCause DcFailure} + * {@code ERROR_CODE_1} is an integer defined in android.telephony.DataFailCause * Example: - * {@link DataFailCause#MISSING_UNKNOWN_APN} + * android.telephony.DataFailCause#MISSING_UNKNOWN_APN * * {@code CARRIER_ACTION_IDX_1} is an integer defined in - * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils} + * com.android.carrierdefaultapp.CarrierActionUtils * Example: - * {@link com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS} - * @hide + * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_DISABLE_METERED_APNS + * disables metered APNs */ + @SuppressLint("IntentName") public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY = "carrier_default_actions_on_dcfailure_string_array"; /** - * Defines carrier-specific actions which act upon - * com.android.internal.telephony.CARRIER_SIGNAL_RESET, used for customization of the - * default carrier app + * Defines carrier-specific actions which act upon CARRIER_SIGNAL_RESET, + * used for customization of the default carrier app. * Format: "CARRIER_ACTION_IDX, ..." * Where {@code CARRIER_ACTION_IDX} is an integer defined in - * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils} + * com.android.carrierdefaultapp.CarrierActionUtils * Example: - * {@link com.android.carrierdefaultapp.CarrierActionUtils - * #CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS clear all notifications on reset} - * @hide + * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS + * clears all notifications on reset */ + @SuppressLint("IntentName") public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET = "carrier_default_actions_on_reset_string_array"; /** * Defines carrier-specific actions which act upon * com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE, - * used for customization of the default carrier app + * used for customization of the default carrier app. * Format: * { * "true : CARRIER_ACTION_IDX_1", @@ -1689,17 +1658,17 @@ public class CarrierConfigManager { * } * Where {@code true} is a boolean indicates default network available/unavailable * Where {@code CARRIER_ACTION_IDX} is an integer defined in - * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils} + * com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils * Example: - * {@link com.android.carrierdefaultapp.CarrierActionUtils - * #CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER enable the app as the default URL handler} - * @hide + * com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER + * enables the app as the default URL handler */ + @SuppressLint("IntentName") public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE = "carrier_default_actions_on_default_network_available_string_array"; + /** - * Defines a list of acceptable redirection url for default carrier app - * @hides + * Defines a list of acceptable redirection url for default carrier app. */ public static final String KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY = "carrier_default_redirection_url_string_array"; @@ -1827,10 +1796,10 @@ public class CarrierConfigManager { /** * Determines whether to enable enhanced call blocking feature on the device. - * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED - * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE - * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE - * @see SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN + * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED + * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PRIVATE + * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_PAYPHONE + * android.provider.BlockedNumberContract.SystemContract#ENHANCED_SETTING_KEY_BLOCK_UNKNOWN * * <p> * 1. For Single SIM(SS) device, it can be customized in both carrier_config_mccmnc.xml @@ -1840,7 +1809,6 @@ public class CarrierConfigManager { * function is used regardless of SIM. * <p> * If {@code true} enable enhanced call blocking feature on the device, {@code false} otherwise. - * @hide */ public static final String KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL = "support_enhanced_call_blocking_bool"; @@ -1943,7 +1911,6 @@ public class CarrierConfigManager { /** * Flag indicating whether the carrier supports call deflection for an incoming IMS call. - * @hide */ public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL = "carrier_allow_deflect_ims_call_bool"; @@ -2008,8 +1975,6 @@ public class CarrierConfigManager { /** * Whether system apps are allowed to use fallback if carrier video call is not available. * Defaults to {@code true}. - * - * @hide */ public static final String KEY_ALLOW_VIDEO_CALLING_FALLBACK_BOOL = "allow_video_calling_fallback_bool"; @@ -2047,9 +2012,8 @@ public class CarrierConfigManager { "enhanced_4g_lte_title_variant_bool"; /** - * The index indicates the carrier specified title string of Enahnce 4G LTE Mode settings. + * The index indicates the carrier specified title string of Enhanced 4G LTE Mode settings. * Default value is 0, which indicates the default title string. - * @hide */ public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT = "enhanced_4g_lte_title_variant_int"; @@ -2093,15 +2057,13 @@ public class CarrierConfigManager { * {@link #KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL} is false. If * {@link #KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL} is true, this * configuration is ignored and roaming preference cannot be changed. - * @hide */ public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL = "editable_wfc_roaming_mode_bool"; /** - * Flag specifying wether to show blocking pay phone option in blocked numbers screen. Only show - * the option if payphone call presentation represents in the carrier's region. - * @hide + * Flag specifying whether to show blocking pay phone option in blocked numbers screen. + * Only show the option if payphone call presentation is present in the carrier's region. */ public static final java.lang.String KEY_SHOW_BLOCKING_PAY_PHONE_OPTION_BOOL = "show_blocking_pay_phone_option_bool"; @@ -2111,7 +2073,6 @@ public class CarrierConfigManager { * {@code false} - roaming preference can be selected separately from the home preference. * {@code true} - roaming preference is the same as home preference and * {@link #KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT} is used as the default value. - * @hide */ public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = "use_wfc_home_network_mode_in_roaming_network_bool"; @@ -2145,7 +2106,6 @@ public class CarrierConfigManager { * while the device is registered over WFC. Default value is -1, which indicates * that this notification is not pertinent for a particular carrier. We've added a delay * to prevent false positives. - * @hide */ public static final String KEY_EMERGENCY_NOTIFICATION_DELAY_INT = "emergency_notification_delay_int"; @@ -2196,7 +2156,6 @@ public class CarrierConfigManager { /** * Flag specifying whether to show an alert dialog for video call charges. * By default this value is {@code false}. - * @hide */ public static final String KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL = "show_video_call_charges_alert_dialog_bool"; @@ -2483,10 +2442,9 @@ public class CarrierConfigManager { /** * Identifies if the key is available for WLAN or EPDG or both. The value is a bitmask. * 0 indicates that neither EPDG or WLAN is enabled. - * 1 indicates that key type {@link TelephonyManager#KEY_TYPE_EPDG} is enabled. - * 2 indicates that key type {@link TelephonyManager#KEY_TYPE_WLAN} is enabled. + * 1 indicates that key type TelephonyManager#KEY_TYPE_EPDG is enabled. + * 2 indicates that key type TelephonyManager#KEY_TYPE_WLAN is enabled. * 3 indicates that both are enabled. - * @hide */ public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int"; @@ -2505,7 +2463,6 @@ public class CarrierConfigManager { /** * Flag specifying whether IMS registration state menu is shown in Status Info setting, * default to false. - * @hide */ public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL = "show_ims_registration_status_bool"; @@ -2561,7 +2518,6 @@ public class CarrierConfigManager { /** * The flag to disable the popup dialog which warns the user of data charges. - * @hide */ public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL = "disable_charge_indication_bool"; @@ -2626,15 +2582,13 @@ public class CarrierConfigManager { /** * Determines whether any carrier has been identified and its specific config has been applied, * default to false. - * @hide */ public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool"; /** * Determines whether we should show a warning asking the user to check with their carrier - * on pricing when the user enabled data roaming. + * on pricing when the user enabled data roaming, * default to false. - * @hide */ public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool"; @@ -2786,10 +2740,10 @@ public class CarrierConfigManager { * Specifies a carrier-defined {@link android.telecom.CallRedirectionService} which Telecom * will bind to for outgoing calls. An empty string indicates that no carrier-defined * {@link android.telecom.CallRedirectionService} is specified. - * @hide */ public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING = "call_redirection_service_component_name_string"; + /** * Support for the original string display of CDMA MO call. * By default, it is disabled. @@ -2902,8 +2856,8 @@ public class CarrierConfigManager { "call_waiting_service_class_int"; /** - * This configuration allow the system UI to display different 5G icon for different 5G - * scenario. + * This configuration allows the system UI to display different 5G icons for different 5G + * scenarios. * * There are five 5G scenarios: * 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using @@ -2920,24 +2874,22 @@ public class CarrierConfigManager { * 5G cell as a secondary cell) but the use of 5G is restricted. * * The configured string contains multiple key-value pairs separated by comma. For each pair, - * the key and value is separated by a colon. The key is corresponded to a 5G status above and + * the key and value are separated by a colon. The key corresponds to a 5G status above and * the value is the icon name. Use "None" as the icon name if no icon should be shown in a * specific 5G scenario. If the scenario is "None", config can skip this key and value. * * Icon name options: "5G_Plus", "5G". * * Here is an example: - * UE want to display 5G_Plus icon for scenario#1, and 5G icon for scenario#2; otherwise no + * UE wants to display 5G_Plus icon for scenario#1, and 5G icon for scenario#2; otherwise not * define. * The configuration is: "connected_mmwave:5G_Plus,connected:5G" - * - * @hide */ public static final String KEY_5G_ICON_CONFIGURATION_STRING = "5g_icon_configuration_string"; /** - * Timeout in second for displaying 5G icon, default value is 0 which means the timer is + * Timeout in seconds for displaying 5G icon, default value is 0 which means the timer is * disabled. * * System UI will show the 5G icon and start a timer with the timeout from this config when the @@ -2946,8 +2898,6 @@ public class CarrierConfigManager { * * If 5G is reacquired during this timer, the timer is canceled and restarted when 5G is next * lost. Allows us to momentarily lose 5G without blinking the icon. - * - * @hide */ public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT = "5g_icon_display_grace_period_sec_int"; @@ -3026,6 +2976,54 @@ public class CarrierConfigManager { "ping_test_before_data_switch_bool"; /** + * Controls whether to switch data to primary from opportunistic subscription + * if primary is out of service. This control only affects system or 1st party app + * initiated data switch, but will not override data switch initiated by privileged carrier apps + * This carrier config is used to disable this feature. + * @hide + */ + public static final String KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL = + "switch_data_to_primary_if_primary_is_oos_bool"; + + /** + * Controls the ping pong determination of opportunistic network. + * If opportunistic network is determined as out of service or below + * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT or + * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT within + * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG of switching to opportunistic network, + * it will be determined as ping pong situation by system app or 1st party app. + * @hide + */ + public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG = + "opportunistic_network_ping_pong_time_long"; + /** + * Controls back off time in milli seconds for switching back to + * opportunistic subscription. This time will be added to + * {@link CarrierConfigManager#KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG} to + * determine hysteresis time if there is ping pong situation + * (determined by system app or 1st party app) between primary and opportunistic + * subscription. Ping ping situation is defined in + * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG. + * If ping pong situation continuous #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG + * will be added to previously determined hysteresis time. + * @hide + */ + public static final String KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG = + "opportunistic_network_backoff_time_long"; + + /** + * Controls the max back off time in milli seconds for switching back to + * opportunistic subscription. + * This time will be the max hysteresis that can be determined irrespective of there is + * continuous ping pong situation or not as described in + * #KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG and + * #KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG. + * @hide + */ + public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG = + "opportunistic_network_max_backoff_time_long"; + + /** * Controls time in milliseconds until DcTracker reevaluates 5G connection state. * @hide */ @@ -3065,8 +3063,6 @@ public class CarrierConfigManager { * signal bar of primary network. By default it will be false, meaning whenever data * is going over opportunistic network, signal bar will reflect signal strength and rat * icon of that network. - * - * @hide */ public static final String KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN = "always_show_primary_signal_bar_in_opportunistic_network_boolean"; @@ -3280,8 +3276,6 @@ public class CarrierConfigManager { /** * Determines whether wifi calling location privacy policy is shown. - * - * @hide */ public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool"; @@ -3387,8 +3381,8 @@ public class CarrierConfigManager { "support_wps_over_ims_bool"; /** - * Holds the list of carrier certificate hashes. Note that each carrier has its own certificates - * @hide + * Holds the list of carrier certificate hashes. + * Note that each carrier has its own certificates. */ public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY = "carrier_certificate_string_array"; @@ -3572,7 +3566,7 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0); sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100); sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false); - sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, true); + sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false); sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0); sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true); @@ -3857,6 +3851,13 @@ public class CarrierConfigManager { /* Default value is 3 seconds. */ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 3000); sDefaults.putBoolean(KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL, true); + sDefaults.putBoolean(KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL, true); + /* Default value is 60 seconds. */ + sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG, 60000); + /* Default value is 10 seconds. */ + sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG, 10000); + /* Default value is 60 seconds. */ + sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000); /* Default value is 1 hour. */ sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000); sDefaults.putAll(Gps.getDefaults()); diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java index 8e703fee3126..399b1a4f25de 100644 --- a/telephony/java/android/telephony/CellIdentity.java +++ b/telephony/java/android/telephony/CellIdentity.java @@ -22,10 +22,13 @@ import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.hardware.radio.V1_0.CellInfoType; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import com.android.telephony.Rlog; + import java.util.Objects; import java.util.UUID; @@ -324,6 +327,86 @@ public abstract class CellIdentity implements Parcelable { } /** @hide */ + public static CellIdentity create(android.hardware.radio.V1_0.CellIdentity cellIdentity) { + if (cellIdentity == null) return null; + switch(cellIdentity.cellInfoType) { + case CellInfoType.GSM: { + if (cellIdentity.cellIdentityGsm.size() == 1) { + return new CellIdentityGsm(cellIdentity.cellIdentityGsm.get(0)); + } + break; + } + case CellInfoType.WCDMA: { + if (cellIdentity.cellIdentityWcdma.size() == 1) { + return new CellIdentityWcdma(cellIdentity.cellIdentityWcdma.get(0)); + } + break; + } + case CellInfoType.TD_SCDMA: { + if (cellIdentity.cellIdentityTdscdma.size() == 1) { + return new CellIdentityTdscdma(cellIdentity.cellIdentityTdscdma.get(0)); + } + break; + } + case CellInfoType.LTE: { + if (cellIdentity.cellIdentityLte.size() == 1) { + return new CellIdentityLte(cellIdentity.cellIdentityLte.get(0)); + } + break; + } + case CellInfoType.CDMA: { + if (cellIdentity.cellIdentityCdma.size() == 1) { + return new CellIdentityCdma(cellIdentity.cellIdentityCdma.get(0)); + } + break; + } + case CellInfoType.NONE: break; + default: break; + } + return null; + } + + /** @hide */ + public static CellIdentity create(android.hardware.radio.V1_2.CellIdentity cellIdentity) { + if (cellIdentity == null) return null; + switch(cellIdentity.cellInfoType) { + case CellInfoType.GSM: { + if (cellIdentity.cellIdentityGsm.size() == 1) { + return new CellIdentityGsm(cellIdentity.cellIdentityGsm.get(0)); + } + break; + } + case CellInfoType.WCDMA: { + if (cellIdentity.cellIdentityWcdma.size() == 1) { + return new CellIdentityWcdma(cellIdentity.cellIdentityWcdma.get(0)); + } + break; + } + case CellInfoType.TD_SCDMA: { + if (cellIdentity.cellIdentityTdscdma.size() == 1) { + return new CellIdentityTdscdma(cellIdentity.cellIdentityTdscdma.get(0)); + } + break; + } + case CellInfoType.LTE: { + if (cellIdentity.cellIdentityLte.size() == 1) { + return new CellIdentityLte(cellIdentity.cellIdentityLte.get(0)); + } + break; + } + case CellInfoType.CDMA: { + if (cellIdentity.cellIdentityCdma.size() == 1) { + return new CellIdentityCdma(cellIdentity.cellIdentityCdma.get(0)); + } + break; + } + case CellInfoType.NONE: break; + default: break; + } + return null; + } + + /** @hide */ public static CellIdentity create(android.hardware.radio.V1_5.CellIdentity ci) { if (ci == null) return null; switch (ci.getDiscriminator()) { diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java index 2ecdfce92825..49f425acead6 100644 --- a/telephony/java/android/telephony/CellIdentityGsm.java +++ b/telephony/java/android/telephony/CellIdentityGsm.java @@ -18,7 +18,7 @@ package android.telephony; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.telephony.gsm.GsmCellLocation; import android.text.TextUtils; diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index 15c91752badf..bc4655069dba 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -18,7 +18,7 @@ package android.telephony; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.telephony.gsm.GsmCellLocation; diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java index 4c090b8a96cd..31434c1d2adf 100644 --- a/telephony/java/android/telephony/PreciseDataConnectionState.java +++ b/telephony/java/android/telephony/PreciseDataConnectionState.java @@ -81,18 +81,20 @@ public final class PreciseDataConnectionState implements Parcelable { /** - * Constructor + * Constructor of PreciseDataConnectionState * * @param state the state of the data connection * @param networkType the access network that is/would carry this data connection * @param apnTypes the APN types that this data connection carries - * @param apnSetting if there is a valid APN for this Data Connection, then the APN Settings; - * if there is no valid APN setting for the specific type, then this will be null + * @param apn the APN of this data connection * @param linkProperties if the data connection is connected, the properties of the connection * @param failCause in case a procedure related to this data connection fails, a non-zero error * code indicating the cause of the failure. + * @param apnSetting if there is a valid APN for this Data Connection, then the APN Settings; + * if there is no valid APN setting for the specific type, then this will be null * @hide */ + @SystemApi public PreciseDataConnectionState(@DataState int state, @NetworkType int networkType, @ApnType int apnTypes, @NonNull String apn, diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 2c62d0667e19..ad8ac76642ae 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -1624,7 +1624,8 @@ public class ServiceState implements Parcelable { * @return Current data network type * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @SystemApi + @TestApi public @NetworkType int getDataNetworkType() { final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN); diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index c217b8b83c26..30a61c31ab81 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -20,8 +20,13 @@ import com.android.telephony.Rlog; import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; +import android.Manifest; +import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.StringDef; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.os.Binder; @@ -31,8 +36,10 @@ import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; import com.android.internal.telephony.Sms7BitEncodingTranslator; import com.android.internal.telephony.SmsConstants; +import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SmsMessageBase.SubmitPduBase; +import com.android.internal.telephony.cdma.sms.UserData; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -56,6 +63,16 @@ public class SmsMessage { UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3; } + /** @hide */ + @IntDef(prefix = { "ENCODING_" }, value = { + ENCODING_UNKNOWN, + ENCODING_7BIT, + ENCODING_8BIT, + ENCODING_16BIT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EncodingSize {} + /** User data text encoding code unit size */ public static final int ENCODING_UNKNOWN = 0; public static final int ENCODING_7BIT = 1; @@ -315,6 +332,34 @@ public class SmsMessage { } /** + * Create an SmsMessage from a native SMS-Submit PDU, specified by Bluetooth Message Access + * Profile Specification v1.4.2 5.8. + * This is used by Bluetooth MAP profile to decode message when sending non UTF-8 SMS messages. + * + * @param data Message data. + * @param isCdma Indicates weather the type of the SMS is CDMA. + * @return An SmsMessage representing the message. + * + * @hide + */ + @SystemApi + @Nullable + public static SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[] data, boolean isCdma) { + SmsMessageBase wrappedMessage; + + if (isCdma) { + wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord( + 0, data); + } else { + // Bluetooth uses its own method to decode GSM PDU so this part is not called. + wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord( + 0, data); + } + + return wrappedMessage != null ? new SmsMessage(wrappedMessage) : null; + } + + /** * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the * length in bytes (not hex chars) less the SMSC header * @@ -633,6 +678,83 @@ public class SmsMessage { } /** + * Get an SMS-SUBMIT PDU's encoded message. + * This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages. + * + * @param isTypeGsm true when message's type is GSM, false when type is CDMA + * @param destinationAddress the address of the destination for the message + * @param message message content + * @param encoding User data text encoding code unit size + * @param languageTable GSM national language table to use, specified by 3GPP + * 23.040 9.2.3.24.16 + * @param languageShiftTable GSM national language shift table to use, specified by 3GPP + * 23.040 9.2.3.24.15 + * @param refNumber parameter to create SmsHeader + * @param seqNumber parameter to create SmsHeader + * @param msgCount parameter to create SmsHeader + * @return a byte[] containing the encoded message + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @SystemApi + @NonNull + public static byte[] getSubmitPduEncodedMessage(boolean isTypeGsm, + @NonNull String destinationAddress, + @NonNull String message, + @EncodingSize int encoding, int languageTable, + int languageShiftTable, int refNumber, + int seqNumber, int msgCount) { + byte[] data; + SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); + concatRef.refNumber = refNumber; + concatRef.seqNumber = seqNumber; // 1-based sequence + concatRef.msgCount = msgCount; + // We currently set this to true since our messaging app will never + // send more than 255 parts (it converts the message to MMS well before that). + // However, we should support 3rd party messaging apps that might need 16-bit + // references + // Note: It's not sufficient to just flip this bit to true; it will have + // ripple effects (several calculations assume 8-bit ref). + concatRef.isEightBits = true; + SmsHeader smsHeader = new SmsHeader(); + smsHeader.concatRef = concatRef; + + /* Depending on the type, call either GSM or CDMA getSubmitPdu(). The encoding + * will be determined(again) by getSubmitPdu(). + * All packets need to be encoded using the same encoding, as the bMessage + * only have one filed to describe the encoding for all messages in a concatenated + * SMS... */ + if (encoding == ENCODING_7BIT) { + smsHeader.languageTable = languageTable; + smsHeader.languageShiftTable = languageShiftTable; + } + + if (isTypeGsm) { + data = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(null, + destinationAddress, message, false, + SmsHeader.toByteArray(smsHeader), encoding, languageTable, + languageShiftTable).encodedMessage; + } else { // SMS_TYPE_CDMA + UserData uData = new UserData(); + uData.payloadStr = message; + uData.userDataHeader = smsHeader; + if (encoding == ENCODING_7BIT) { + uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; + } else { // assume UTF-16 + uData.msgEncoding = UserData.ENCODING_UNICODE_16; + } + uData.msgEncodingSet = true; + data = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( + destinationAddress, uData, false).encodedMessage; + } + if (data == null) { + return new byte[0]; + } + return data; + } + + /** * Returns the address of the SMS service center that relayed this message * or null if there is none. */ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 262cf1a92c89..5ef199e03e04 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -156,7 +156,7 @@ public class TelephonyManager { */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) - private static final long CALLBACK_ON_MORE_ERROR_CODE_CHANGE = 130595455L; + public static final long CALLBACK_ON_MORE_ERROR_CODE_CHANGE = 130595455L; /** * The key to use when placing the result of {@link #requestModemActivityInfo(ResultReceiver)} @@ -10272,6 +10272,7 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. * * @param enabled control enable or disable carrier data. + * @see #resetAllCarrierActions() * @hide */ @SystemApi @@ -10298,6 +10299,7 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. * * @param enabled control enable or disable radio. + * @see #resetAllCarrierActions() * @hide */ @SystemApi @@ -10324,6 +10326,7 @@ public class TelephonyManager { * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. * * @param report control start/stop reporting network status. + * @see #resetAllCarrierActions() * @hide */ @SystemApi @@ -11750,6 +11753,32 @@ public class TelephonyManager { } /** + * Get the calling application status about carrier privileges for the subscription created + * in TelephonyManager. Used by Telephony Module for permission checking. + * + * @param uid Uid to check. + * @return any value of {@link #CARRIER_PRIVILEGE_STATUS_HAS_ACCESS}, + * {@link #CARRIER_PRIVILEGE_STATUS_NO_ACCESS}, + * {@link #CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED}, or + * {@link #CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES} + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public int getCarrierPrivilegeStatus(int uid) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getCarrierPrivilegeStatusForUid(getSubId(), uid); + } + } catch (RemoteException ex) { + Log.e(TAG, "getCarrierPrivilegeStatus RemoteException", ex); + } + return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; + } + + /** * Returns a list of APNs set as overrides by the device policy manager via * {@link #addDevicePolicyOverrideApn}. * This method must only be called from the system or phone processes. diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 1b1a135952fb..35a2a911ac2b 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -591,19 +591,25 @@ public class ProvisioningManager { /** * Notify the framework that an RCS autoconfiguration XML file has been received for * provisioning. + * <p> + * Requires Permission: Manifest.permission.MODIFY_PHONE_STATE or that the calling app has + * carrier privileges (see {@link #hasCarrierPrivileges}). * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed. * @param isCompressed The XML file is compressed in gzip format and must be decompressed * before being read. - * @hide + * */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) { if (config == null) { throw new IllegalArgumentException("Must include a non-null config XML file."); } - // TODO: Connect to ImsConfigImplBase. - throw new UnsupportedOperationException("notifyRcsAutoConfigurationReceived is not" - + "supported"); + try { + getITelephony().notifyRcsAutoConfigurationReceived(mSubId, config, isCompressed); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } private static boolean isImsAvailableOnDevice() { diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl index 53e459697958..57206c9f059a 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl @@ -40,4 +40,5 @@ interface IImsConfig { // Return result code defined in ImsConfig#OperationStatusConstants int setConfigString(int item, String value); void updateImsCarrierConfigs(in PersistableBundle bundle); + void notifyRcsAutoConfigurationReceived(in byte[] config, boolean isCompressed); } diff --git a/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl b/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl index 881b4776b25b..70cf651d3924 100644 --- a/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl +++ b/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl @@ -32,11 +32,11 @@ interface IRcsFeatureListener { oneway void onNetworkResponse(int code, in String reason, int operationToken); oneway void onCapabilityRequestResponsePresence(in List<RcsContactUceCapability> infos, int operationToken); - oneway void onNotifyUpdateCapabilities(); + oneway void onNotifyUpdateCapabilities(int publishTriggerType); oneway void onUnpublish(); // RcsSipOptionsImplBase specific oneway void onCapabilityRequestResponseOptions(int code, in String reason, in RcsContactUceCapability info, int operationToken); oneway void onRemoteCapabilityRequest(in Uri contactUri, in RcsContactUceCapability remoteInfo, int operationToken); -}
\ No newline at end of file +} diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index 1516c65e4b4d..e0d576db4f14 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -17,6 +17,7 @@ package android.telephony.ims.stub; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.Context; @@ -200,6 +201,12 @@ public class ImsConfigImplBase { } } + @Override + public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) + throws RemoteException { + getImsConfigImpl().notifyRcsAutoConfigurationReceived(config, isCompressed); + } + private void notifyImsConfigChanged(int item, int value) throws RemoteException { getImsConfigImpl().notifyConfigChanged(item, value); } @@ -357,9 +364,9 @@ public class ImsConfigImplBase { * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format. * @param isCompressed The XML file is compressed in gzip format and must be decompressed * before being read. - * @hide + * */ - public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) { + public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) { } /** diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java index 055fca57a628..bb034489a296 100644 --- a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java @@ -113,6 +113,51 @@ public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange { }) public @interface PresenceResponseCode {} + + /** A capability update has been requested due to the Entity Tag (ETag) expiring. */ + public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; + /** A capability update has been requested due to moving to LTE with VoPS disabled. */ + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; + /** A capability update has been requested due to moving to LTE with VoPS enabled. */ + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; + /** A capability update has been requested due to moving to eHRPD. */ + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; + /** A capability update has been requested due to moving to HSPA+. */ + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; + /** A capability update has been requested due to moving to 3G. */ + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; + /** A capability update has been requested due to moving to 2G. */ + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; + /** A capability update has been requested due to moving to WLAN */ + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; + /** A capability update has been requested due to moving to IWLAN */ + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; + /** A capability update has been requested but the reason is unknown. */ + public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; + /** A capability update has been requested due to moving to 5G NR with VoPS disabled. */ + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; + /** A capability update has been requested due to moving to 5G NR with VoPS enabled. */ + public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; + + /** @hide*/ + @IntDef(value = { + CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN, + CAPABILITY_UPDATE_TRIGGER_UNKNOWN, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED, + CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED + }, prefix = "CAPABILITY_UPDATE_TRIGGER_") + @Retention(RetentionPolicy.SOURCE) + public @interface StackPublishTriggerType { + } + /** * Provide the framework with a subsequent network response update to * {@link #updateCapabilities(RcsContactUceCapability, int)} and @@ -164,15 +209,18 @@ public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange { * This is typically used when trying to generate an initial PUBLISH for a new subscription to * the network. The device will cache all presence publications after boot until this method is * called once. + * @param publishTriggerType {@link StackPublishTriggerType} The reason for the capability + * update request. * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently * connected to the framework. This can happen if the {@link RcsFeature} is not * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the * Telephony stack has crashed. */ - public final void onNotifyUpdateCapabilites() throws ImsException { + public final void onNotifyUpdateCapabilites(@StackPublishTriggerType int publishTriggerType) + throws ImsException { try { - getListener().onNotifyUpdateCapabilities(); + getListener().onNotifyUpdateCapabilities(publishTriggerType); } catch (RemoteException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index a85c85c0d5e4..1f84451a35e4 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2132,4 +2132,9 @@ interface ITelephony { * Command line command to enable or disable handling of CEP data for test purposes. */ oneway void setCepEnabled(boolean isCepEnabled); + + /** + * Notify Rcs auto config received. + */ + void notifyRcsAutoConfigurationReceived(int subId, in byte[] config, boolean isCompressed); } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java index bdd1fab34a3c..515687fdfdf0 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java @@ -225,7 +225,7 @@ public class GsmSmsCbMessage { private static Pair<Integer, List<Geometry>> parseWarningAreaCoordinates( byte[] pdu, int wacOffset) { // little-endian - int wacDataLength = (pdu[wacOffset + 1] << 8) | pdu[wacOffset]; + int wacDataLength = ((pdu[wacOffset + 1] & 0xff) << 8) | (pdu[wacOffset] & 0xff); int offset = wacOffset + 2; if (offset + wacDataLength > pdu.length) { diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index c42201fa0d3e..be22c9e4bfe6 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -269,7 +269,8 @@ public class PackageWatchdogTest { // Then fail APP_A below the threshold for (int i = 0; i < watchdog.getTriggerFailureCount() - 1; i++) { - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); } // Run handler so package failures are dispatched to observers @@ -296,7 +297,8 @@ public class PackageWatchdogTest { // Then fail APP_C (not observed) above the threshold for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); } // Run handler so package failures are dispatched to observers @@ -331,7 +333,8 @@ public class PackageWatchdogTest { // Then fail APP_A (different version) above the threshold for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { watchdog.onPackageFailure(Arrays.asList( - new VersionedPackage(APP_A, differentVersionCode))); + new VersionedPackage(APP_A, differentVersionCode)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); } // Run handler so package failures are dispatched to observers @@ -372,7 +375,8 @@ public class PackageWatchdogTest { watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE), new VersionedPackage(APP_B, VERSION_CODE), new VersionedPackage(APP_C, VERSION_CODE), - new VersionedPackage(APP_D, VERSION_CODE))); + new VersionedPackage(APP_D, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); } // Run handler so package failures are dispatched to observers @@ -422,7 +426,8 @@ public class PackageWatchdogTest { // Then fail APP_A above the threshold for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); } // Run handler so package failures are dispatched to observers mTestLooper.dispatchAll(); @@ -439,7 +444,8 @@ public class PackageWatchdogTest { // Then fail APP_A again above the threshold for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); } // Run handler so package failures are dispatched to observers mTestLooper.dispatchAll(); @@ -456,7 +462,8 @@ public class PackageWatchdogTest { // Then fail APP_A again above the threshold for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); } // Run handler so package failures are dispatched to observers mTestLooper.dispatchAll(); @@ -473,7 +480,8 @@ public class PackageWatchdogTest { // Then fail APP_A again above the threshold for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); } // Run handler so package failures are dispatched to observers mTestLooper.dispatchAll(); @@ -500,7 +508,8 @@ public class PackageWatchdogTest { // Then fail APP_A above the threshold for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { - watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE))); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_UNKNOWN); } // Run handler so package failures are dispatched to observers @@ -746,6 +755,44 @@ public class PackageWatchdogTest { assertEquals(APP_A, observer.mFailedPackages.get(0)); } + /** Test that observers execute correctly for different failure reasons */ + @Test + public void testFailureReasons() { + PackageWatchdog watchdog = createWatchdog(); + TestObserver observer1 = new TestObserver(OBSERVER_NAME_1); + TestObserver observer2 = new TestObserver(OBSERVER_NAME_2); + TestObserver observer3 = new TestObserver(OBSERVER_NAME_3); + TestObserver observer4 = new TestObserver(OBSERVER_NAME_4); + + watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION); + watchdog.startObservingHealth(observer2, Arrays.asList(APP_B), SHORT_DURATION); + watchdog.startObservingHealth(observer3, Arrays.asList(APP_C), SHORT_DURATION); + watchdog.startObservingHealth(observer4, Arrays.asList(APP_D), SHORT_DURATION); + + for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) { + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_B, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_APP_CRASH); + watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_D, VERSION_CODE)), + PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING); + } + + // Run handler so requests are dispatched to the controller + mTestLooper.dispatchAll(); + + assertTrue(observer1.getLastFailureReason() + == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); + assertTrue(observer2.getLastFailureReason() + == PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK); + assertTrue(observer3.getLastFailureReason() + == PackageWatchdog.FAILURE_REASON_APP_CRASH); + assertTrue(observer4.getLastFailureReason() + == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING); + } + private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() @@ -801,6 +848,7 @@ public class PackageWatchdogTest { private static class TestObserver implements PackageHealthObserver { private final String mName; private int mImpact; + private int mLastFailureReason; final List<String> mFailedPackages = new ArrayList<>(); TestObserver(String name) { @@ -817,14 +865,19 @@ public class PackageWatchdogTest { return mImpact; } - public boolean execute(VersionedPackage versionedPackage) { + public boolean execute(VersionedPackage versionedPackage, int failureReason) { mFailedPackages.add(versionedPackage.getPackageName()); + mLastFailureReason = failureReason; return true; } public String getName() { return mName; } + + public int getLastFailureReason() { + return mLastFailureReason; + } } private static class TestController extends ExplicitHealthCheckController { diff --git a/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java b/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java new file mode 100644 index 000000000000..47afed441ace --- /dev/null +++ b/tests/net/java/android/net/TelephonyNetworkSpecifierTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.net; + +import static com.android.testutils.ParcelUtilsKt.assertParcelSane; + +import static org.junit.Assert.assertEquals; + +import android.telephony.SubscriptionManager; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Unit test for {@link android.net.TelephonyNetworkSpecifier}. + */ +@SmallTest +public class TelephonyNetworkSpecifierTest { + private static final int TEST_SUBID = 5; + + /** + * Validate that IllegalArgumentException will be thrown if build TelephonyNetworkSpecifier + * without calling {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}. + */ + @Test + public void testBuilderBuildWithDefault() { + try { + new TelephonyNetworkSpecifier.Builder().build(); + } catch (IllegalArgumentException iae) { + // expected, test pass + } + } + + /** + * Validate that no exception will be thrown even if pass invalid subscription id to + * {@link TelephonyNetworkSpecifier.Builder#setSubscriptionId(int)}. + */ + @Test + public void testBuilderBuildWithInvalidSubId() { + TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(SubscriptionManager.INVALID_SUBSCRIPTION_ID) + .build(); + assertEquals(specifier.getSubscriptionId(), SubscriptionManager.INVALID_SUBSCRIPTION_ID); + } + + /** + * Validate the correctness of TelephonyNetworkSpecifier when provide valid subId. + */ + @Test + public void testBuilderBuildWithValidSubId() { + final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(TEST_SUBID) + .build(); + assertEquals(TEST_SUBID, specifier.getSubscriptionId()); + } + + /** + * Validate that parcel marshalling/unmarshalling works. + */ + @Test + public void testParcel() { + TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(TEST_SUBID) + .build(); + assertParcelSane(specifier, 1 /* fieldCount */); + } +} diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java index b783467cfaf2..de1028cd2303 100644 --- a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java +++ b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java @@ -51,6 +51,7 @@ import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.net.NetworkTemplate; import android.net.StringNetworkSpecifier; +import android.net.TelephonyNetworkSpecifier; import android.os.Handler; import android.provider.Settings; import android.telephony.TelephonyManager; @@ -229,7 +230,7 @@ public class MultipathPolicyTrackerTest { verify(mCM).registerNetworkCallback(any(), networkCallback.capture(), any()); // Simulate callback after capability changes - final NetworkCapabilities capabilities = new NetworkCapabilities() + NetworkCapabilities capabilities = new NetworkCapabilities() .addCapability(NET_CAPABILITY_INTERNET) .addTransportType(TRANSPORT_CELLULAR) .setNetworkSpecifier(new StringNetworkSpecifier("234")); @@ -239,6 +240,19 @@ public class MultipathPolicyTrackerTest { networkCallback.getValue().onCapabilitiesChanged( TEST_NETWORK, capabilities); + + // make sure it also works with the new introduced TelephonyNetworkSpecifier + capabilities = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .addTransportType(TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder() + .setSubscriptionId(234).build()); + if (!roaming) { + capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING); + } + networkCallback.getValue().onCapabilitiesChanged( + TEST_NETWORK, + capabilities); } @Test diff --git a/tests/testables/src/android/testing/TestableSettingsProvider.java b/tests/testables/src/android/testing/TestableSettingsProvider.java index b158476bd438..fd92c657cb2e 100644 --- a/tests/testables/src/android/testing/TestableSettingsProvider.java +++ b/tests/testables/src/android/testing/TestableSettingsProvider.java @@ -14,6 +14,8 @@ package android.testing; +import static org.junit.Assert.assertEquals; + import android.content.ContentProviderClient; import android.content.Context; import android.os.Bundle; @@ -25,8 +27,6 @@ import android.util.Log; import java.util.HashMap; -import static org.junit.Assert.*; - /** * Allows calls to android.provider.Settings to be tested easier. * @@ -71,7 +71,7 @@ public class TestableSettingsProvider extends MockContentProvider { public Bundle call(String method, String arg, Bundle extras) { // Methods are "GET_system", "GET_global", "PUT_secure", etc. - final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, 0); + final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, UserHandle.myUserId()); final String[] commands = method.split("_", 2); final String op = commands[0]; final String table = commands[1]; diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index f85bb5f8ad59..4c6b81c9a06b 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1776,12 +1776,13 @@ public class WifiManager { } /** - * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name). + * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name) added + * by the caller. * - * @param fqdn The FQDN of the Passpoint configuration to be removed + * @param fqdn The FQDN of the Passpoint configuration added by the caller to be removed * @throws IllegalArgumentException if no configuration is associated with the given FQDN or * Passpoint is not enabled on the device. - * @deprecated This is no longer supported. + * @deprecated This will be non-functional in a future release. */ @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) @@ -1796,12 +1797,12 @@ public class WifiManager { } /** - * Return the list of installed Passpoint configurations. + * Return the list of installed Passpoint configurations added by the caller. * * An empty list will be returned when no configurations are installed. * - * @return A list of {@link PasspointConfiguration} - * @deprecated This is no longer supported. + * @return A list of {@link PasspointConfiguration} added by the caller + * @deprecated This will be non-functional in a future release. */ @Deprecated @RequiresPermission(anyOf = { @@ -2068,69 +2069,69 @@ public class WifiManager { } /** @hide */ - public static final int WIFI_FEATURE_INFRA = 0x0001; // Basic infrastructure mode + public static final long WIFI_FEATURE_INFRA = 0x0001L; // Basic infrastructure mode /** @hide */ - public static final int WIFI_FEATURE_INFRA_5G = 0x0002; // Support for 5 GHz Band + public static final long WIFI_FEATURE_INFRA_5G = 0x0002L; // Support for 5 GHz Band /** @hide */ - public static final int WIFI_FEATURE_PASSPOINT = 0x0004; // Support for GAS/ANQP + public static final long WIFI_FEATURE_PASSPOINT = 0x0004L; // Support for GAS/ANQP /** @hide */ - public static final int WIFI_FEATURE_P2P = 0x0008; // Wifi-Direct + public static final long WIFI_FEATURE_P2P = 0x0008L; // Wifi-Direct /** @hide */ - public static final int WIFI_FEATURE_MOBILE_HOTSPOT = 0x0010; // Soft AP + public static final long WIFI_FEATURE_MOBILE_HOTSPOT = 0x0010L; // Soft AP /** @hide */ - public static final int WIFI_FEATURE_SCANNER = 0x0020; // WifiScanner APIs + public static final long WIFI_FEATURE_SCANNER = 0x0020L; // WifiScanner APIs /** @hide */ - public static final int WIFI_FEATURE_AWARE = 0x0040; // Wi-Fi AWare networking + public static final long WIFI_FEATURE_AWARE = 0x0040L; // Wi-Fi AWare networking /** @hide */ - public static final int WIFI_FEATURE_D2D_RTT = 0x0080; // Device-to-device RTT + public static final long WIFI_FEATURE_D2D_RTT = 0x0080L; // Device-to-device RTT /** @hide */ - public static final int WIFI_FEATURE_D2AP_RTT = 0x0100; // Device-to-AP RTT + public static final long WIFI_FEATURE_D2AP_RTT = 0x0100L; // Device-to-AP RTT /** @hide */ - public static final int WIFI_FEATURE_BATCH_SCAN = 0x0200; // Batched Scan (deprecated) + public static final long WIFI_FEATURE_BATCH_SCAN = 0x0200L; // Batched Scan (deprecated) /** @hide */ - public static final int WIFI_FEATURE_PNO = 0x0400; // Preferred network offload + public static final long WIFI_FEATURE_PNO = 0x0400L; // Preferred network offload /** @hide */ - public static final int WIFI_FEATURE_ADDITIONAL_STA = 0x0800; // Support for two STAs + public static final long WIFI_FEATURE_ADDITIONAL_STA = 0x0800L; // Support for two STAs /** @hide */ - public static final int WIFI_FEATURE_TDLS = 0x1000; // Tunnel directed link setup + public static final long WIFI_FEATURE_TDLS = 0x1000L; // Tunnel directed link setup /** @hide */ - public static final int WIFI_FEATURE_TDLS_OFFCHANNEL = 0x2000; // Support for TDLS off channel + public static final long WIFI_FEATURE_TDLS_OFFCHANNEL = 0x2000L; // TDLS off channel /** @hide */ - public static final int WIFI_FEATURE_EPR = 0x4000; // Enhanced power reporting + public static final long WIFI_FEATURE_EPR = 0x4000L; // Enhanced power reporting /** @hide */ - public static final int WIFI_FEATURE_AP_STA = 0x8000; // AP STA Concurrency + public static final long WIFI_FEATURE_AP_STA = 0x8000L; // AP STA Concurrency /** @hide */ - public static final int WIFI_FEATURE_LINK_LAYER_STATS = 0x10000; // Link layer stats collection + public static final long WIFI_FEATURE_LINK_LAYER_STATS = 0x10000L; // Link layer stats /** @hide */ - public static final int WIFI_FEATURE_LOGGER = 0x20000; // WiFi Logger + public static final long WIFI_FEATURE_LOGGER = 0x20000L; // WiFi Logger /** @hide */ - public static final int WIFI_FEATURE_HAL_EPNO = 0x40000; // Enhanced PNO + public static final long WIFI_FEATURE_HAL_EPNO = 0x40000L; // Enhanced PNO /** @hide */ - public static final int WIFI_FEATURE_RSSI_MONITOR = 0x80000; // RSSI Monitor + public static final long WIFI_FEATURE_RSSI_MONITOR = 0x80000L; // RSSI Monitor /** @hide */ - public static final int WIFI_FEATURE_MKEEP_ALIVE = 0x100000; // mkeep_alive + public static final long WIFI_FEATURE_MKEEP_ALIVE = 0x100000L; // mkeep_alive /** @hide */ - public static final int WIFI_FEATURE_CONFIG_NDO = 0x200000; // ND offload + public static final long WIFI_FEATURE_CONFIG_NDO = 0x200000L; // ND offload /** @hide */ - public static final int WIFI_FEATURE_TRANSMIT_POWER = 0x400000; // Capture transmit power + public static final long WIFI_FEATURE_TRANSMIT_POWER = 0x400000L; // Capture transmit power /** @hide */ - public static final int WIFI_FEATURE_CONTROL_ROAMING = 0x800000; // Control firmware roaming + public static final long WIFI_FEATURE_CONTROL_ROAMING = 0x800000L; // Control firmware roaming /** @hide */ - public static final int WIFI_FEATURE_IE_WHITELIST = 0x1000000; // Probe IE white listing + public static final long WIFI_FEATURE_IE_WHITELIST = 0x1000000L; // Probe IE white listing /** @hide */ - public static final int WIFI_FEATURE_SCAN_RAND = 0x2000000; // Random MAC & Probe seq + public static final long WIFI_FEATURE_SCAN_RAND = 0x2000000L; // Random MAC & Probe seq /** @hide */ - public static final int WIFI_FEATURE_TX_POWER_LIMIT = 0x4000000; // Set Tx power limit + public static final long WIFI_FEATURE_TX_POWER_LIMIT = 0x4000000L; // Set Tx power limit /** @hide */ - public static final int WIFI_FEATURE_WPA3_SAE = 0x8000000; // WPA3-Personal SAE + public static final long WIFI_FEATURE_WPA3_SAE = 0x8000000L; // WPA3-Personal SAE /** @hide */ - public static final int WIFI_FEATURE_WPA3_SUITE_B = 0x10000000; // WPA3-Enterprise Suite-B + public static final long WIFI_FEATURE_WPA3_SUITE_B = 0x10000000L; // WPA3-Enterprise Suite-B /** @hide */ - public static final int WIFI_FEATURE_OWE = 0x20000000; // Enhanced Open + public static final long WIFI_FEATURE_OWE = 0x20000000L; // Enhanced Open /** @hide */ - public static final int WIFI_FEATURE_LOW_LATENCY = 0x40000000; // Low Latency modes + public static final long WIFI_FEATURE_LOW_LATENCY = 0x40000000L; // Low Latency modes /** @hide */ - public static final int WIFI_FEATURE_DPP = 0x80000000; // DPP (Easy-Connect) + public static final long WIFI_FEATURE_DPP = 0x80000000L; // DPP (Easy-Connect) /** @hide */ public static final long WIFI_FEATURE_P2P_RAND_MAC = 0x100000000L; // Random P2P MAC diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java index fd26817bfd79..e9fb37ea0c29 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkInfo.java @@ -120,10 +120,12 @@ public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable { new Creator<WifiAwareNetworkInfo>() { @Override public WifiAwareNetworkInfo createFromParcel(Parcel in) { + byte[] addr = in.createByteArray(); + String interfaceName = in.readString(); + int port = in.readInt(); + int transportProtocol = in.readInt(); Inet6Address ipv6Addr; try { - byte[] addr = in.createByteArray(); - String interfaceName = in.readString(); NetworkInterface ni = null; if (interfaceName != null) { try { @@ -137,9 +139,6 @@ public final class WifiAwareNetworkInfo implements TransportInfo, Parcelable { e.printStackTrace(); return null; } - int port = in.readInt(); - int transportProtocol = in.readInt(); - return new WifiAwareNetworkInfo(ipv6Addr, port, transportProtocol); } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java index b0ea7202b7ba..a59799b3fc54 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java @@ -28,6 +28,7 @@ import android.text.TextUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.nio.charset.StandardCharsets; import java.util.regex.PatternSyntaxException; /** @@ -228,6 +229,10 @@ public class WifiP2pConfig implements Parcelable { private static final MacAddress MAC_ANY_ADDRESS = MacAddress.fromString("02:00:00:00:00:00"); + /** + * Maximum number of bytes allowed for a SSID. + */ + private static final int MAX_SSID_BYTES = 32; private MacAddress mDeviceAddress = MAC_ANY_ADDRESS; private String mNetworkName = ""; @@ -279,6 +284,10 @@ public class WifiP2pConfig implements Parcelable { throw new IllegalArgumentException( "network name must be non-empty."); } + if (networkName.getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) { + throw new IllegalArgumentException( + "network name exceeds " + MAX_SSID_BYTES + " bytes."); + } try { if (!networkName.matches("^DIRECT-[a-zA-Z0-9]{2}.*")) { throw new IllegalArgumentException( diff --git a/wifi/java/android/net/wifi/rtt/package.html b/wifi/java/android/net/wifi/rtt/package.html index e6392821ff54..4a32f5206fde 100644 --- a/wifi/java/android/net/wifi/rtt/package.html +++ b/wifi/java/android/net/wifi/rtt/package.html @@ -37,5 +37,9 @@ location to be queried.</p> <pre> getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT) </pre> + +<p>For an example of this functionality, see +<a href="{@docRoot}guide/topics/connectivity/wifi-rtt" class="external">Wi-Fi location: ranging +with RTT</a>.</p> </BODY> </HTML> diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java index 41f109a63759..61993252c05e 100644 --- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java +++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java @@ -53,6 +53,13 @@ public class WifiP2pConfigTest { fail("Unexpected IllegalArgumentException"); } + // sunny case with maximum bytes for the network name + try { + b.setNetworkName("DIRECT-abcdefghijklmnopqrstuvwxy"); + } catch (IllegalArgumentException e) { + fail("Unexpected IllegalArgumentException"); + } + // less than 9 characters. try { b.setNetworkName("DIRECT-z"); @@ -77,6 +84,12 @@ public class WifiP2pConfigTest { b.setNetworkName("direct-a?"); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException e) { } + + // over maximum bytes + try { + b.setNetworkName("DIRECT-abcdefghijklmnopqrstuvwxyz"); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { } } /** |