diff options
38 files changed, 1152 insertions, 391 deletions
diff --git a/apex/media/framework/java/android/media/Session2Command.java b/apex/media/framework/java/android/media/Session2Command.java index 26f4568fa7e5..7e71591d05fb 100644 --- a/apex/media/framework/java/android/media/Session2Command.java +++ b/apex/media/framework/java/android/media/Session2Command.java @@ -37,9 +37,8 @@ import java.util.Objects; * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and * {@link #getCustomAction()} shouldn't be {@code null}. * <p> - * Refer to the - * <a href="{@docRoot}reference/androidx/media2/SessionCommand2.html">AndroidX SessionCommand</a> - * class for the list of valid commands. + * Refer to the <a href="{@docRoot}reference/androidx/media2/session/SessionCommand.html"> + * AndroidX SessionCommand</a> class for the list of valid commands. */ public final class Session2Command implements Parcelable { /** diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 0a73e6c78bb0..f6bcfd6f6dd3 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5430,6 +5430,12 @@ public final class ActivityThread extends ClientTransactionHandler // behave properly when activity is relaunching. r.window.clearContentView(); } else { + final ViewRootImpl viewRoot = v.getViewRootImpl(); + if (viewRoot != null) { + // Clear the callback to avoid the destroyed activity from receiving + // configuration changes that are no longer effective. + viewRoot.setActivityConfigCallback(null); + } wm.removeViewImmediate(v); } } @@ -5834,10 +5840,9 @@ public final class ActivityThread extends ClientTransactionHandler final boolean movedToDifferentDisplay = isDifferentDisplay(activity.getDisplayId(), displayId); - final SizeConfigurationBuckets buckets = getActivityClient(activityToken) - .mSizeConfigurations; + final ActivityClientRecord r = mActivities.get(activityToken); final int diff = diffPublicWithSizeBuckets(activity.mCurrentConfig, - newConfig, buckets); + newConfig, r != null ? r.mSizeConfigurations : null); final boolean hasPublicConfigChange = diff != 0; // TODO(b/173090263): Use diff instead after the improvement of AssetManager and // ResourcesImpl constructions. diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java index 19183b8eb9e7..c9a9e51dceed 100644 --- a/core/java/com/android/internal/display/BrightnessSynchronizer.java +++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java @@ -154,10 +154,20 @@ public class BrightnessSynchronizer { } } + /** + * Gets the stored screen brightness float value from the display brightness setting. + * @return brightness + */ private float getScreenBrightnessFloat() { return mDisplayManager.getBrightness(Display.DEFAULT_DISPLAY); } + /** + * Gets the stored screen brightness int from the system settings. + * @param context for accessing settings + * + * @return brightness + */ private static int getScreenBrightnessInt(Context context) { return Settings.System.getIntForUser(context.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, PowerManager.BRIGHTNESS_INVALID, diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index c16d35e19236..06d18168a7fa 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -648,7 +648,7 @@ <string name="face_error_canceled" msgid="2164434737103802131">"Gesichtserkennung abgebrochen."</string> <string name="face_error_user_canceled" msgid="5766472033202928373">"Entsperrung per Gesichtserkennung vom Nutzer abgebrochen"</string> <string name="face_error_lockout" msgid="7864408714994529437">"Zu viele Versuche, bitte später noch einmal versuchen"</string> - <string name="face_error_lockout_permanent" msgid="3277134834042995260">"Zu viele Versuche. Entsperrung per Gesichtserkennung wurde deaktiviert."</string> + <string name="face_error_lockout_permanent" msgid="3277134834042995260">"Zu viele Versuche. Die Entsperrung per Gesichtserkennung wurde deaktiviert."</string> <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"Zu viele Versuche. Verwende stattdessen die Displaysperre."</string> <string name="face_error_unable_to_process" msgid="5723292697366130070">"Gesichtsprüfung nicht möglich. Noch mal versuchen."</string> <string name="face_error_not_enrolled" msgid="1134739108536328412">"Entsperrung per Gesichtserkennung ist nicht eingerichtet"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index c17044aeb416..1605ff05d8bd 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -617,7 +617,7 @@ <string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string> <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloqueo"</string> <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca para añadir una huella digital"</string> - <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo con Huella Digital"</string> + <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo con huella digital"</string> <string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"No se puede usar el sensor de huellas digitales"</string> <string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visita un proveedor de reparaciones."</string> <string name="face_acquired_insufficient" msgid="2150805835949162453">"Datos faciales no reconocidos. Vuelve a intentarlo."</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 84223aebfaa7..9812b67784af 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -1483,8 +1483,7 @@ <string name="deny" msgid="6632259981847676572">"Refuser"</string> <string name="permission_request_notification_title" msgid="1810025922441048273">"Autorisation demandée"</string> <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Autorisation demandée\npour le compte \"<xliff:g id="ACCOUNT">%s</xliff:g>\""</string> - <!-- no translation found for permission_request_notification_for_app_with_subtitle (1298704005732851350) --> - <skip /> + <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"Autorisation demandée par <xliff:g id="APP">%1$s</xliff:g>\npour le compte <xliff:g id="ACCOUNT">%2$s</xliff:g>."</string> <string name="forward_intent_to_owner" msgid="4620359037192871015">"Vous utilisez cette application en dehors de votre profil professionnel"</string> <string name="forward_intent_to_work" msgid="3620262405636021151">"Vous utilisez cette application dans votre profil professionnel"</string> <string name="input_method_binding_label" msgid="1166731601721983656">"Mode de saisie"</string> @@ -2101,7 +2100,7 @@ <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string> <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Désactiver"</string> <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"En savoir plus"</string> - <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Les notifications améliorées ont remplacé les notifications adaptatives Android sous Android 12. Cette fonctionnalité vous présente des suggestions d\'actions et de réponse, et organise vos notifications.\n\nLes notifications améliorées peuvent accéder au contenu de toutes les notifications, y compris les renseignements personnels comme le nom des contacts et les messages. Cette fonctionnalité peut aussi fermer des notifications ou interagir avec elles, comme répondre aux appels téléphoniques et gérer le mode Ne pas déranger."</string> + <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Les notifications améliorées ont remplacé les notifications adaptatives Android sous Android 12. Cette fonctionnalité vous présente des suggestions d\'actions et de réponses, et organise vos notifications.\n\nLes notifications améliorées peuvent accéder au contenu de toutes les notifications, y compris les renseignements personnels comme le nom des contacts et les messages. Cette fonctionnalité peut aussi fermer des notifications ou interagir avec elles, comme répondre aux appels téléphoniques et gérer le mode Ne pas déranger."</string> <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notification d\'information du mode Routine"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"La pile pourrait s\'épuiser avant la charge habituelle"</string> <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Le mode Économiseur de pile est activé afin de prolonger l\'autonomie"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 5a1a3d562659..3eb23dc21ca1 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1523,8 +1523,7 @@ <string name="deny" msgid="6632259981847676572">"עדיף שלא"</string> <string name="permission_request_notification_title" msgid="1810025922441048273">"בקשת הרשאה"</string> <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"נדרשת הרשאה\nלחשבון <xliff:g id="ACCOUNT">%s</xliff:g>."</string> - <!-- no translation found for permission_request_notification_for_app_with_subtitle (1298704005732851350) --> - <skip /> + <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"התבקשה הרשאה על ידי <xliff:g id="APP">%1$s</xliff:g>\nלחשבון <xliff:g id="ACCOUNT">%2$s</xliff:g>."</string> <string name="forward_intent_to_owner" msgid="4620359037192871015">"בחרת להשתמש באפליקציה הזאת מחוץ לפרופיל העבודה שלך"</string> <string name="forward_intent_to_work" msgid="3620262405636021151">"נעשה שימוש באפליקציה הזו בפרופיל העבודה שלך"</string> <string name="input_method_binding_label" msgid="1166731601721983656">"שיטת קלט"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 151d7fde9e57..716b3586ff04 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -1483,8 +1483,7 @@ <string name="deny" msgid="6632259981847676572">"ನಿರಾಕರಿಸಿ"</string> <string name="permission_request_notification_title" msgid="1810025922441048273">"ಅನುಮತಿ ವಿನಂತಿಸಲಾಗಿದೆ"</string> <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"<xliff:g id="ACCOUNT">%s</xliff:g> ಖಾತೆಗಾಗಿ\n ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಲಾಗಿದೆ."</string> - <!-- no translation found for permission_request_notification_for_app_with_subtitle (1298704005732851350) --> - <skip /> + <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"<xliff:g id="ACCOUNT">%2$s</xliff:g> ಖಾತೆಗಾಗಿ \n <xliff:g id="APP">%1$s</xliff:g> ನಿಂದ ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಲಾಗಿದೆ."</string> <string name="forward_intent_to_owner" msgid="4620359037192871015">"ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್ನ ಹೊರಗೆ ನೀವು ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ"</string> <string name="forward_intent_to_work" msgid="3620262405636021151">"ನಿಮ್ಮ ಕೆಲಸದ ಪ್ರೊಫೈಲ್ನಲ್ಲಿ ನೀವು ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಬಳಸುತ್ತಿರುವಿರಿ"</string> <string name="input_method_binding_label" msgid="1166731601721983656">"ಇನ್ಪುಟ್ ವಿಧಾನ"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index e7ccc4aa2638..5195055d2f5c 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -652,7 +652,7 @@ <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"बरेच प्रयत्न. त्याऐवजी स्क्रीन लॉक वापरा."</string> <string name="face_error_unable_to_process" msgid="5723292697366130070">"चेहरा पडताळणी करू शकत नाही. पुन्हा प्रयत्न करा."</string> <string name="face_error_not_enrolled" msgid="1134739108536328412">"तुम्ही फेस अनलॉक सेट केले नाही"</string> - <string name="face_error_hw_not_present" msgid="7940978724978763011">"फेस अनलॉक या डिव्हाइसवर सपोर्ट करत नाही"</string> + <string name="face_error_hw_not_present" msgid="7940978724978763011">"या डिव्हाइसवर फेस अनलॉकला सपोर्ट नाही"</string> <string name="face_error_security_update_required" msgid="5076017208528750161">"सेन्सर तात्पुरता बंद केला आहे."</string> <string name="face_name_template" msgid="3877037340223318119">"चेहरा <xliff:g id="FACEID">%d</xliff:g>"</string> <string name="face_app_setting_name" msgid="5854024256907828015">"फेस अनलॉक वापरा"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index da53e0a2a488..33b57d1cc9e2 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -914,13 +914,13 @@ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"तपाईंले गलत तरिकाले आफ्नो पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक टाइप गर्नुभयो। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"तपाईँले गलत तरिकाले तपाईँको PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक टाइप गर्नु भएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"तपाईँले तपाईँको अनलक प्याटर्न गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक खिच्नु भएको छ। पछि <xliff:g id="NUMBER_1">%2$d</xliff:g> थप असफल कोसिसहरू, तपाईँको Google साइन इन प्रयोग गरी तपाईँको ट्याब्लेट अनलक गर्न भनिने छ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा फरि प्रयास गर्नुहोस्।"</string> - <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"तपाईंले आफ्नो अनलक शैली <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले कोर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो Google खाता मार्फत साइन इन गरेर आफ्नो Android टिभी डिभाइस अनलक गर्न अनुरोध गरिनेछ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"</string> + <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"तपाईंले आफ्नो अनलक शैली <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले कोर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो Google खाता मार्फत साइन इन गरेर आफ्नो Android टिभी डिभाइस अनलक गर्न अनुरोध गरिने छ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"तपाईँले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले तपाईँको अनलक ढाँचालाई कोर्नु भएको छ। पछि <xliff:g id="NUMBER_1">%2$d</xliff:g> अरू धेरै असफल कोसिसहरूपछि, तपाईँलाई तपाईँको फोन Google साइन इन प्रयोग गरेर अनलक गर्नको लागि सोधिने छ। \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा पुनः प्रयास गर्नुहोस्।"</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"तपाईँले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक ट्याब्लेटलाई अनलक गर्नको लागि गलत तरिकाले कोशिस गर्नुभएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> अरू धेरै असफल कोसिसहरूपछि, ट्याब्लेट फ्याट्रि पूर्वनिर्धारितमा रिसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।"</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर डिफल्ट फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"तपाईंले गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक फोन अनलक गर्ने प्रयत्न गर्नुभयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> बढी असफल प्रयत्नहरू पछि, फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ र सबै प्रयोगकर्ता डेटा हराउने छन्।"</string> <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER">%d</xliff:g> पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई डिफल्ट कार्यशालामा रिसेट गरिने छ।"</string> - <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर डिफल्ट फ्याक्ट्री सेटिङ लागू गरिनेछ।"</string> + <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर डिफल्ट फ्याक्ट्री सेटिङ लागू गरिने छ।"</string> <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"तपाईंले गलत तरिकाले फोन <xliff:g id="NUMBER">%d</xliff:g> पटक अनलक गर्ने प्रयत्न गर्नुभयो। अब फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ।"</string> <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"<xliff:g id="NUMBER">%d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"ढाँचा बिर्सनु भयो?"</string> @@ -1680,10 +1680,10 @@ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर डिफल्ट फ्याक्ट्री सेटिङ लागू गरिने छ र प्रयोगकर्ताको सम्पूर्ण डेटा गुम्ने छ।"</string> <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"तपाईँले गलतसँग फोनलाई अनलक गर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोसिस गर्नु भयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> पछि थप असफल कोसिसहरू, फोनलाई डिफल्ट कार्यशालामा रिसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string> <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"तपाईँले ट्यब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER">%d</xliff:g> पटक प्रयास गर्नु भएको छ। अब ट्याब्लेटलाई डिफल्ट कार्यशालामा रिसेट गरिने छ।"</string> - <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर डिफल्ट फ्याक्ट्री सेटिङ लागू गरिनेछ।"</string> + <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"तपाईंले आफ्नो Android टिभी यन्त्र <xliff:g id="NUMBER">%d</xliff:g> पटक गलत तरिकाले अनलक गर्ने प्रयास गर्नुभएको छ। अब तपाईंको Android टिभी यन्त्रलाई रिसेट गरेर डिफल्ट फ्याक्ट्री सेटिङ लागू गरिने छ।"</string> <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"तपाईंले गलत तरिकाले फोन <xliff:g id="NUMBER">%d</xliff:g> पटक अनलक गर्ने प्रयत्न गर्नुभयो। अब फोन फ्याक्ट्रि पूर्वनिर्धारितमा रिसेट हुने छ।"</string> <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"तपाईंले गलत तरिकाले आफ्नो अनलक प्याटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोर्नुभयो। <xliff:g id="NUMBER_1">%2$d</xliff:g> विफल प्रयत्नहरू पछि, तपाईंलाई आफ्नो ट्याब्लेट इमेल खाता प्रयोग गरेर अनलक गर्न सोधिने छ।\n\n फेरि प्रयास गर्नुहोस् <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डहरूमा।"</string> - <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"तपाईंले आफ्नो अनलक शैली <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले कोर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो इमेल खाता प्रयोग गरेर आफ्नो Android टिभी डिभाइस अनलक गर्न अनुरोध गरिनेछ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"</string> + <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"तपाईंले आफ्नो अनलक शैली <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले कोर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो इमेल खाता प्रयोग गरेर आफ्नो Android टिभी डिभाइस अनलक गर्न अनुरोध गरिने छ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"</string> <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"तपाईँले आफ्नो अनलक प्याटर्न गलत रूपमा <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक तान्नु भएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> धेरै असफल प्रयासहरूपछि, तपाईँलाई एउटा इमेल खाताको प्रयोग गरेर तपाईँको फोन अनलक गर्न सोधिने छ।\n\n फेरि <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा प्रयास गर्नुहोस्।"</string> <string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"हटाउनुहोस्"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 6a6b34cf9fd8..757745743623 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1483,8 +1483,7 @@ <string name="deny" msgid="6632259981847676572">"ਅਸਵੀਕਾਰ ਕਰੋ"</string> <string name="permission_request_notification_title" msgid="1810025922441048273">"ਅਨੁਮਤੀ ਦੀ ਬੇਨਤੀ ਕੀਤੀ"</string> <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"<xliff:g id="ACCOUNT">%s</xliff:g> ਖਾਤੇ ਲਈ ਅਨੁਮਤੀ ਦੀ ਬੇਨਤੀ ਕੀਤੀ\n।"</string> - <!-- no translation found for permission_request_notification_for_app_with_subtitle (1298704005732851350) --> - <skip /> + <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"<xliff:g id="APP">%1$s</xliff:g> ਨੇ <xliff:g id="ACCOUNT">%2$s</xliff:g> ਖਾਤੇ ਤੱਕ ਪਹੁੰਚ ਕਰਨ\nਦੀ ਇਜਾਜ਼ਤ ਲਈ ਬੇਨਤੀ ਕੀਤੀ।"</string> <string name="forward_intent_to_owner" msgid="4620359037192871015">"ਤੁਸੀਂ ਇਹ ਐਪ ਆਪਣੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਦੇ ਬਾਹਰ ਵਰਤ ਰਹੇ ਹੋ"</string> <string name="forward_intent_to_work" msgid="3620262405636021151">"ਤੁਸੀਂ ਇਹ ਐਪ ਆਪਣੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿੱਚ ਵਰਤ ਰਹੇ ਹੋ"</string> <string name="input_method_binding_label" msgid="1166731601721983656">"ਇਨਪੁੱਟ ਵਿਧੀ"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 283fe2e79cab..07a1b6ec0d95 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -1483,8 +1483,7 @@ <string name="deny" msgid="6632259981847676572">"Moho"</string> <string name="permission_request_notification_title" msgid="1810025922441048273">"Kërkohet leje"</string> <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Kërkohet leje\npër llogarinë <xliff:g id="ACCOUNT">%s</xliff:g>."</string> - <!-- no translation found for permission_request_notification_for_app_with_subtitle (1298704005732851350) --> - <skip /> + <string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"Kërkohet leja nga <xliff:g id="APP">%1$s</xliff:g>\npër llogarinë <xliff:g id="ACCOUNT">%2$s</xliff:g>."</string> <string name="forward_intent_to_owner" msgid="4620359037192871015">"Po e përdor këtë aplikacion jashtë profilit tënd të punës"</string> <string name="forward_intent_to_work" msgid="3620262405636021151">"Këtë aplikacion po e përdor në profilin tënd të punës"</string> <string name="input_method_binding_label" msgid="1166731601721983656">"Metoda e hyrjeve"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 3b0a8e65b517..41664782148c 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -610,10 +610,10 @@ <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Aikoni ya alama ya kidole"</string> - <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Kufungua kwa uso"</string> - <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Hitilafu imetokea kwenye kipengele cha Kufungua kwa uso"</string> + <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Kufungua kwa Uso"</string> + <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Hitilafu imetokea kwenye kipengele cha Kufungua kwa Uso"</string> <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Gusa ili ufute muundo wa uso wako, kisha uweke uso wako tena"</string> - <string name="face_setup_notification_title" msgid="8843461561970741790">"Weka mipangilio ya Kufungua kwa uso"</string> + <string name="face_setup_notification_title" msgid="8843461561970741790">"Weka mipangilio ya Kufungua kwa Uso"</string> <string name="face_setup_notification_content" msgid="5463999831057751676">"Fungua simu yako kwa kuiangalia"</string> <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weka mipangilio ya mbinu zaidi za kufungua"</string> <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Gusa ili uweke alama ya kidole"</string> @@ -643,19 +643,19 @@ <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Imeshindwa kuthibitisha uso. Maunzi hayapatikani."</string> - <string name="face_error_timeout" msgid="2598544068593889762">"Jaribu Kufungua kwa uso tena"</string> + <string name="face_error_timeout" msgid="2598544068593889762">"Jaribu Kufungua kwa Uso tena"</string> <string name="face_error_no_space" msgid="5649264057026021723">"Imeshindwa kuhifadhi data ya uso mpya. Futa wa kale kwanza."</string> <string name="face_error_canceled" msgid="2164434737103802131">"Utendaji wa kitambulisho umeghairiwa."</string> - <string name="face_error_user_canceled" msgid="5766472033202928373">"Hatua ya Kufungua kwa uso imeghairiwa na mtumiaji"</string> + <string name="face_error_user_canceled" msgid="5766472033202928373">"Hatua ya Kufungua kwa Uso imeghairiwa na mtumiaji"</string> <string name="face_error_lockout" msgid="7864408714994529437">"Umejaribu mara nyingi mno. Jaribu tena baadaye."</string> - <string name="face_error_lockout_permanent" msgid="3277134834042995260">"Umejaribu mara nyingi mno. Umezima kipengele cha Kufungua kwa uso."</string> + <string name="face_error_lockout_permanent" msgid="3277134834042995260">"Umejaribu mara nyingi mno. Umezima kipengele cha Kufungua kwa Uso."</string> <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"Umejaribu mara nyingi mno. Weka mbinu ya kufunga skrini badala yake."</string> <string name="face_error_unable_to_process" msgid="5723292697366130070">"Imeshindwa kuthibitisha uso. Jaribu tena."</string> - <string name="face_error_not_enrolled" msgid="1134739108536328412">"Hujaweka mipangilio ya kipengele cha Kufungua kwa uso"</string> - <string name="face_error_hw_not_present" msgid="7940978724978763011">"Kipengele cha Kufungua kwa uso hakitumiki kwenye kifaa hiki"</string> + <string name="face_error_not_enrolled" msgid="1134739108536328412">"Hujaweka mipangilio ya kipengele cha Kufungua kwa Uso"</string> + <string name="face_error_hw_not_present" msgid="7940978724978763011">"Kipengele cha Kufungua kwa Uso hakitumiki kwenye kifaa hiki"</string> <string name="face_error_security_update_required" msgid="5076017208528750161">"Kitambuzi kimezimwa kwa muda."</string> <string name="face_name_template" msgid="3877037340223318119">"Uso wa <xliff:g id="FACEID">%d</xliff:g>"</string> - <string name="face_app_setting_name" msgid="5854024256907828015">"Tumia kipengele cha Kufungua kwa uso"</string> + <string name="face_app_setting_name" msgid="5854024256907828015">"Tumia kipengele cha Kufungua kwa Uso"</string> <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Tumia uso au mbinu ya kufunga skrini"</string> <string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Tumia uso wako ili uendelee"</string> <string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Tumia uso au mbinu yako ya kufunga skrini ili uendelee"</string> @@ -958,7 +958,7 @@ <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Panua eneo la kufungua."</string> <string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Kufungua slaidi."</string> <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Kufungua kwa ruwaza."</string> - <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Kufungua kwa uso."</string> + <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Kufungua kwa Uso."</string> <string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Kufungua kwa PIN."</string> <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"Kufungua Pin ya Sim."</string> <string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"Kufungua Puk ya Sim."</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 318e5b9b06e4..9cef37510e6f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3370,8 +3370,7 @@ <integer name="config_vibrationWaveformRampStepDuration">5</integer> <!-- The duration (in milliseconds) that should be applied to waveform vibrations that ends in - non-zero amplitudes, . The waveform will - be played as a PWLE instead of on/off calls if this value is set. --> + non-zero amplitudes, to bring the vibrator amplitude down to zero using this timing. --> <integer name="config_vibrationWaveformRampDownDuration">0</integer> <!-- Number of retries Cell Data should attempt for a given error code before diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml index 6e6c148e1962..5634f790f234 100644 --- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml @@ -33,127 +33,127 @@ <!-- no translation found for tile_states_default:2 (9192445505551219506) --> <string-array name="tile_states_internet"> <item msgid="5499482407653291407">"No disponible"</item> - <item msgid="3048856902433862868">"No"</item> - <item msgid="6877982264300789870">"Sí"</item> + <item msgid="3048856902433862868">"Desactivado"</item> + <item msgid="6877982264300789870">"Activado"</item> </string-array> <string-array name="tile_states_wifi"> <item msgid="8054147400538405410">"No disponible"</item> - <item msgid="4293012229142257455">"No"</item> - <item msgid="6221288736127914861">"Sí"</item> + <item msgid="4293012229142257455">"Desactivado"</item> + <item msgid="6221288736127914861">"Activado"</item> </string-array> <string-array name="tile_states_cell"> <item msgid="1235899788959500719">"No disponible"</item> - <item msgid="2074416252859094119">"No"</item> - <item msgid="287997784730044767">"Sí"</item> + <item msgid="2074416252859094119">"Desactivado"</item> + <item msgid="287997784730044767">"Activado"</item> </string-array> <string-array name="tile_states_battery"> <item msgid="6311253873330062961">"No disponible"</item> - <item msgid="7838121007534579872">"No"</item> - <item msgid="1578872232501319194">"Sí"</item> + <item msgid="7838121007534579872">"Desactivado"</item> + <item msgid="1578872232501319194">"Activado"</item> </string-array> <string-array name="tile_states_dnd"> <item msgid="467587075903158357">"No disponible"</item> - <item msgid="5376619709702103243">"No"</item> - <item msgid="4875147066469902392">"Sí"</item> + <item msgid="5376619709702103243">"Desactivado"</item> + <item msgid="4875147066469902392">"Activado"</item> </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"No disponible"</item> - <item msgid="5044688398303285224">"No"</item> - <item msgid="8527389108867454098">"Sí"</item> + <item msgid="5044688398303285224">"Desactivado"</item> + <item msgid="8527389108867454098">"Activado"</item> </string-array> <string-array name="tile_states_rotation"> <item msgid="4578491772376121579">"No disponible"</item> - <item msgid="5776427577477729185">"No"</item> - <item msgid="7105052717007227415">"Sí"</item> + <item msgid="5776427577477729185">"Desactivado"</item> + <item msgid="7105052717007227415">"Activado"</item> </string-array> <string-array name="tile_states_bt"> <item msgid="5330252067413512277">"No disponible"</item> - <item msgid="5315121904534729843">"No"</item> - <item msgid="503679232285959074">"Sí"</item> + <item msgid="5315121904534729843">"Desactivado"</item> + <item msgid="503679232285959074">"Activado"</item> </string-array> <string-array name="tile_states_airplane"> <item msgid="1985366811411407764">"No disponible"</item> - <item msgid="4801037224991420996">"No"</item> - <item msgid="1982293347302546665">"Sí"</item> + <item msgid="4801037224991420996">"Desactivado"</item> + <item msgid="1982293347302546665">"Activado"</item> </string-array> <string-array name="tile_states_location"> <item msgid="3316542218706374405">"No disponible"</item> - <item msgid="4813655083852587017">"No"</item> - <item msgid="6744077414775180687">"Sí"</item> + <item msgid="4813655083852587017">"Desactivado"</item> + <item msgid="6744077414775180687">"Activado"</item> </string-array> <string-array name="tile_states_hotspot"> <item msgid="3145597331197351214">"No disponible"</item> - <item msgid="5715725170633593906">"No"</item> - <item msgid="2075645297847971154">"Sí"</item> + <item msgid="5715725170633593906">"Desactivado"</item> + <item msgid="2075645297847971154">"Activado"</item> </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"No disponible"</item> - <item msgid="9103697205127645916">"No"</item> - <item msgid="8067744885820618230">"Sí"</item> + <item msgid="9103697205127645916">"Desactivado"</item> + <item msgid="8067744885820618230">"Activado"</item> </string-array> <string-array name="tile_states_saver"> <item msgid="39714521631367660">"No disponible"</item> - <item msgid="6983679487661600728">"No"</item> - <item msgid="7520663805910678476">"Sí"</item> + <item msgid="6983679487661600728">"Desactivado"</item> + <item msgid="7520663805910678476">"Activado"</item> </string-array> <string-array name="tile_states_dark"> <item msgid="2762596907080603047">"No disponible"</item> - <item msgid="400477985171353">"No"</item> - <item msgid="630890598801118771">"Sí"</item> + <item msgid="400477985171353">"Desactivado"</item> + <item msgid="630890598801118771">"Activado"</item> </string-array> <string-array name="tile_states_work"> <item msgid="389523503690414094">"No disponible"</item> - <item msgid="8045580926543311193">"No"</item> - <item msgid="4913460972266982499">"Sí"</item> + <item msgid="8045580926543311193">"Desactivado"</item> + <item msgid="4913460972266982499">"Activado"</item> </string-array> <string-array name="tile_states_cast"> <item msgid="6032026038702435350">"No disponible"</item> - <item msgid="1488620600954313499">"No"</item> - <item msgid="588467578853244035">"Sí"</item> + <item msgid="1488620600954313499">"Desactivado"</item> + <item msgid="588467578853244035">"Activado"</item> </string-array> <string-array name="tile_states_night"> <item msgid="7857498964264855466">"No disponible"</item> - <item msgid="2744885441164350155">"No"</item> - <item msgid="151121227514952197">"Sí"</item> + <item msgid="2744885441164350155">"Desactivada"</item> + <item msgid="151121227514952197">"Activada"</item> </string-array> <string-array name="tile_states_screenrecord"> <item msgid="1085836626613341403">"No disponible"</item> - <item msgid="8259411607272330225">"No"</item> - <item msgid="578444932039713369">"Sí"</item> + <item msgid="8259411607272330225">"Desactivado"</item> + <item msgid="578444932039713369">"Activado"</item> </string-array> <string-array name="tile_states_reverse"> <item msgid="3574611556622963971">"No disponible"</item> - <item msgid="8707481475312432575">"No"</item> - <item msgid="8031106212477483874">"Sí"</item> + <item msgid="8707481475312432575">"Desactivado"</item> + <item msgid="8031106212477483874">"Activado"</item> </string-array> <string-array name="tile_states_reduce_brightness"> <item msgid="1839836132729571766">"No disponible"</item> - <item msgid="4572245614982283078">"No"</item> - <item msgid="6536448410252185664">"Sí"</item> + <item msgid="4572245614982283078">"Desactivado"</item> + <item msgid="6536448410252185664">"Activado"</item> </string-array> <string-array name="tile_states_cameratoggle"> <item msgid="6680671247180519913">"No disponible"</item> - <item msgid="4765607635752003190">"No"</item> - <item msgid="1697460731949649844">"Sí"</item> + <item msgid="4765607635752003190">"Desactivado"</item> + <item msgid="1697460731949649844">"Activado"</item> </string-array> <string-array name="tile_states_mictoggle"> <item msgid="6895831614067195493">"No disponible"</item> - <item msgid="3296179158646568218">"No"</item> - <item msgid="8998632451221157987">"Sí"</item> + <item msgid="3296179158646568218">"Desactivado"</item> + <item msgid="8998632451221157987">"Activado"</item> </string-array> <string-array name="tile_states_controls"> <item msgid="8199009425335668294">"No disponible"</item> - <item msgid="4544919905196727508">"No"</item> - <item msgid="3422023746567004609">"Sí"</item> + <item msgid="4544919905196727508">"Desactivado"</item> + <item msgid="3422023746567004609">"Activado"</item> </string-array> <string-array name="tile_states_wallet"> <item msgid="4177615438710836341">"No disponible"</item> - <item msgid="7571394439974244289">"No"</item> - <item msgid="6866424167599381915">"Sí"</item> + <item msgid="7571394439974244289">"Desactivado"</item> + <item msgid="6866424167599381915">"Activado"</item> </string-array> <string-array name="tile_states_alarm"> <item msgid="4936533380177298776">"No disponible"</item> - <item msgid="2710157085538036590">"No"</item> - <item msgid="7809470840976856149">"Sí"</item> + <item msgid="2710157085538036590">"Desactivado"</item> + <item msgid="7809470840976856149">"Activado"</item> </string-array> </resources> diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml new file mode 100644 index 000000000000..67dfb34d0f33 --- /dev/null +++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml @@ -0,0 +1,159 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- This resources set the default subtitle for tiles. This way, each tile can be translated + separately. + The indices in the array correspond to the state values in QSTile: + * STATE_UNAVAILABLE + * STATE_INACTIVE + * STATE_ACTIVE + This subtitle is shown when the tile is in that particular state but does not set its own + subtitle, so some of these may never appear on screen. They should still be translated as if + they could appear. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for tile_states_default:0 (4578901299524323311) --> + <!-- no translation found for tile_states_default:1 (7086813178962737808) --> + <!-- no translation found for tile_states_default:2 (9192445505551219506) --> + <string-array name="tile_states_internet"> + <item msgid="5499482407653291407">"ઉપલબ્ધ નથી"</item> + <item msgid="3048856902433862868">"બંધ છે"</item> + <item msgid="6877982264300789870">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_wifi"> + <item msgid="8054147400538405410">"ઉપલબ્ધ નથી"</item> + <item msgid="4293012229142257455">"બંધ છે"</item> + <item msgid="6221288736127914861">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_cell"> + <item msgid="1235899788959500719">"ઉપલબ્ધ નથી"</item> + <item msgid="2074416252859094119">"બંધ છે"</item> + <item msgid="287997784730044767">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_battery"> + <item msgid="6311253873330062961">"ઉપલબ્ધ નથી"</item> + <item msgid="7838121007534579872">"બંધ છે"</item> + <item msgid="1578872232501319194">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_dnd"> + <item msgid="467587075903158357">"ઉપલબ્ધ નથી"</item> + <item msgid="5376619709702103243">"બંધ છે"</item> + <item msgid="4875147066469902392">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_flashlight"> + <item msgid="3465257127433353857">"ઉપલબ્ધ નથી"</item> + <item msgid="5044688398303285224">"બંધ છે"</item> + <item msgid="8527389108867454098">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_rotation"> + <item msgid="4578491772376121579">"ઉપલબ્ધ નથી"</item> + <item msgid="5776427577477729185">"બંધ છે"</item> + <item msgid="7105052717007227415">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_bt"> + <item msgid="5330252067413512277">"ઉપલબ્ધ નથી"</item> + <item msgid="5315121904534729843">"બંધ છે"</item> + <item msgid="503679232285959074">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_airplane"> + <item msgid="1985366811411407764">"ઉપલબ્ધ નથી"</item> + <item msgid="4801037224991420996">"બંધ છે"</item> + <item msgid="1982293347302546665">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_location"> + <item msgid="3316542218706374405">"ઉપલબ્ધ નથી"</item> + <item msgid="4813655083852587017">"બંધ છે"</item> + <item msgid="6744077414775180687">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_hotspot"> + <item msgid="3145597331197351214">"ઉપલબ્ધ નથી"</item> + <item msgid="5715725170633593906">"બંધ છે"</item> + <item msgid="2075645297847971154">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_inversion"> + <item msgid="3638187931191394628">"ઉપલબ્ધ નથી"</item> + <item msgid="9103697205127645916">"બંધ છે"</item> + <item msgid="8067744885820618230">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_saver"> + <item msgid="39714521631367660">"ઉપલબ્ધ નથી"</item> + <item msgid="6983679487661600728">"બંધ છે"</item> + <item msgid="7520663805910678476">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_dark"> + <item msgid="2762596907080603047">"ઉપલબ્ધ નથી"</item> + <item msgid="400477985171353">"બંધ છે"</item> + <item msgid="630890598801118771">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_work"> + <item msgid="389523503690414094">"ઉપલબ્ધ નથી"</item> + <item msgid="8045580926543311193">"બંધ છે"</item> + <item msgid="4913460972266982499">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_cast"> + <item msgid="6032026038702435350">"ઉપલબ્ધ નથી"</item> + <item msgid="1488620600954313499">"બંધ છે"</item> + <item msgid="588467578853244035">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_night"> + <item msgid="7857498964264855466">"ઉપલબ્ધ નથી"</item> + <item msgid="2744885441164350155">"બંધ છે"</item> + <item msgid="151121227514952197">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_screenrecord"> + <item msgid="1085836626613341403">"ઉપલબ્ધ નથી"</item> + <item msgid="8259411607272330225">"બંધ છે"</item> + <item msgid="578444932039713369">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_reverse"> + <item msgid="3574611556622963971">"ઉપલબ્ધ નથી"</item> + <item msgid="8707481475312432575">"બંધ છે"</item> + <item msgid="8031106212477483874">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_reduce_brightness"> + <item msgid="1839836132729571766">"ઉપલબ્ધ નથી"</item> + <item msgid="4572245614982283078">"બંધ છે"</item> + <item msgid="6536448410252185664">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_cameratoggle"> + <item msgid="6680671247180519913">"ઉપલબ્ધ નથી"</item> + <item msgid="4765607635752003190">"બંધ છે"</item> + <item msgid="1697460731949649844">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_mictoggle"> + <item msgid="6895831614067195493">"ઉપલબ્ધ નથી"</item> + <item msgid="3296179158646568218">"બંધ છે"</item> + <item msgid="8998632451221157987">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_controls"> + <item msgid="8199009425335668294">"ઉપલબ્ધ નથી"</item> + <item msgid="4544919905196727508">"બંધ છે"</item> + <item msgid="3422023746567004609">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_wallet"> + <item msgid="4177615438710836341">"ઉપલબ્ધ નથી"</item> + <item msgid="7571394439974244289">"બંધ છે"</item> + <item msgid="6866424167599381915">"ચાલુ છે"</item> + </string-array> + <string-array name="tile_states_alarm"> + <item msgid="4936533380177298776">"ઉપલબ્ધ નથી"</item> + <item msgid="2710157085538036590">"બંધ છે"</item> + <item msgid="7809470840976856149">"ચાલુ છે"</item> + </string-array> +</resources> diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml index 3201c3e62c2c..3f564245c11f 100644 --- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml @@ -32,14 +32,14 @@ <!-- no translation found for tile_states_default:1 (7086813178962737808) --> <!-- no translation found for tile_states_default:2 (9192445505551219506) --> <string-array name="tile_states_internet"> - <item msgid="5499482407653291407">"Indisponibilă"</item> - <item msgid="3048856902433862868">"Dezactivată"</item> - <item msgid="6877982264300789870">"Activată"</item> + <item msgid="5499482407653291407">"Indisponibil"</item> + <item msgid="3048856902433862868">"Dezactivat"</item> + <item msgid="6877982264300789870">"Activat"</item> </string-array> <string-array name="tile_states_wifi"> - <item msgid="8054147400538405410">"Indisponibilă"</item> - <item msgid="4293012229142257455">"Dezactivată"</item> - <item msgid="6221288736127914861">"Activată"</item> + <item msgid="8054147400538405410">"Indisponibil"</item> + <item msgid="4293012229142257455">"Dezactivat"</item> + <item msgid="6221288736127914861">"Activat"</item> </string-array> <string-array name="tile_states_cell"> <item msgid="1235899788959500719">"Indisponibilă"</item> @@ -67,14 +67,14 @@ <item msgid="7105052717007227415">"Activată"</item> </string-array> <string-array name="tile_states_bt"> - <item msgid="5330252067413512277">"Indisponibilă"</item> - <item msgid="5315121904534729843">"Dezactivată"</item> - <item msgid="503679232285959074">"Activată"</item> + <item msgid="5330252067413512277">"Indisponibil"</item> + <item msgid="5315121904534729843">"Dezactivat"</item> + <item msgid="503679232285959074">"Activat"</item> </string-array> <string-array name="tile_states_airplane"> - <item msgid="1985366811411407764">"Indisponibilă"</item> - <item msgid="4801037224991420996">"Dezactivată"</item> - <item msgid="1982293347302546665">"Activată"</item> + <item msgid="1985366811411407764">"Indisponibil"</item> + <item msgid="4801037224991420996">"Dezactivat"</item> + <item msgid="1982293347302546665">"Activat"</item> </string-array> <string-array name="tile_states_location"> <item msgid="3316542218706374405">"Indisponibilă"</item> @@ -82,9 +82,9 @@ <item msgid="6744077414775180687">"Activată"</item> </string-array> <string-array name="tile_states_hotspot"> - <item msgid="3145597331197351214">"Indisponibilă"</item> - <item msgid="5715725170633593906">"Dezactivată"</item> - <item msgid="2075645297847971154">"Activată"</item> + <item msgid="3145597331197351214">"Indisponibil"</item> + <item msgid="5715725170633593906">"Dezactivat"</item> + <item msgid="2075645297847971154">"Activat"</item> </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Indisponibilă"</item> @@ -97,14 +97,14 @@ <item msgid="7520663805910678476">"Activată"</item> </string-array> <string-array name="tile_states_dark"> - <item msgid="2762596907080603047">"Indisponibilă"</item> - <item msgid="400477985171353">"Dezactivată"</item> - <item msgid="630890598801118771">"Activată"</item> + <item msgid="2762596907080603047">"Indisponibil"</item> + <item msgid="400477985171353">"Dezactivat"</item> + <item msgid="630890598801118771">"Activat"</item> </string-array> <string-array name="tile_states_work"> - <item msgid="389523503690414094">"Indisponibilă"</item> - <item msgid="8045580926543311193">"Dezactivată"</item> - <item msgid="4913460972266982499">"Activată"</item> + <item msgid="389523503690414094">"Indisponibil"</item> + <item msgid="8045580926543311193">"Dezactivat"</item> + <item msgid="4913460972266982499">"Activat"</item> </string-array> <string-array name="tile_states_cast"> <item msgid="6032026038702435350">"Indisponibilă"</item> @@ -127,9 +127,9 @@ <item msgid="8031106212477483874">"Activată"</item> </string-array> <string-array name="tile_states_reduce_brightness"> - <item msgid="1839836132729571766">"Indisponibilă"</item> - <item msgid="4572245614982283078">"Dezactivată"</item> - <item msgid="6536448410252185664">"Activată"</item> + <item msgid="1839836132729571766">"Indisponibil"</item> + <item msgid="4572245614982283078">"Dezactivat"</item> + <item msgid="6536448410252185664">"Activat"</item> </string-array> <string-array name="tile_states_cameratoggle"> <item msgid="6680671247180519913">"Indisponibilă"</item> @@ -147,9 +147,9 @@ <item msgid="3422023746567004609">"Activată"</item> </string-array> <string-array name="tile_states_wallet"> - <item msgid="4177615438710836341">"Indisponibilă"</item> - <item msgid="7571394439974244289">"Dezactivată"</item> - <item msgid="6866424167599381915">"Activată"</item> + <item msgid="4177615438710836341">"Indisponibil"</item> + <item msgid="7571394439974244289">"Dezactivat"</item> + <item msgid="6866424167599381915">"Activat"</item> </string-array> <string-array name="tile_states_alarm"> <item msgid="4936533380177298776">"Indisponibilă"</item> diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 5cb6fd9a5279..d6bf8dbe0a6a 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1574,8 +1574,9 @@ public class OomAdjuster { state.setSystemNoUi(false); } if (!state.isSystemNoUi()) { - if (mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE) { - // screen on, promote UI + if (mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE + || state.isRunningRemoteAnimation()) { + // screen on or animating, promote UI state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI); state.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_TOP_APP); } else { diff --git a/services/core/java/com/android/server/compat/overrides/OWNERS b/services/core/java/com/android/server/compat/overrides/OWNERS new file mode 100644 index 000000000000..b80f3402c19d --- /dev/null +++ b/services/core/java/com/android/server/compat/overrides/OWNERS @@ -0,0 +1,2 @@ +tomnatan@google.com +mariiasand@google.com diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java index 8ce7b66e6c7e..ca7b789da47d 100644 --- a/services/core/java/com/android/server/display/BrightnessSetting.java +++ b/services/core/java/com/android/server/display/BrightnessSetting.java @@ -17,19 +17,14 @@ package com.android.server.display; import android.annotation.NonNull; -import android.content.Context; -import android.database.ContentObserver; -import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.PowerManager; -import android.os.UserHandle; -import android.provider.Settings; import android.util.Slog; -import android.view.Display; -import java.util.concurrent.CopyOnWriteArrayList; +import com.android.internal.annotations.GuardedBy; + +import java.util.concurrent.CopyOnWriteArraySet; /** * Saves brightness to a persistent data store, enabling each logical display to have its own @@ -39,14 +34,11 @@ public class BrightnessSetting { private static final String TAG = "BrightnessSetting"; private static final int MSG_BRIGHTNESS_CHANGED = 1; - private static final Uri BRIGHTNESS_FLOAT_URI = - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT); + private final PersistentDataStore mPersistentDataStore; + private final DisplayManagerService.SyncRoot mSyncRoot; - private final boolean mIsDefaultDisplay; - private final Context mContext; private final LogicalDisplay mLogicalDisplay; - private final Object mLock = new Object(); private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override @@ -58,37 +50,20 @@ public class BrightnessSetting { } }; - private final ContentObserver mBrightnessSettingsObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange, Uri uri) { - if (selfChange) { - return; - } - if (BRIGHTNESS_FLOAT_URI.equals(uri)) { - float brightness = getScreenBrightnessSettingFloat(); - setBrightness(brightness, true); - } - } - }; - - private final CopyOnWriteArrayList<BrightnessSettingListener> mListeners = - new CopyOnWriteArrayList<BrightnessSettingListener>(); + private final CopyOnWriteArraySet<BrightnessSettingListener> mListeners = + new CopyOnWriteArraySet<>(); + @GuardedBy("mSyncRoot") private float mBrightness; BrightnessSetting(@NonNull PersistentDataStore persistentDataStore, @NonNull LogicalDisplay logicalDisplay, - @NonNull Context context) { + DisplayManagerService.SyncRoot syncRoot) { mPersistentDataStore = persistentDataStore; mLogicalDisplay = logicalDisplay; - mContext = context; - mIsDefaultDisplay = mLogicalDisplay.getDisplayIdLocked() == Display.DEFAULT_DISPLAY; mBrightness = mPersistentDataStore.getBrightness( mLogicalDisplay.getPrimaryDisplayDeviceLocked()); - if (mIsDefaultDisplay) { - mContext.getContentResolver().registerContentObserver(BRIGHTNESS_FLOAT_URI, - false, mBrightnessSettingsObserver); - } + mSyncRoot = syncRoot; } /** @@ -97,16 +72,19 @@ public class BrightnessSetting { * @return brightness for the current display */ public float getBrightness() { - return mBrightness; + synchronized (mSyncRoot) { + return mBrightness; + } } /** * Registers listener for brightness setting change events. */ public void registerListener(BrightnessSettingListener l) { - if (!mListeners.contains(l)) { - mListeners.add(l); + if (mListeners.contains(l)) { + Slog.wtf(TAG, "Duplicate Listener added"); } + mListeners.add(l); } /** @@ -119,27 +97,16 @@ public class BrightnessSetting { } void setBrightness(float brightness) { - setBrightness(brightness, false); - } - - private void setBrightness(float brightness, boolean isFromSystemSetting) { - if (brightness == mBrightness) { - return; - } if (Float.isNaN(brightness)) { Slog.w(TAG, "Attempting to set invalid brightness"); return; } - synchronized (mLock) { + synchronized (mSyncRoot) { + if (brightness == mBrightness) { + return; + } mBrightness = brightness; - - // If it didn't come from us - if (mIsDefaultDisplay && !isFromSystemSetting) { - Settings.System.putFloatForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FLOAT, brightness, - UserHandle.USER_CURRENT); - } mPersistentDataStore.setBrightness(mLogicalDisplay.getPrimaryDisplayDeviceLocked(), brightness); int toSend = Float.floatToIntBits(mBrightness); @@ -148,12 +115,6 @@ public class BrightnessSetting { } } - private float getScreenBrightnessSettingFloat() { - return Settings.System.getFloatForUser(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FLOAT, PowerManager.BRIGHTNESS_INVALID_FLOAT, - UserHandle.USER_CURRENT); - } - private void notifyListeners(float brightness) { for (BrightnessSettingListener l : mListeners) { l.onBrightnessChanged(brightness); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 79ea108a75d5..9d8ca9a2c26a 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -399,9 +399,6 @@ public final class DisplayManagerService extends SystemService { // Receives notifications about changes to Settings. private SettingsObserver mSettingsObserver; - // Received notifications of the device-state action (such as "fold", "unfold") - private DeviceStateManager mDeviceStateManager; - private final boolean mAllowNonNativeRefreshRateOverride; private final BrightnessSynchronizer mBrightnessSynchronizer; @@ -1236,13 +1233,6 @@ public final class DisplayManagerService extends SystemService { adapter.registerLocked(); } - @VisibleForTesting - void handleLogicalDisplayAdded(LogicalDisplay display) { - synchronized (mSyncRoot) { - handleLogicalDisplayAddedLocked(display); - } - } - private void handleLogicalDisplayAddedLocked(LogicalDisplay display) { final DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); final int displayId = display.getDisplayIdLocked(); @@ -2107,7 +2097,7 @@ public final class DisplayManagerService extends SystemService { } final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore, - display, mContext); + display, mSyncRoot); final DisplayPowerController displayPowerController = new DisplayPowerController( mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting, diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 8396f664146d..20d364ea16e2 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -149,9 +149,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int REPORTED_TO_POLICY_SCREEN_ON = 2; private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3; - private static final Uri BRIGHTNESS_FLOAT_URI = - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT); - private final Object mLock = new Object(); private final Context mContext; @@ -393,7 +390,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The last brightness that was set by the user and not temporary. Set to // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded. - private float mLastUserSetScreenBrightness; + private float mLastUserSetScreenBrightness = Float.NaN; // The screen brightness setting has changed but not taken effect yet. If this is different // from the current screen brightness setting then this is coming from something other than us @@ -424,6 +421,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set. private float mTemporaryAutoBrightnessAdjustment; + // Whether a reduce bright colors (rbc) change has been initiated by the user. We want to + // retain the current backlight level when rbc is toggled, since rbc additionally makes the + // screen appear dimmer using screen colors rather than backlight levels, and therefore we + // don't actually want to compensate for this by then in/decreasing the backlight when + // toggling this feature. + // This should be false during system start up. + private boolean mPendingUserRbcChange; + // Animators. private ObjectAnimator mColorFadeOnAnimator; private ObjectAnimator mColorFadeOffAnimator; @@ -555,24 +560,25 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class); boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() { @Override - public void onReduceBrightColorsActivationChanged(boolean activated) { - applyReduceBrightColorsSplineAdjustment(); + public void onReduceBrightColorsActivationChanged(boolean activated, + boolean userInitiated) { + applyReduceBrightColorsSplineAdjustment(userInitiated); } @Override public void onReduceBrightColorsStrengthChanged(int strength) { - applyReduceBrightColorsSplineAdjustment(); + applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false); } }); if (active) { - applyReduceBrightColorsSplineAdjustment(); + applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false); } } else { mCdsi = null; } } - private void applyReduceBrightColorsSplineAdjustment() { + private void applyReduceBrightColorsSplineAdjustment(boolean userInitiated) { if (mBrightnessMapper == null) { Log.w(TAG, "No brightness mapping available to recalculate splines"); return; @@ -583,6 +589,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]); } mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits); + mPendingUserRbcChange = userInitiated; + sendUpdatePowerState(); } /** @@ -914,7 +922,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void reloadReduceBrightColours() { if (mCdsi != null && mCdsi.isReduceBrightColorsActivated()) { - applyReduceBrightColorsSplineAdjustment(); + applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false); } } @@ -2042,15 +2050,21 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private boolean updateUserSetScreenBrightness() { + final boolean brightnessSplineChanged = mPendingUserRbcChange; + if (mPendingUserRbcChange && !Float.isNaN(mCurrentScreenBrightnessSetting)) { + mLastUserSetScreenBrightness = mCurrentScreenBrightnessSetting; + } + mPendingUserRbcChange = false; + if ((Float.isNaN(mPendingScreenBrightnessSetting) || mPendingScreenBrightnessSetting < 0.0f)) { - return false; + return brightnessSplineChanged; } if (BrightnessSynchronizer.floatEquals( mCurrentScreenBrightnessSetting, mPendingScreenBrightnessSetting)) { mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT; mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; - return false; + return brightnessSplineChanged; } setCurrentScreenBrightness(mPendingScreenBrightnessSetting); mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting; 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 851accca58e1..f9a1368ff3e8 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -356,7 +356,7 @@ public final class ColorDisplayService extends SystemService { updateDisplayWhiteBalanceStatus(); break; case Secure.REDUCE_BRIGHT_COLORS_ACTIVATED: - onReduceBrightColorsActivationChanged(); + onReduceBrightColorsActivationChanged(/*userInitiated*/ true); mHandler.sendEmptyMessage(MSG_APPLY_REDUCE_BRIGHT_COLORS); break; case Secure.REDUCE_BRIGHT_COLORS_LEVEL: @@ -437,7 +437,7 @@ public final class ColorDisplayService extends SystemService { onReduceBrightColorsStrengthLevelChanged(); final boolean reset = resetReduceBrightColors(); if (!reset) { - onReduceBrightColorsActivationChanged(); + onReduceBrightColorsActivationChanged(/*userInitiated*/ false); mHandler.sendEmptyMessage(MSG_APPLY_REDUCE_BRIGHT_COLORS); } } @@ -614,7 +614,7 @@ public final class ColorDisplayService extends SystemService { isAccessiblityInversionEnabled() ? MATRIX_INVERT_COLOR : null); } - private void onReduceBrightColorsActivationChanged() { + private void onReduceBrightColorsActivationChanged(boolean userInitiated) { if (mCurrentUser == UserHandle.USER_NULL) { return; } @@ -622,7 +622,8 @@ public final class ColorDisplayService extends SystemService { Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0, mCurrentUser) == 1; mReduceBrightColorsTintController.setActivated(activated); if (mReduceBrightColorsListener != null) { - mReduceBrightColorsListener.onReduceBrightColorsActivationChanged(activated); + mReduceBrightColorsListener.onReduceBrightColorsActivationChanged(activated, + userInitiated); } } @@ -1552,7 +1553,7 @@ public final class ColorDisplayService extends SystemService { /** * Notify that the reduce bright colors activation status has changed. */ - void onReduceBrightColorsActivationChanged(boolean activated); + void onReduceBrightColorsActivationChanged(boolean activated, boolean userInitiated); /** * Notify that the reduce bright colors strength has changed. diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index c0a1d92104c5..4f484426009e 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -99,16 +99,24 @@ final class VibrationSettings { private boolean mLowPowerMode; VibrationSettings(Context context, Handler handler) { + this(context, handler, + context.getResources().getInteger( + com.android.internal.R.integer.config_vibrationWaveformRampDownDuration), + context.getResources().getInteger( + com.android.internal.R.integer.config_vibrationWaveformRampStepDuration)); + } + + @VisibleForTesting + VibrationSettings(Context context, Handler handler, int rampDownDuration, + int rampStepDuration) { mContext = context; mSettingObserver = new SettingsObserver(handler); mUidObserver = new UidObserver(); mUserReceiver = new UserObserver(); // TODO(b/191150049): move these to vibrator static config file - mRampStepDuration = context.getResources().getInteger( - com.android.internal.R.integer.config_vibrationWaveformRampStepDuration); - mRampDownDuration = context.getResources().getInteger( - com.android.internal.R.integer.config_vibrationWaveformRampDownDuration); + mRampDownDuration = rampDownDuration; + mRampStepDuration = rampStepDuration; VibrationEffect clickEffect = createEffectFromResource( com.android.internal.R.array.config_virtualKeyVibePattern); diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java index 45d511159e11..03863723a5e9 100644 --- a/services/core/java/com/android/server/vibrator/VibrationThread.java +++ b/services/core/java/com/android/server/vibrator/VibrationThread.java @@ -61,6 +61,9 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { */ private static final long CALLBACKS_EXTRA_TIMEOUT = 100; + /** Threshold to prevent the ramp off steps from trying to set extremely low amplitudes. */ + private static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f; + /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */ private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000; @@ -87,26 +90,33 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { /** Callback triggered to cancel a prepared synced vibration. */ void cancelSyncedVibration(); - /** Callback triggered when vibration thread is complete. */ - void onVibrationEnded(long vibrationId, Vibration.Status status); + /** Callback triggered when the vibration is complete. */ + void onVibrationCompleted(long vibrationId, Vibration.Status status); + + /** Callback triggered when the vibrators are released after the thread is complete. */ + void onVibratorsReleased(); } private final Object mLock = new Object(); private final WorkSource mWorkSource = new WorkSource(); private final PowerManager.WakeLock mWakeLock; private final IBatteryStats mBatteryStatsService; + private final VibrationSettings mVibrationSettings; private final DeviceVibrationEffectAdapter mDeviceEffectAdapter; private final Vibration mVibration; private final VibrationCallbacks mCallbacks; private final SparseArray<VibratorController> mVibrators = new SparseArray<>(); private final StepQueue mStepQueue = new StepQueue(); + private volatile boolean mStop; private volatile boolean mForceStop; - VibrationThread(Vibration vib, DeviceVibrationEffectAdapter effectAdapter, + VibrationThread(Vibration vib, VibrationSettings vibrationSettings, + DeviceVibrationEffectAdapter effectAdapter, SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService, VibrationCallbacks callbacks) { mVibration = vib; + mVibrationSettings = vibrationSettings; mDeviceEffectAdapter = effectAdapter; mCallbacks = callbacks; mWakeLock = wakeLock; @@ -145,8 +155,8 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { mWakeLock.acquire(); try { mVibration.token.linkToDeath(this, 0); - Vibration.Status status = playVibration(); - mCallbacks.onVibrationEnded(mVibration.id, status); + playVibration(); + mCallbacks.onVibratorsReleased(); } catch (RemoteException e) { Slog.e(TAG, "Error linking vibration to token death", e); } finally { @@ -155,9 +165,13 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } } - /** Cancel current vibration and shuts down the thread gracefully. */ + /** Cancel current vibration and ramp down the vibrators gracefully. */ public void cancel() { - mForceStop = true; + if (mStop) { + // Already cancelled, running clean-up steps. + return; + } + mStop = true; synchronized (mLock) { if (DEBUG) { Slog.d(TAG, "Vibration cancelled"); @@ -166,6 +180,21 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } } + /** Cancel current vibration and shuts off the vibrators immediately. */ + public void cancelImmediately() { + if (mForceStop) { + // Already forced the thread to stop, wait for it to finish. + return; + } + mStop = mForceStop = true; + synchronized (mLock) { + if (DEBUG) { + Slog.d(TAG, "Vibration cancelled immediately"); + } + mLock.notify(); + } + } + /** Notify current vibration that a synced step has completed. */ public void syncedVibrationComplete() { synchronized (mLock) { @@ -190,17 +219,18 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } } - private Vibration.Status playVibration() { + private void playVibration() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration"); try { - CombinedVibration.Sequential effect = toSequential(mVibration.getEffect()); - mStepQueue.offer(new StartVibrateStep(effect)); + CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect()); + final int sequentialEffectSize = sequentialEffect.getEffects().size(); + mStepQueue.offer(new StartVibrateStep(sequentialEffect)); - int stepsPlayed = 0; + Vibration.Status status = null; while (!mStepQueue.isEmpty()) { long waitTime = mStepQueue.calculateWaitTime(); if (waitTime <= 0) { - stepsPlayed += mStepQueue.consumeNext(); + mStepQueue.consumeNext(); } else { synchronized (mLock) { try { @@ -209,17 +239,33 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } } } + Vibration.Status currentStatus = mStop ? Vibration.Status.CANCELLED + : mStepQueue.calculateVibrationStatus(sequentialEffectSize); + if (status == null && currentStatus != Vibration.Status.RUNNING) { + // First time vibration stopped running, start clean-up tasks and notify + // callback immediately. + status = currentStatus; + mCallbacks.onVibrationCompleted(mVibration.id, status); + if (status == Vibration.Status.CANCELLED) { + mStepQueue.cancel(); + } + } if (mForceStop) { - mStepQueue.cancel(); - return Vibration.Status.CANCELLED; + // Cancel every step and stop playing them right away, even clean-up steps. + mStepQueue.cancelImmediately(); + break; } } - // Some effects might be ignored because the specified vibrator don't exist or doesn't - // support the effect. We only report ignored here if nothing was played besides the - // StartVibrateStep (which means every attempt to turn on the vibrator was ignored). - return stepsPlayed > effect.getEffects().size() - ? Vibration.Status.FINISHED : Vibration.Status.IGNORED_UNSUPPORTED; + if (status == null) { + status = mStepQueue.calculateVibrationStatus(sequentialEffectSize); + if (status == Vibration.Status.RUNNING) { + Slog.w(TAG, "Something went wrong, step queue completed but vibration status" + + " is still RUNNING for vibration " + mVibration.id); + status = Vibration.Status.FINISHED; + } + mCallbacks.onVibrationCompleted(mVibration.id, status); + } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } @@ -260,12 +306,9 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { segmentIndex = effect.getRepeatIndex(); } if (segmentIndex < 0) { - if (vibratorOffTimeout > SystemClock.uptimeMillis()) { - // No more segments to play, last step is to wait for the vibrator to complete - return new OffStep(vibratorOffTimeout, controller); - } else { - return null; - } + // No more segments to play, last step is to complete the vibration on this vibrator. + return new CompleteStep(startTime, /* cancelled= */ false, controller, + vibratorOffTimeout); } VibrationEffectSegment segment = effect.getSegments().get(segmentIndex); @@ -299,8 +342,18 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { @GuardedBy("mLock") private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>(); + @GuardedBy("mLock") + private int mPendingVibrateSteps; + @GuardedBy("mLock") + private int mConsumedStartVibrateSteps; + @GuardedBy("mLock") + private int mSuccessfulVibratorOnSteps; + public void offer(@NonNull Step step) { synchronized (mLock) { + if (!step.isCleanUp()) { + mPendingVibrateSteps++; + } mNextSteps.offer(step); } } @@ -311,6 +364,24 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } } + /** + * Calculate the {@link Vibration.Status} based on the current queue state and the expected + * number of {@link StartVibrateStep} to be played. + */ + public Vibration.Status calculateVibrationStatus(int expectedStartVibrateSteps) { + synchronized (mLock) { + if (mPendingVibrateSteps > 0 + || mConsumedStartVibrateSteps < expectedStartVibrateSteps) { + return Vibration.Status.RUNNING; + } + if (mSuccessfulVibratorOnSteps > 0) { + return Vibration.Status.FINISHED; + } + // If no step was able to turn the vibrator ON successfully. + return Vibration.Status.IGNORED_UNSUPPORTED; + } + } + /** Returns the time in millis to wait before calling {@link #consumeNext()}. */ public long calculateWaitTime() { Step nextStep; @@ -330,18 +401,28 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { * * @return the number of steps played */ - public int consumeNext() { + public void consumeNext() { Step nextStep = pollNext(); if (nextStep != null) { // This might turn on the vibrator and have a HAL latency. Execute this outside any // lock to avoid blocking other interactions with the thread. List<Step> nextSteps = nextStep.play(); synchronized (mLock) { + if (nextStep.getVibratorOnDuration() > 0) { + mSuccessfulVibratorOnSteps++; + } + if (nextStep instanceof StartVibrateStep) { + mConsumedStartVibrateSteps++; + } + if (!nextStep.isCleanUp()) { + mPendingVibrateSteps--; + } + for (int i = 0; i < nextSteps.size(); i++) { + mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1; + } mNextSteps.addAll(nextSteps); } - return 1; } - return 0; } /** @@ -368,16 +449,38 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } /** - * Cancel the current queue, clearing all remaining steps. + * Cancel the current queue, replacing all remaining steps with respective clean-up steps. * - * <p>This will remove and trigger {@link Step#cancel()} in all steps, in order. + * <p>This will remove all steps and replace them with respective + * {@link Step#cancel()}. */ public void cancel() { + List<Step> cleanUpSteps = new ArrayList<>(); + Step step; + while ((step = pollNext()) != null) { + cleanUpSteps.addAll(step.cancel()); + } + synchronized (mLock) { + // All steps generated by Step.cancel() should be clean-up steps. + mPendingVibrateSteps = 0; + mNextSteps.addAll(cleanUpSteps); + } + } + + /** + * Cancel the current queue immediately, clearing all remaining steps and skipping clean-up. + * + * <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order. + */ + public void cancelImmediately() { Step step; while ((step = pollNext()) != null) { // This might turn off the vibrator and have a HAL latency. Execute this outside // any lock to avoid blocking other interactions with the thread. - step.cancel(); + step.cancelImmediately(); + } + synchronized (mLock) { + mPendingVibrateSteps = 0; } } @@ -406,12 +509,37 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { this.startTime = startTime; } + /** + * Returns true if this step is a clean up step and not part of a {@link VibrationEffect} or + * {@link CombinedVibration}. + */ + public boolean isCleanUp() { + return false; + } + /** Play this step, returning a (possibly empty) list of next steps. */ @NonNull public abstract List<Step> play(); - /** Cancel this pending step. */ - public void cancel() { + /** + * Cancel this pending step and return a (possibly empty) list of clean-up steps that should + * be played to gracefully cancel this step. + */ + @NonNull + public abstract List<Step> cancel(); + + /** Cancel this pending step immediately, skipping any clean-up. */ + public abstract void cancelImmediately(); + + /** + * Return the duration the vibrator was turned on when this step was played. + * + * @return A positive duration that the vibrator was turned on for by this step; + * Zero if the segment is not supported, the step was not played yet or vibrator was never + * turned on by this step; A negative value if the vibrator call has failed. + */ + public long getVibratorOnDuration() { + return 0; } /** @@ -452,6 +580,8 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { public final CombinedVibration.Sequential sequentialEffect; public final int currentIndex; + private long mVibratorsOnMaxDuration; + StartVibrateStep(CombinedVibration.Sequential effect) { this(SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0); } @@ -463,10 +593,15 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } @Override + public long getVibratorOnDuration() { + return mVibratorsOnMaxDuration; + } + + @Override public List<Step> play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartVibrateStep"); List<Step> nextSteps = new ArrayList<>(); - long duration = -1; + mVibratorsOnMaxDuration = -1; try { if (DEBUG) { Slog.d(TAG, "StartVibrateStep for effect #" + currentIndex); @@ -478,25 +613,33 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { return nextSteps; } - duration = startVibrating(effectMapping, nextSteps); - noteVibratorOn(duration); + mVibratorsOnMaxDuration = startVibrating(effectMapping, nextSteps); + noteVibratorOn(mVibratorsOnMaxDuration); } finally { - if (duration < 0) { - // Something failed while playing this step so stop playing this sequence. - return EMPTY_STEP_LIST; - } - // It least one vibrator was started then add a finish step to wait for all - // active vibrators to finish their individual steps before going to the next. - // Otherwise this step was ignored so just go to the next one. - Step nextStep = duration > 0 ? new FinishVibrateStep(this) : nextStep(); - if (nextStep != null) { - nextSteps.add(nextStep); + if (mVibratorsOnMaxDuration >= 0) { + // It least one vibrator was started then add a finish step to wait for all + // active vibrators to finish their individual steps before going to the next. + // Otherwise this step was ignored so just go to the next one. + Step nextStep = + mVibratorsOnMaxDuration > 0 ? new FinishVibrateStep(this) : nextStep(); + if (nextStep != null) { + nextSteps.add(nextStep); + } } Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } return nextSteps; } + @Override + public List<Step> cancel() { + return EMPTY_STEP_LIST; + } + + @Override + public void cancelImmediately() { + } + /** * Create the next {@link StartVibrateStep} to play this sequential effect, starting at the * time this method is called, or null if sequence is complete. @@ -593,7 +736,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { // Some vibrator failed without being prepared so other vibrators might be // active. Cancel and remove every pending step from output list. for (int i = nextSteps.size() - 1; i >= 0; i--) { - nextSteps.remove(i).cancel(); + nextSteps.remove(i).cancelImmediately(); } } } @@ -627,6 +770,12 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } @Override + public boolean isCleanUp() { + // This step only notes that all the vibrators has been turned off. + return true; + } + + @Override public List<Step> play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishVibrateStep"); try { @@ -642,7 +791,13 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } @Override - public void cancel() { + public List<Step> cancel() { + cancelImmediately(); + return EMPTY_STEP_LIST; + } + + @Override + public void cancelImmediately() { noteVibratorOff(); } } @@ -658,6 +813,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { public final long vibratorOffTimeout; long mVibratorOnResult; + boolean mVibratorCallbackReceived; /** * @param startTime The time to schedule this step in the {@link StepQueue}. @@ -678,27 +834,28 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { this.vibratorOffTimeout = vibratorOffTimeout; } - /** - * Return the duration the vibrator was turned on when this step was played. - * - * @return A positive duration that the vibrator was turned on for by this step; - * Zero if the segment is not supported, the step was not played yet or vibrator was never - * turned on by this step; A negative value if the vibrator call has failed. - */ + @Override public long getVibratorOnDuration() { return mVibratorOnResult; } @Override public boolean shouldPlayWhenVibratorComplete(int vibratorId) { + boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId; + mVibratorCallbackReceived |= isSameVibrator; // Only anticipate this step if a timeout was set to wait for the vibration to complete, // otherwise we are waiting for the correct time to play the next step. - return (controller.getVibratorInfo().getId() == vibratorId) - && (vibratorOffTimeout > SystemClock.uptimeMillis()); + return isSameVibrator && (vibratorOffTimeout > SystemClock.uptimeMillis()); } @Override - public void cancel() { + public List<Step> cancel() { + return Arrays.asList(new CompleteStep(SystemClock.uptimeMillis(), + /* cancelled= */ true, controller, vibratorOffTimeout)); + } + + @Override + public void cancelImmediately() { if (vibratorOffTimeout > SystemClock.uptimeMillis()) { // Vibrator might be running from previous steps, so turn it off while canceling. stopVibrating(); @@ -712,6 +869,14 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { controller.off(); } + void changeAmplitude(float amplitude) { + if (DEBUG) { + Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId() + + " to " + amplitude); + } + controller.setAmplitude(amplitude); + } + /** Return the {@link #nextVibrateStep} with same timings, only jumping the segments. */ public List<Step> skipToNextSteps(int segmentsSkipped) { return nextSteps(startTime, vibratorOffTimeout, segmentsSkipped); @@ -938,6 +1103,140 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } /** + * Represents a step to complete a {@link VibrationEffect}. + * + * <p>This runs right at the time the vibration is considered to end and will update the pending + * vibrators count. This can turn off the vibrator or slowly ramp it down to zero amplitude. + */ + private final class CompleteStep extends SingleVibratorStep { + private final boolean mCancelled; + + CompleteStep(long startTime, boolean cancelled, VibratorController controller, + long vibratorOffTimeout) { + super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout); + mCancelled = cancelled; + } + + @Override + public boolean isCleanUp() { + // If the vibration was cancelled then this is just a clean up to ramp off the vibrator. + // Otherwise this step is part of the vibration. + return mCancelled; + } + + @Override + public List<Step> cancel() { + if (mCancelled) { + // Double cancelling will just turn off the vibrator right away. + return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller)); + } + return super.cancel(); + } + + @Override + public List<Step> play() { + Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "CompleteStep"); + try { + if (DEBUG) { + Slog.d(TAG, "Running " + (mCancelled ? "cancel" : "complete") + " vibration" + + " step on vibrator " + controller.getVibratorInfo().getId()); + } + if (mVibratorCallbackReceived) { + // Vibration completion callback was received by this step, just turn if off + // and skip any clean-up. + stopVibrating(); + return EMPTY_STEP_LIST; + } + + float currentAmplitude = controller.getCurrentAmplitude(); + long remainingOnDuration = + vibratorOffTimeout - CALLBACKS_EXTRA_TIMEOUT - SystemClock.uptimeMillis(); + long rampDownDuration = + Math.min(remainingOnDuration, mVibrationSettings.getRampDownDuration()); + long stepDownDuration = mVibrationSettings.getRampStepDuration(); + if (currentAmplitude < RAMP_OFF_AMPLITUDE_MIN + || rampDownDuration <= stepDownDuration) { + // No need to ramp down the amplitude, just wait to turn it off. + if (mCancelled) { + // Vibration is completing because it was cancelled, turn off right away. + stopVibrating(); + return EMPTY_STEP_LIST; + } else { + return Arrays.asList(new OffStep(vibratorOffTimeout, controller)); + } + } + + if (DEBUG) { + Slog.d(TAG, "Ramping down vibrator " + controller.getVibratorInfo().getId() + + " from amplitude " + currentAmplitude + + " for " + rampDownDuration + "ms"); + } + float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration); + float amplitudeTarget = currentAmplitude - amplitudeDelta; + long newVibratorOffTimeout = mCancelled ? rampDownDuration : vibratorOffTimeout; + return Arrays.asList(new RampOffStep(startTime, amplitudeTarget, amplitudeDelta, + controller, newVibratorOffTimeout)); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); + } + } + } + + /** Represents a step to ramp down the vibrator amplitude before turning it off. */ + private final class RampOffStep extends SingleVibratorStep { + private final float mAmplitudeTarget; + private final float mAmplitudeDelta; + + RampOffStep(long startTime, float amplitudeTarget, float amplitudeDelta, + VibratorController controller, long vibratorOffTimeout) { + super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout); + mAmplitudeTarget = amplitudeTarget; + mAmplitudeDelta = amplitudeDelta; + } + + @Override + public boolean isCleanUp() { + return true; + } + + @Override + public List<Step> cancel() { + return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller)); + } + + @Override + public List<Step> play() { + Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "RampOffStep"); + try { + if (DEBUG) { + long latency = SystemClock.uptimeMillis() - startTime; + Slog.d(TAG, "Ramp down the vibrator amplitude, step with " + + latency + "ms latency."); + } + if (mVibratorCallbackReceived) { + // Vibration completion callback was received by this step, just turn if off + // and skip the rest of the steps to ramp down the vibrator amplitude. + stopVibrating(); + return EMPTY_STEP_LIST; + } + + changeAmplitude(mAmplitudeTarget); + + float newAmplitudeTarget = mAmplitudeTarget - mAmplitudeDelta; + if (newAmplitudeTarget < RAMP_OFF_AMPLITUDE_MIN) { + // Vibrator amplitude cannot go further down, just turn it off. + return Arrays.asList(new OffStep(vibratorOffTimeout, controller)); + } + return Arrays.asList(new RampOffStep( + startTime + mVibrationSettings.getRampStepDuration(), newAmplitudeTarget, + mAmplitudeDelta, controller, vibratorOffTimeout)); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); + } + } + } + + /** * Represents a step to turn the vibrator off. * * <p>This runs after a timeout on the expected time the vibrator should have finished playing, @@ -950,6 +1249,21 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { } @Override + public boolean isCleanUp() { + return true; + } + + @Override + public List<Step> cancel() { + return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller)); + } + + @Override + public void cancelImmediately() { + stopVibrating(); + } + + @Override public List<Step> play() { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "OffStep"); try { @@ -1044,14 +1358,6 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { return controller.on(duration, mVibration.id); } - private void changeAmplitude(float amplitude) { - if (DEBUG) { - Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId() - + " to " + amplitude); - } - controller.setAmplitude(amplitude); - } - /** * Get the duration the vibrator will be on for a waveform, starting at {@code startIndex} * until the next time it's vibrating amplitude is zero or a different type of segment is @@ -1080,6 +1386,11 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { return 1000; } } + if (i == segmentCount && effect.getRepeatIndex() < 0) { + // Vibration ending at non-zero amplitude, add extra timings to ramp down after + // vibration is complete. + timing += mVibrationSettings.getRampDownDuration(); + } return timing; } } diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java index 001d5c440d58..69cc90bf381e 100644 --- a/services/core/java/com/android/server/vibrator/VibratorController.java +++ b/services/core/java/com/android/server/vibrator/VibratorController.java @@ -54,6 +54,8 @@ final class VibratorController { private boolean mIsVibrating; @GuardedBy("mLock") private boolean mIsUnderExternalControl; + @GuardedBy("mLock") + private float mCurrentAmplitude; /** Listener for vibration completion callbacks from native. */ public interface OnVibrationCompleteListener { @@ -131,6 +133,23 @@ final class VibratorController { } } + /** + * Returns the current amplitude the device is vibrating. + * + * <p>This value is set to 1 by the method {@link #on(long, long)}, and can be updated via + * {@link #setAmplitude(float)} if called while the device is vibrating. + * + * <p>If the device is vibrating via any other {@link #on} method then the current amplitude is + * unknown and this will return -1. + * + * <p>If {@link #isVibrating()} is false then this will be zero. + */ + public float getCurrentAmplitude() { + synchronized (mLock) { + return mCurrentAmplitude; + } + } + /** Return {@code true} if this vibrator is under external control, false otherwise. */ public boolean isUnderExternalControl() { synchronized (mLock) { @@ -192,6 +211,9 @@ final class VibratorController { if (mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { mNativeWrapper.setAmplitude(amplitude); } + if (mIsVibrating) { + mCurrentAmplitude = amplitude; + } } } @@ -208,6 +230,7 @@ final class VibratorController { synchronized (mLock) { long duration = mNativeWrapper.on(milliseconds, vibrationId); if (duration > 0) { + mCurrentAmplitude = -1; notifyVibratorOnLocked(); } return duration; @@ -228,6 +251,7 @@ final class VibratorController { long duration = mNativeWrapper.perform(prebaked.getEffectId(), prebaked.getEffectStrength(), vibrationId); if (duration > 0) { + mCurrentAmplitude = -1; notifyVibratorOnLocked(); } return duration; @@ -250,6 +274,7 @@ final class VibratorController { synchronized (mLock) { long duration = mNativeWrapper.compose(primitives, vibrationId); if (duration > 0) { + mCurrentAmplitude = -1; notifyVibratorOnLocked(); } return duration; @@ -272,6 +297,7 @@ final class VibratorController { int braking = mVibratorInfo.getDefaultBraking(); long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId); if (duration > 0) { + mCurrentAmplitude = -1; notifyVibratorOnLocked(); } return duration; @@ -282,19 +308,23 @@ final class VibratorController { public void off() { synchronized (mLock) { mNativeWrapper.off(); + mCurrentAmplitude = 0; notifyVibratorOffLocked(); } } @Override public String toString() { - return "VibratorController{" - + "mVibratorInfo=" + mVibratorInfo - + ", mIsVibrating=" + mIsVibrating - + ", mIsUnderExternalControl=" + mIsUnderExternalControl - + ", mVibratorStateListeners count=" - + mVibratorStateListeners.getRegisteredCallbackCount() - + '}'; + synchronized (mLock) { + return "VibratorController{" + + "mVibratorInfo=" + mVibratorInfo + + ", mIsVibrating=" + mIsVibrating + + ", mCurrentAmplitude=" + mCurrentAmplitude + + ", mIsUnderExternalControl=" + mIsUnderExternalControl + + ", mVibratorStateListeners count=" + + mVibratorStateListeners.getRegisteredCallbackCount() + + '}'; + } } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 644e451ce0e8..fb8498ec73c1 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -514,8 +514,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { return Vibration.Status.FORWARDED_TO_INPUT_DEVICES; } - VibrationThread vibThread = new VibrationThread(vib, mDeviceVibrationEffectAdapter, - mVibrators, mWakeLock, mBatteryStatsService, mVibrationCallbacks); + VibrationThread vibThread = new VibrationThread(vib, mVibrationSettings, + mDeviceVibrationEffectAdapter, mVibrators, mWakeLock, mBatteryStatsService, + mVibrationCallbacks); if (mCurrentVibration == null) { return startVibrationThreadLocked(vibThread); @@ -569,7 +570,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); try { Vibration vib = mCurrentVibration.getVibration(); - mCurrentVibration = null; endVibrationLocked(vib, status); finishAppOpModeLocked(vib.uid, vib.opPkg); } finally { @@ -613,7 +613,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // Repeating vibrations always take precedence. return null; } - if (mCurrentVibration != null) { + if (mCurrentVibration != null && !mCurrentVibration.getVibration().hasEnded()) { if (mCurrentVibration.getVibration().attrs.getUsage() == VibrationAttributes.USAGE_ALARM) { if (DEBUG) { @@ -1003,20 +1003,29 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } @Override - public void onVibrationEnded(long vibrationId, Vibration.Status status) { + public void onVibrationCompleted(long vibrationId, Vibration.Status status) { if (DEBUG) { - Slog.d(TAG, "Vibration " + vibrationId + " thread finished with status " + status); + Slog.d(TAG, "Vibration " + vibrationId + " finished with status " + status); } synchronized (mLock) { if (mCurrentVibration != null && mCurrentVibration.getVibration().id == vibrationId) { reportFinishedVibrationLocked(status); + } + } + } - if (mNextVibration != null) { - VibrationThread vibThread = mNextVibration; - mNextVibration = null; - startVibrationThreadLocked(vibThread); - } + @Override + public void onVibratorsReleased() { + if (DEBUG) { + Slog.d(TAG, "Vibrators released after finished vibration"); + } + synchronized (mLock) { + mCurrentVibration = null; + if (mNextVibration != null) { + VibrationThread vibThread = mNextVibration; + mNextVibration = null; + startVibrationThreadLocked(vibThread); } } } @@ -1337,7 +1346,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { // vibration that may be playing and ready the vibrator for external control. if (mCurrentVibration != null) { mNextVibration = null; - mCurrentVibration.cancel(); + mCurrentVibration.cancelImmediately(); cancelingVibration = mCurrentVibration; } } else { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 8fb77c3cf61d..16d3c531d018 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -301,6 +301,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // started or finished. static final long ACTIVITY_BG_START_GRACE_PERIOD_MS = 10 * 1000; + /** + * The duration to keep a process in animating state (top scheduling group) when the + * wakefulness is changing from awake to doze or sleep. + */ + private static final long DOZE_ANIMATING_STATE_RETAIN_TIME_MS = 2000; + /** Used to indicate that an app transition should be animated. */ static final boolean ANIMATE = true; @@ -2757,12 +2763,35 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { }); } + // The caller MUST NOT hold the global lock. public void onScreenAwakeChanged(boolean isAwake) { mH.post(() -> { for (int i = mScreenObservers.size() - 1; i >= 0; i--) { mScreenObservers.get(i).onAwakeStateChanged(isAwake); } }); + + if (isAwake) { + return; + } + // If the device is going to sleep, keep a higher priority temporarily for potential + // animation of system UI. Even if AOD is not enabled, it should be no harm. + final WindowProcessController proc; + synchronized (mGlobalLockWithoutBoost) { + final WindowState notificationShade = mRootWindowContainer.getDefaultDisplay() + .getDisplayPolicy().getNotificationShade(); + proc = notificationShade != null + ? mProcessMap.getProcess(notificationShade.mSession.mPid) : null; + } + if (proc == null) { + return; + } + // Set to activity manager directly to make sure the state can be seen by the subsequent + // update of scheduling group. + proc.setRunningAnimationUnsafe(); + mH.removeMessages(H.UPDATE_PROCESS_ANIMATING_STATE, proc); + mH.sendMessageDelayed(mH.obtainMessage(H.UPDATE_PROCESS_ANIMATING_STATE, proc), + DOZE_ANIMATING_STATE_RETAIN_TIME_MS); } @Override @@ -5036,7 +5065,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final class H extends Handler { static final int REPORT_TIME_TRACKER_MSG = 1; - + static final int UPDATE_PROCESS_ANIMATING_STATE = 2; static final int FIRST_ACTIVITY_TASK_MSG = 100; static final int FIRST_SUPERVISOR_TASK_MSG = 200; @@ -5053,6 +5082,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { tracker.deliverResult(mContext); } break; + case UPDATE_PROCESS_ANIMATING_STATE: { + final WindowProcessController proc = (WindowProcessController) msg.obj; + synchronized (mGlobalLock) { + proc.updateRunningRemoteOrRecentsAnimation(); + } + } + break; } } } diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java index 2beb3780633e..35add129309f 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java +++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java @@ -353,7 +353,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl // Only update focus/visibility for the last one because there may be many root tasks are // reparented and the intermediate states are unnecessary. if (lastReparentedRootTask != null) { - lastReparentedRootTask.postReparent(); + lastReparentedRootTask.resumeNextFocusAfterReparent(); } } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 58c15797fe1f..a627018daa46 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5695,7 +5695,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Only update focus/visibility for the last one because there may be many root tasks are // reparented and the intermediate states are unnecessary. if (lastReparentedRootTask != null) { - lastReparentedRootTask.postReparent(); + lastReparentedRootTask.resumeNextFocusAfterReparent(); } releaseSelfIfNeeded(); mDisplayPolicy.release(); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index c2b9796a3d94..d240b88965cc 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -71,7 +71,6 @@ import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList; import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity; -import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT; import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER; import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER; @@ -2078,6 +2077,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> + " to its current taskDisplayArea=" + taskDisplayArea); } rootTask.reparent(taskDisplayArea, onTop); + + // Resume focusable root task after reparenting to another display area. + rootTask.resumeNextFocusAfterReparent(); + // TODO(multi-display): resize rootTasks properly if moved from split-screen. } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index a6bf520561ac..42d56e4b9f19 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1231,12 +1231,6 @@ class Task extends TaskFragment { adjustBoundsForDisplayChangeIfNeeded(getDisplayContent()); mRootWindowContainer.updateUIDsPresentOnDisplay(); - - // Resume next focusable root task after reparenting to another display if we aren't - // removing the prevous display. - if (oldDisplay != null && oldDisplay.isRemoving()) { - postReparent(); - } } /** Returns the currently topmost resumed activity. */ @@ -4514,8 +4508,7 @@ class Task extends TaskFragment { mRootWindowContainer.resumeFocusedTasksTopActivities(); } - /** Resume next focusable root task after reparenting to another display. */ - void postReparent() { + void resumeNextFocusAfterReparent() { adjustFocusToNextFocusableTask("reparent", true /* allowFocusSelf */, true /* moveDisplayToTop */); mRootWindowContainer.resumeFocusedTasksTopActivities(); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index bee722e8c5e6..0ca6dc6daf4a 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -1594,14 +1594,18 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio updateRunningRemoteOrRecentsAnimation(); } - private void updateRunningRemoteOrRecentsAnimation() { - + void updateRunningRemoteOrRecentsAnimation() { // Posting on handler so WM lock isn't held when we call into AM. mAtm.mH.sendMessage(PooledLambda.obtainMessage( WindowProcessListener::setRunningRemoteAnimation, mListener, mRunningRecentsAnimation || mRunningRemoteAnimation)); } + /** Adjusts scheduling group for animation. This method MUST NOT be called inside WM lock. */ + void setRunningAnimationUnsafe() { + mListener.setRunningRemoteAnimation(true); + } + @Override public String toString() { return mOwner != null ? mOwner.toString() : null; diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index 7513512406e8..3d4f866948af 100644 --- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -131,28 +131,6 @@ static bool sendRequest(int fd, RequestType requestType, FileIdx fileIdx = -1, return android::base::WriteFully(fd, &command, sizeof(command)); } -static int waitForDataOrSignal(int fd, int event_fd) { - struct pollfd pfds[2] = {{fd, POLLIN, 0}, {event_fd, POLLIN, 0}}; - // Wait until either data is ready or stop signal is received - int res = poll(pfds, 2, PollTimeoutMs); - if (res == -1 && errno == EINTR) { - // Treat it the same as timeout and allow the caller to retry. - return 0; - } - if (res <= 0) { - return res; - } - // First check if there is a stop signal - if (pfds[1].revents == POLLIN) { - return event_fd; - } - // Otherwise check if incoming data is ready - if (pfds[0].revents == POLLIN) { - return fd; - } - return -1; -} - static bool readChunk(int fd, std::vector<uint8_t>& data) { int32_t size; if (!android::base::ReadFully(fd, &size, sizeof(size))) { @@ -382,7 +360,12 @@ static OnTraceChanged& onTraceChanged() { class PMSCDataLoader : public android::dataloader::DataLoader { public: PMSCDataLoader(JavaVM* jvm) : mJvm(jvm) { CHECK(mJvm); } - ~PMSCDataLoader() { onTraceChanged().unregisterCallback(this); } + ~PMSCDataLoader() { + onTraceChanged().unregisterCallback(this); + if (mReceiverThread.joinable()) { + mReceiverThread.join(); + } + } void updateReadLogsState(const bool enabled) { if (enabled != mReadLogsEnabled.exchange(enabled)) { @@ -417,11 +400,7 @@ private: mReceiverThread.join(); } } - void onDestroy() final { - onTraceChanged().unregisterCallback(this); - // Make sure the receiver thread stopped. - CHECK(!mReceiverThread.joinable()); - } + void onDestroy() final {} // Installation. bool onPrepareImage(dataloader::DataLoaderInstallationFiles addedFiles) final { @@ -573,6 +552,60 @@ private: return true; } + enum class WaitResult { + DataAvailable, + Timeout, + Failure, + StopRequested, + }; + + WaitResult waitForData(int fd) { + using Clock = std::chrono::steady_clock; + using Milliseconds = std::chrono::milliseconds; + + auto pollTimeoutMs = PollTimeoutMs; + const auto waitEnd = Clock::now() + Milliseconds(pollTimeoutMs); + while (!mStopReceiving) { + struct pollfd pfds[2] = {{fd, POLLIN, 0}, {mEventFd, POLLIN, 0}}; + // Wait until either data is ready or stop signal is received + int res = poll(pfds, std::size(pfds), pollTimeoutMs); + + if (res < 0) { + if (errno == EINTR) { + pollTimeoutMs = std::chrono::duration_cast<Milliseconds>(waitEnd - Clock::now()) + .count(); + if (pollTimeoutMs < 0) { + return WaitResult::Timeout; + } + continue; + } + ALOGE("Failed to poll. Error %d", errno); + return WaitResult::Failure; + } + + if (res == 0) { + return WaitResult::Timeout; + } + + // First check if there is a stop signal + if (pfds[1].revents == POLLIN) { + ALOGE("DataLoader requested to stop."); + return WaitResult::StopRequested; + } + // Otherwise check if incoming data is ready + if (pfds[0].revents == POLLIN) { + return WaitResult::DataAvailable; + } + + // Invalid case, just fail. + ALOGE("Failed to poll. Result %d", res); + return WaitResult::Failure; + } + + ALOGE("DataLoader requested to stop."); + return WaitResult::StopRequested; + } + // Streaming. bool initStreaming(unique_fd inout, MetadataMode mode) { mEventFd.reset(eventfd(0, EFD_CLOEXEC)); @@ -582,6 +615,11 @@ private: } // Awaiting adb handshake. + if (waitForData(inout) != WaitResult::DataAvailable) { + ALOGE("Failure waiting for the handshake."); + return false; + } + char okay_buf[OKAY.size()]; if (!android::base::ReadFully(inout, okay_buf, OKAY.size())) { ALOGE("Failed to receive OKAY. Abort. Error %d", errno); @@ -601,8 +639,14 @@ private: } } + if (mStopReceiving) { + ALOGE("DataLoader requested to stop."); + return false; + } + mReceiverThread = std::thread( [this, io = std::move(inout), mode]() mutable { receiver(std::move(io), mode); }); + ALOGI("Started streaming..."); return true; } @@ -750,17 +794,16 @@ private: std::vector<IncFsDataBlock> instructions; std::unordered_map<FileIdx, unique_fd> writeFds; while (!mStopReceiving) { - const int res = waitForDataOrSignal(inout, mEventFd); - if (res == 0) { + const auto res = waitForData(inout); + if (res == WaitResult::Timeout) { continue; } - if (res < 0) { - ALOGE("Failed to poll. Abort. Error %d", res); + if (res == WaitResult::Failure) { mStatusListener->reportStatus(DATA_LOADER_UNRECOVERABLE); break; } - if (res == mEventFd) { - ALOGE("DataLoader requested to stop. Sending EXIT to server."); + if (res == WaitResult::StopRequested) { + ALOGE("Sending EXIT to server."); sendRequest(inout, EXIT); break; } diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS b/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS new file mode 100644 index 000000000000..f8c3520e9fa8 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/compat/OWNERS diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS new file mode 100644 index 000000000000..6e8aefc4bc01 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/compat/overrides/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index 6d25e8c55a51..1596483cdbe1 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -86,6 +87,7 @@ public class VibrationThreadTest { private static final int VIBRATOR_ID = 1; private static final String PACKAGE_NAME = "package"; private static final VibrationAttributes ATTRS = new VibrationAttributes.Builder().build(); + private static final int TEST_RAMP_STEP_DURATION = 5; @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @@ -100,6 +102,7 @@ public class VibrationThreadTest { private IBatteryStats mIBatteryStatsMock; private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>(); + private VibrationSettings mVibrationSettings; private DeviceVibrationEffectAdapter mEffectAdapter; private PowerManager.WakeLock mWakeLock; private TestLooper mTestLooper; @@ -109,9 +112,9 @@ public class VibrationThreadTest { mTestLooper = new TestLooper(); Context context = InstrumentationRegistry.getContext(); - VibrationSettings vibrationSettings = new VibrationSettings(context, - new Handler(mTestLooper.getLooper())); - mEffectAdapter = new DeviceVibrationEffectAdapter(vibrationSettings); + mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()), + /* rampDownDuration= */ 0, TEST_RAMP_STEP_DURATION); + mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); mWakeLock = context.getSystemService( PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); @@ -128,8 +131,7 @@ public class VibrationThreadTest { waitForCompletion(thread); verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), - eq(Vibration.Status.IGNORED_UNSUPPORTED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); } @Test @@ -143,8 +145,7 @@ public class VibrationThreadTest { waitForCompletion(thread); verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), - eq(Vibration.Status.IGNORED_UNSUPPORTED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); } @Test @@ -159,7 +160,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L)); verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), @@ -178,7 +179,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L)); verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), @@ -200,7 +201,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(15L)); verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(15)), @@ -232,7 +233,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), anyLong()); verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); List<Float> playedAmplitudes = fakeVibrator.getAmplitudes(); @@ -269,7 +270,7 @@ public class VibrationThreadTest { waitForCompletion(vibrationThread, /* timeout= */ 50); waitForCompletion(cancellingThread); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating()); } @@ -294,7 +295,7 @@ public class VibrationThreadTest { waitForCompletion(vibrationThread, /* timeout= */ 50); waitForCompletion(cancellingThread); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating()); } @@ -310,7 +311,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L)); verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)), @@ -333,7 +334,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L)); verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedOneShot(10)), @@ -352,8 +353,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong()); verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID)); verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), - eq(Vibration.Status.IGNORED_UNSUPPORTED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty()); } @@ -373,7 +373,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(40L)); verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList( expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0), @@ -393,8 +393,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock, never()).noteVibratorOn(eq(UID), anyLong()); verify(mIBatteryStatsMock, never()).noteVibratorOff(eq(UID)); verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), - eq(Vibration.Status.IGNORED_UNSUPPORTED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED); assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty()); } @@ -413,7 +412,7 @@ public class VibrationThreadTest { VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); waitForCompletion(thread); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); // Vibrator compose called twice. verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); assertEquals(3, fakeVibrator.getEffectSegments().size()); @@ -443,7 +442,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(10L)); verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList( expectedOneShot(10), @@ -479,7 +478,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(100L)); verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList( expectedRamp(/* amplitude= */ 1, /* frequency= */ 150, /* duration= */ 10), @@ -512,7 +511,7 @@ public class VibrationThreadTest { VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); waitForCompletion(thread); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); // Vibrator compose called twice. verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); assertEquals(4, fakeVibrator.getEffectSegments().size()); @@ -537,7 +536,7 @@ public class VibrationThreadTest { waitForCompletion(thread); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); } @Test @@ -545,10 +544,10 @@ public class VibrationThreadTest { mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); long vibrationId = 1; - waitForCompletion(startThreadAndDispatcher(vibrationId++, + waitForCompletion(startThreadAndDispatcher(vibrationId, VibrationEffect.createOneShot(10, 100))); - verify(mThreadCallbacks).onVibrationEnded(anyLong(), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); verify(mThreadCallbacks, never()).prepareSyncedVibration(anyLong(), any()); verify(mThreadCallbacks, never()).triggerSyncedVibration(anyLong()); verify(mThreadCallbacks, never()).cancelSyncedVibration(); @@ -571,7 +570,7 @@ public class VibrationThreadTest { verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)), @@ -596,7 +595,7 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(1).isVibrating()); assertFalse(thread.getVibrators().get(2).isVibrating()); assertFalse(thread.getVibrators().get(3).isVibrating()); @@ -635,7 +634,7 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(1).isVibrating()); assertFalse(thread.getVibrators().get(2).isVibrating()); assertFalse(thread.getVibrators().get(3).isVibrating()); @@ -686,7 +685,7 @@ public class VibrationThreadTest { batterVerifier.verify(mIBatteryStatsMock).noteVibratorOn(eq(UID), eq(20L)); batterVerifier.verify(mIBatteryStatsMock).noteVibratorOff(eq(UID)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(1).isVibrating()); assertFalse(thread.getVibrators().get(2).isVibrating()); assertFalse(thread.getVibrators().get(3).isVibrating()); @@ -727,7 +726,7 @@ public class VibrationThreadTest { verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds)); verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId)); verify(mThreadCallbacks, never()).cancelSyncedVibration(); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); VibrationEffectSegment expected = expectedPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100); @@ -767,7 +766,7 @@ public class VibrationThreadTest { verify(mThreadCallbacks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds)); verify(mThreadCallbacks).triggerSyncedVibration(eq(vibrationId)); verify(mThreadCallbacks, never()).cancelSyncedVibration(); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); } @Test @@ -858,7 +857,7 @@ public class VibrationThreadTest { verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId)); verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(1).isVibrating()); assertFalse(thread.getVibrators().get(2).isVibrating()); assertFalse(thread.getVibrators().get(3).isVibrating()); @@ -935,7 +934,7 @@ public class VibrationThreadTest { // After the vibrator call ends the vibration is cancelled and the vibrator is turned off. waitForCompletion(vibrationThread, /* timeout= */ latency + TEST_TIMEOUT_MILLIS); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating()); } @@ -968,7 +967,7 @@ public class VibrationThreadTest { waitForCompletion(vibrationThread, /* timeout= */ 50); waitForCompletion(cancellingThread); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(vibrationThread.getVibrators().get(1).isVibrating()); assertFalse(vibrationThread.getVibrators().get(2).isVibrating()); } @@ -1000,7 +999,7 @@ public class VibrationThreadTest { waitForCompletion(vibrationThread, /* timeout= */ 50); waitForCompletion(cancellingThread); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(vibrationThread.getVibrators().get(1).isVibrating()); assertFalse(vibrationThread.getVibrators().get(2).isVibrating()); } @@ -1020,11 +1019,179 @@ public class VibrationThreadTest { verify(mVibrationToken).linkToDeath(same(thread), eq(0)); verify(mVibrationToken).unlinkToDeath(same(thread), eq(0)); - verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.CANCELLED)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments().isEmpty()); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); } + @Test + public void vibrate_waveformWithRampDown_addsRampDownAfterVibrationCompleted() { + int rampDownDuration = 15; + mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), + new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.createWaveform( + new long[]{5, 5, 5}, new int[]{60, 120, 240}, -1); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + + // Duration extended for 5 + 5 + 5 + 15. + assertEquals(Arrays.asList(expectedOneShot(30)), + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes(); + assertTrue(amplitudes.size() > 3); + assertEquals(expectedAmplitudes(60, 120, 240), amplitudes.subList(0, 3)); + for (int i = 3; i < amplitudes.size(); i++) { + assertTrue(amplitudes.get(i) < amplitudes.get(i - 1)); + } + } + + @Test + public void vibrate_waveformWithRampDown_triggersCallbackWhenOriginalVibrationEnds() { + int rampDownDuration = 10_000; + mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), + new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.createOneShot(10, 200); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + + // Vibration completed but vibrator not yet released. + verify(mThreadCallbacks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId), + eq(Vibration.Status.FINISHED)); + verify(mThreadCallbacks, never()).onVibratorsReleased(); + + // Thread still running ramp down. + assertTrue(thread.isAlive()); + + // Duration extended for 10 + 10000. + assertEquals(Arrays.asList(expectedOneShot(10_010)), + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + + // Will stop the ramp down right away. + thread.cancelImmediately(); + waitForCompletion(thread); + + // Does not cancel already finished vibration, but releases vibrator. + verify(mThreadCallbacks, never()).onVibrationCompleted(eq(vibrationId), + eq(Vibration.Status.CANCELLED)); + verify(mThreadCallbacks).onVibratorsReleased(); + } + + @Test + public void vibrate_waveformCancelledWithRampDown_addsRampDownAfterVibrationCancelled() + throws Exception { + int rampDownDuration = 15; + mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), + new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), thread, + TEST_TIMEOUT_MILLIS)); + thread.cancel(); + waitForCompletion(thread); + + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED); + + // Duration extended for 10000 + 15. + assertEquals(Arrays.asList(expectedOneShot(10_015)), + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes(); + assertTrue(amplitudes.size() > 1); + for (int i = 1; i < amplitudes.size(); i++) { + assertTrue(amplitudes.get(i) < amplitudes.get(i - 1)); + } + } + + @Test + public void vibrate_predefinedWithRampDown_doesNotAddRampDown() { + int rampDownDuration = 15; + mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), + new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK); + + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + + assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)), + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty()); + } + + @Test + public void vibrate_composedWithRampDown_doesNotAddRampDown() { + int rampDownDuration = 15; + mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), + new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL, + IVibrator.CAP_COMPOSE_EFFECTS); + mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives( + VibrationEffect.Composition.PRIMITIVE_CLICK); + + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + + assertEquals( + Arrays.asList(expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)), + mVibratorProviders.get(VIBRATOR_ID).getEffectSegments()); + assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty()); + } + + @Test + public void vibrate_pwleWithRampDown_doesNotAddRampDown() { + int rampDownDuration = 15; + mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), + new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID); + fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL, + IVibrator.CAP_COMPOSE_PWLE_EFFECTS); + fakeVibrator.setMinFrequency(100); + fakeVibrator.setResonantFrequency(150); + fakeVibrator.setFrequencyResolution(50); + fakeVibrator.setMaxAmplitudes(1, 1, 1); + fakeVibrator.setPwleSizeMax(2); + + long vibrationId = 1; + VibrationEffect effect = VibrationEffect.startWaveform().addRamp(1, 1).build(); + VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); + waitForCompletion(thread); + + verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId)); + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + + assertEquals(Arrays.asList(expectedRamp(0, 1, 150, 150, 1)), + fakeVibrator.getEffectSegments()); + assertTrue(fakeVibrator.getAmplitudes().isEmpty()); + } + private void mockVibrators(int... vibratorIds) { for (int vibratorId : vibratorIds) { mVibratorProviders.put(vibratorId, @@ -1042,7 +1209,7 @@ public class VibrationThreadTest { } private VibrationThread startThreadAndDispatcher(Vibration vib) { - VibrationThread thread = new VibrationThread(vib, mEffectAdapter, + VibrationThread thread = new VibrationThread(vib, mVibrationSettings, mEffectAdapter, createVibratorControllers(), mWakeLock, mIBatteryStatsMock, mThreadCallbacks); doAnswer(answer -> { thread.vibratorComplete(answer.getArgument(0)); @@ -1117,4 +1284,9 @@ public class VibrationThreadTest { .mapToObj(amplitude -> amplitude / 255f) .collect(Collectors.toList()); } + + private void verifyCallbacksTriggered(long vibrationId, Vibration.Status expectedStatus) { + verify(mThreadCallbacks).onVibrationCompleted(eq(vibrationId), eq(expectedStatus)); + verify(mThreadCallbacks).onVibratorsReleased(); + } } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java index 9117ae696e9f..77003b2e091a 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -67,6 +67,7 @@ import android.os.VibratorInfo; import android.os.test.TestLooper; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; +import android.os.vibrator.VibrationEffectSegment; import android.platform.test.annotations.Presubmit; import android.provider.Settings; import android.view.InputDevice; @@ -470,13 +471,14 @@ public class VibratorManagerServiceTest { mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM); - fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK, + VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK); setRingerMode(AudioManager.RINGER_MODE_NORMAL); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0); VibratorManagerService service = createSystemReadyService(); - vibrate(service, VibrationEffect.createOneShot(1, 1), RINGTONE_ATTRS); + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS); // Wait before checking it never played. assertFalse(waitUntil(s -> !fakeVibrator.getEffectSegments().isEmpty(), service, /* timeout= */ 50)); @@ -484,43 +486,52 @@ public class VibratorManagerServiceTest { setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1); service = createSystemReadyService(); - vibrate(service, VibrationEffect.createOneShot(1, 10), RINGTONE_ATTRS); + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS); assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1, service, TEST_TIMEOUT_MILLIS)); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0); service = createSystemReadyService(); - vibrate(service, VibrationEffect.createOneShot(1, 100), RINGTONE_ATTRS); + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS); assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2, service, TEST_TIMEOUT_MILLIS)); - assertEquals(Arrays.asList(10 / 255f, 100 / 255f), - mVibratorProviders.get(1).getAmplitudes()); + assertEquals( + Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK), + expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)), + mVibratorProviders.get(1).getEffectSegments()); } @Test public void vibrate_withPowerMode_usesPowerModeState() throws Exception { mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); - fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_CLICK, + VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK); VibratorManagerService service = createSystemReadyService(); mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); - vibrate(service, VibrationEffect.createOneShot(1, 1), HAPTIC_FEEDBACK_ATTRS); - vibrate(service, VibrationEffect.createOneShot(2, 2), RINGTONE_ATTRS); + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), HAPTIC_FEEDBACK_ATTRS); + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS); assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1, service, TEST_TIMEOUT_MILLIS)); mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); - vibrate(service, VibrationEffect.createOneShot(3, 3), /* attributes= */ null); + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), + /* attrs= */ null); assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2, service, TEST_TIMEOUT_MILLIS)); - vibrate(service, VibrationEffect.createOneShot(4, 4), NOTIFICATION_ATTRS); + vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), + NOTIFICATION_ATTRS); assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3, service, TEST_TIMEOUT_MILLIS)); - assertEquals(Arrays.asList(2 / 255f, 3 / 255f, 4 / 255f), fakeVibrator.getAmplitudes()); + assertEquals( + Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK), + expectedPrebaked(VibrationEffect.EFFECT_HEAVY_CLICK), + expectedPrebaked(VibrationEffect.EFFECT_DOUBLE_CLICK)), + mVibratorProviders.get(1).getEffectSegments()); } @Test @@ -836,7 +847,6 @@ public class VibratorManagerServiceTest { vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS); assertEquals(4, fakeVibrator.getEffectSegments().size()); - assertEquals(1, fakeVibrator.getAmplitudes().size()); // Notification vibrations will be scaled with SCALE_VERY_HIGH. assertTrue(0.6 < fakeVibrator.getAmplitudes().get(0)); @@ -957,6 +967,10 @@ public class VibratorManagerServiceTest { assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); } + private VibrationEffectSegment expectedPrebaked(int effectId) { + return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM); + } + private void mockCapabilities(long... capabilities) { when(mNativeWrapperMock.getCapabilities()).thenReturn( Arrays.stream(capabilities).reduce(0, (a, b) -> a | b)); |