diff options
455 files changed, 16910 insertions, 3461 deletions
diff --git a/Android.bp b/Android.bp index 21054ddd89c6..6a85f62a5ae6 100644 --- a/Android.bp +++ b/Android.bp @@ -161,6 +161,7 @@ java_defaults { ":libcamera_client_framework_aidl", "core/java/android/hardware/IConsumerIrService.aidl", "core/java/android/hardware/ISerialManager.aidl", + "core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl", "core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl", "core/java/android/hardware/biometrics/IBiometricService.aidl", "core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl", diff --git a/api/current.txt b/api/current.txt index 5997043cdd4a..01edc71191f8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5335,6 +5335,7 @@ package android.app { field public static final String EXTRA_TITLE = "android.title"; field public static final String EXTRA_TITLE_BIG = "android.title.big"; field public static final int FLAG_AUTO_CANCEL = 16; // 0x10 + field public static final int FLAG_BUBBLE = 4096; // 0x1000 field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40 field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200 field @Deprecated public static final int FLAG_HIGH_PRIORITY = 128; // 0x80 @@ -5480,7 +5481,8 @@ package android.app { method @DimenRes public int getDesiredHeightResId(); method @NonNull public android.graphics.drawable.Icon getIcon(); method @NonNull public android.app.PendingIntent getIntent(); - method public boolean getSuppressInitialNotification(); + method @Deprecated public boolean getSuppressInitialNotification(); + method public boolean getSuppressNotification(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.Notification.BubbleMetadata> CREATOR; } @@ -5494,7 +5496,8 @@ package android.app { method @NonNull public android.app.Notification.BubbleMetadata.Builder setDesiredHeightResId(@DimenRes int); method @NonNull public android.app.Notification.BubbleMetadata.Builder setIcon(@NonNull android.graphics.drawable.Icon); method @NonNull public android.app.Notification.BubbleMetadata.Builder setIntent(@NonNull android.app.PendingIntent); - method @NonNull public android.app.Notification.BubbleMetadata.Builder setSuppressInitialNotification(boolean); + method @Deprecated @NonNull public android.app.Notification.BubbleMetadata.Builder setSuppressInitialNotification(boolean); + method @NonNull public android.app.Notification.BubbleMetadata.Builder setSuppressNotification(boolean); } public static class Notification.Builder { @@ -9515,7 +9518,7 @@ package android.content { method public static android.content.SyncAdapterType[] getSyncAdapterTypes(); method public static boolean getSyncAutomatically(android.accounts.Account, String); method @Nullable public final String getType(@NonNull android.net.Uri); - method @NonNull public final android.content.ContentResolver.TypeInfo getTypeInfo(@NonNull String); + method @NonNull public final android.content.ContentResolver.MimeTypeInfo getTypeInfo(@NonNull String); method @Nullable public final android.net.Uri insert(@RequiresPermission.Write @NonNull android.net.Uri, @Nullable android.content.ContentValues); method public static boolean isSyncActive(android.accounts.Account, String); method public static boolean isSyncPending(android.accounts.Account, String); @@ -9595,7 +9598,7 @@ package android.content { field public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1; // 0x1 } - public static final class ContentResolver.TypeInfo { + public static final class ContentResolver.MimeTypeInfo { method @NonNull public CharSequence getContentDescription(); method @NonNull public android.graphics.drawable.Icon getIcon(); method @NonNull public CharSequence getLabel(); @@ -10307,6 +10310,7 @@ package android.content { field public static final String CATEGORY_APP_CALENDAR = "android.intent.category.APP_CALENDAR"; field public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS"; field public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL"; + field public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES"; field public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY"; field public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS"; field public static final String CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET"; @@ -23077,8 +23081,8 @@ package android.media { method @NonNull public android.media.AudioAttributes.Builder setAllowedCapturePolicy(int); method public android.media.AudioAttributes.Builder setContentType(int); method public android.media.AudioAttributes.Builder setFlags(int); + method @NonNull public android.media.AudioAttributes.Builder setHapticChannelsMuted(boolean); method public android.media.AudioAttributes.Builder setLegacyStreamType(int); - method public android.media.AudioAttributes.Builder setMuteHapticChannels(boolean); method public android.media.AudioAttributes.Builder setUsage(int); } @@ -38477,8 +38481,8 @@ package android.provider { public final class MediaStore { ctor public MediaStore(); - method @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(@NonNull android.content.Context); method @Nullable public static android.net.Uri getDocumentUri(@NonNull android.content.Context, @NonNull android.net.Uri); + method @NonNull public static java.util.Set<java.lang.String> getExternalVolumeNames(@NonNull android.content.Context); method public static android.net.Uri getMediaScannerUri(); method @Nullable public static android.net.Uri getMediaUri(@NonNull android.content.Context, @NonNull android.net.Uri); method @NonNull public static String getVersion(@NonNull android.content.Context); @@ -38522,6 +38526,7 @@ package android.provider { field public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE = "android.media.still_image_camera_preview_service"; field public static final String UNKNOWN_STRING = "<unknown>"; field public static final String VOLUME_EXTERNAL = "external"; + field public static final String VOLUME_EXTERNAL_PRIMARY = "external_primary"; field public static final String VOLUME_INTERNAL = "internal"; } @@ -38536,6 +38541,7 @@ package android.provider { field public static final String ALBUM_ID = "album_id"; field public static final String ALBUM_KEY = "album_key"; field public static final String ARTIST = "artist"; + field public static final String ARTIST_ID = "artist_id"; field public static final String FIRST_YEAR = "minyear"; field public static final String LAST_YEAR = "maxyear"; field public static final String NUMBER_OF_SONGS = "numsongs"; @@ -38769,6 +38775,7 @@ package android.provider { field public static final String RELATIVE_PATH = "relative_path"; field public static final String SIZE = "_size"; field public static final String TITLE = "title"; + field public static final String VOLUME_NAME = "volume_name"; field public static final String WIDTH = "width"; } @@ -44380,9 +44387,9 @@ package android.telephony { method public int getCid(); method public int getLac(); method @Deprecated public int getMcc(); - method public String getMccString(); + method @Nullable public String getMccString(); method @Deprecated public int getMnc(); - method public String getMncString(); + method @Nullable public String getMncString(); method @Nullable public String getMobileNetworkOperator(); method @Deprecated public int getPsc(); method public void writeToParcel(android.os.Parcel, int); @@ -44394,9 +44401,9 @@ package android.telephony { method public int getCi(); method public int getEarfcn(); method @Deprecated public int getMcc(); - method public String getMccString(); + method @Nullable public String getMccString(); method @Deprecated public int getMnc(); - method public String getMncString(); + method @Nullable public String getMncString(); method @Nullable public String getMobileNetworkOperator(); method public int getPci(); method public int getTac(); @@ -44419,8 +44426,8 @@ package android.telephony { method public int getCid(); method public int getCpid(); method public int getLac(); - method public String getMccString(); - method public String getMncString(); + method @Nullable public String getMccString(); + method @Nullable public String getMncString(); method @Nullable public String getMobileNetworkOperator(); method public int getUarfcn(); method public void writeToParcel(android.os.Parcel, int); @@ -44431,9 +44438,9 @@ package android.telephony { method public int getCid(); method public int getLac(); method @Deprecated public int getMcc(); - method public String getMccString(); + method @Nullable public String getMccString(); method @Deprecated public int getMnc(); - method public String getMncString(); + method @Nullable public String getMncString(); method @Nullable public String getMobileNetworkOperator(); method public int getPsc(); method public int getUarfcn(); @@ -44456,22 +44463,22 @@ package android.telephony { } public final class CellInfoCdma extends android.telephony.CellInfo implements android.os.Parcelable { - method public android.telephony.CellIdentityCdma getCellIdentity(); - method public android.telephony.CellSignalStrengthCdma getCellSignalStrength(); + method @NonNull public android.telephony.CellIdentityCdma getCellIdentity(); + method @NonNull public android.telephony.CellSignalStrengthCdma getCellSignalStrength(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellInfoCdma> CREATOR; } public final class CellInfoGsm extends android.telephony.CellInfo implements android.os.Parcelable { - method public android.telephony.CellIdentityGsm getCellIdentity(); - method public android.telephony.CellSignalStrengthGsm getCellSignalStrength(); + method @NonNull public android.telephony.CellIdentityGsm getCellIdentity(); + method @NonNull public android.telephony.CellSignalStrengthGsm getCellSignalStrength(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellInfoGsm> CREATOR; } public final class CellInfoLte extends android.telephony.CellInfo implements android.os.Parcelable { - method public android.telephony.CellIdentityLte getCellIdentity(); - method public android.telephony.CellSignalStrengthLte getCellSignalStrength(); + method @NonNull public android.telephony.CellIdentityLte getCellIdentity(); + method @NonNull public android.telephony.CellSignalStrengthLte getCellSignalStrength(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellInfoLte> CREATOR; } @@ -44507,7 +44514,7 @@ package android.telephony { method public abstract boolean equals(Object); method public abstract int getAsuLevel(); method public abstract int getDbm(); - method public abstract int getLevel(); + method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public abstract int getLevel(); method public abstract int hashCode(); field public static final int SIGNAL_STRENGTH_GOOD = 3; // 0x3 field public static final int SIGNAL_STRENGTH_GREAT = 4; // 0x4 @@ -44527,7 +44534,7 @@ package android.telephony { method public int getEvdoEcio(); method public int getEvdoLevel(); method public int getEvdoSnr(); - method public int getLevel(); + method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthCdma> CREATOR; } @@ -44537,7 +44544,7 @@ package android.telephony { method public int getAsuLevel(); method public int getBitErrorRate(); method public int getDbm(); - method public int getLevel(); + method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel(); method public int getTimingAdvance(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR; @@ -44548,7 +44555,7 @@ package android.telephony { method public int getAsuLevel(); method public int getCqi(); method public int getDbm(); - method public int getLevel(); + method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel(); method public int getRsrp(); method public int getRsrq(); method public int getRssi(); @@ -44565,7 +44572,7 @@ package android.telephony { method public int getCsiRsrq(); method public int getCsiSinr(); method public int getDbm(); - method public int getLevel(); + method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel(); method public int getSsRsrp(); method public int getSsRsrq(); method public int getSsSinr(); @@ -44577,7 +44584,7 @@ package android.telephony { method public int describeContents(); method public int getAsuLevel(); method public int getDbm(); - method public int getLevel(); + method @IntRange(from=0, to=4) public int getLevel(); method public int getRscp(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthTdscdma> CREATOR; @@ -44587,7 +44594,7 @@ package android.telephony { method public int describeContents(); method public int getAsuLevel(); method public int getDbm(); - method public int getLevel(); + method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthWcdma> CREATOR; } @@ -51671,7 +51678,7 @@ package android.view { method public void addOnGlobalLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener); method public void addOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener); method public void addOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener); - method public void addOnSystemGestureExclusionRectsChangedListener(java.util.function.Consumer<java.util.List<android.graphics.Rect>>); + method public void addOnSystemGestureExclusionRectsChangedListener(@NonNull java.util.function.Consumer<java.util.List<android.graphics.Rect>>); method public void addOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener); method public void addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener); method public void addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener); @@ -51686,7 +51693,7 @@ package android.view { method public void removeOnGlobalLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener); method public void removeOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener); method public void removeOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener); - method public void removeOnSystemGestureExclusionRectsChangedListener(java.util.function.Consumer<java.util.List<android.graphics.Rect>>); + method public void removeOnSystemGestureExclusionRectsChangedListener(@NonNull java.util.function.Consumer<java.util.List<android.graphics.Rect>>); method public void removeOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener); method public void removeOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener); method public void removeOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener); diff --git a/api/removed.txt b/api/removed.txt index fe3e866de682..70ff50ed40a6 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -514,6 +514,7 @@ package android.provider { public final class MediaStore { method @Deprecated @NonNull public static android.net.Uri createPending(@NonNull android.content.Context, @NonNull android.provider.MediaStore.PendingParams); + method @Deprecated @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(@NonNull android.content.Context); method @Deprecated @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri); method @Deprecated @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri); method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri); diff --git a/api/system-current.txt b/api/system-current.txt index 76b8f6610590..c0da879241ab 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -553,7 +553,7 @@ package android.app { } public class NotificationManager { - method @NonNull public java.util.List<java.lang.String> getAllowedAssistantCapabilities(); + method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments(); method @Nullable public android.content.ComponentName getAllowedNotificationAssistant(); method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); @@ -1242,30 +1242,29 @@ package android.app.usage { package android.bluetooth { public final class BluetoothAdapter { + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener); method public boolean disableBLE(); method public boolean enableBLE(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect(); method public boolean isBleScanAlwaysAvailable(); method public boolean isLeEnabled(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean registerMetadataListener(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothAdapter.MetadataListener, android.os.Handler); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean unregisterMetadataListener(android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener); field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE"; } - public abstract static class BluetoothAdapter.MetadataListener { - ctor public BluetoothAdapter.MetadataListener(); - method public void onMetadataChanged(android.bluetooth.BluetoothDevice, int, String); + public static interface BluetoothAdapter.OnMetadataChangedListener { + method public void onMetadataChanged(@NonNull android.bluetooth.BluetoothDevice, int, @Nullable byte[]); } public final class BluetoothDevice implements android.os.Parcelable { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public String getMetadata(int); + method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int); method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, String); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, @NonNull byte[]); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSilenceMode(boolean); field public static final int ACCESS_ALLOWED = 1; // 0x1 @@ -1275,21 +1274,21 @@ package android.bluetooth { field public static final int METADATA_COMPANION_APP = 4; // 0x4 field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10 field public static final int METADATA_HARDWARE_VERSION = 3; // 0x3 - field public static final int METADATA_IS_UNTHETHERED_HEADSET = 6; // 0x6 + field public static final int METADATA_IS_UNTETHERED_HEADSET = 6; // 0x6 field public static final int METADATA_MAIN_ICON = 5; // 0x5 field public static final int METADATA_MANUFACTURER_NAME = 0; // 0x0 field public static final int METADATA_MAX_LENGTH = 2048; // 0x800 field public static final int METADATA_MODEL_NAME = 1; // 0x1 field public static final int METADATA_SOFTWARE_VERSION = 2; // 0x2 - field public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12; // 0xc - field public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15; // 0xf - field public static final int METADATA_UNTHETHERED_CASE_ICON = 9; // 0x9 - field public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10; // 0xa - field public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13; // 0xd - field public static final int METADATA_UNTHETHERED_LEFT_ICON = 7; // 0x7 - field public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11; // 0xb - field public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14; // 0xe - field public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8; // 0x8 + field public static final int METADATA_UNTETHERED_CASE_BATTERY = 12; // 0xc + field public static final int METADATA_UNTETHERED_CASE_CHARGING = 15; // 0xf + field public static final int METADATA_UNTETHERED_CASE_ICON = 9; // 0x9 + field public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10; // 0xa + field public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13; // 0xd + field public static final int METADATA_UNTETHERED_LEFT_ICON = 7; // 0x7 + field public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11; // 0xb + field public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14; // 0xe + field public static final int METADATA_UNTETHERED_RIGHT_ICON = 8; // 0x8 } public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile { @@ -6631,8 +6630,8 @@ package android.service.notification { method public final void adjustNotification(@NonNull android.service.notification.Adjustment); method public final void adjustNotifications(@NonNull java.util.List<android.service.notification.Adjustment>); method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int); + method public void onAllowedAdjustmentsChanged(); method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); - method public void onCapabilitiesChanged(); method public void onNotificationDirectReplied(@NonNull String); method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification); method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel); diff --git a/api/test-current.txt b/api/test-current.txt index f76881d19e93..c3215a6867ae 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -14,6 +14,7 @@ package android { field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; + field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS"; field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS"; field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG"; field public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE"; @@ -328,8 +329,14 @@ package android.app { } public class NotificationManager { + method public void allowAssistantAdjustment(String); + method public void disallowAssistantAdjustment(String); + method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments(); + method @Nullable public android.content.ComponentName getAllowedNotificationAssistant(); method public android.content.ComponentName getEffectsSuppressor(); + method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public boolean matchesCallFilter(android.os.Bundle); + method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); } public final class PictureInPictureParams implements android.os.Parcelable { @@ -2454,10 +2461,49 @@ package android.service.contentcapture { package android.service.notification { + public final class Adjustment implements android.os.Parcelable { + ctor public Adjustment(String, String, android.os.Bundle, CharSequence, int); + ctor public Adjustment(@NonNull String, @NonNull String, @NonNull android.os.Bundle, @NonNull CharSequence, @NonNull android.os.UserHandle); + method public int describeContents(); + method @NonNull public CharSequence getExplanation(); + method @NonNull public String getKey(); + method @NonNull public String getPackage(); + method @NonNull public android.os.Bundle getSignals(); + method public int getUser(); + method @NonNull public android.os.UserHandle getUserHandle(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR; + field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions"; + field public static final String KEY_IMPORTANCE = "key_importance"; + field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria"; + field public static final String KEY_TEXT_REPLIES = "key_text_replies"; + field public static final String KEY_USER_SENTIMENT = "key_user_sentiment"; + } + @Deprecated public abstract class ConditionProviderService extends android.app.Service { method @Deprecated public boolean isBound(); } + public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService { + ctor public NotificationAssistantService(); + method public final void adjustNotification(@NonNull android.service.notification.Adjustment); + method public final void adjustNotifications(@NonNull java.util.List<android.service.notification.Adjustment>); + method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int); + method public void onAllowedAdjustmentsChanged(); + method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); + method public void onNotificationDirectReplied(@NonNull String); + method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification); + method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel); + method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean); + method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String); + method public void onNotificationsSeen(@NonNull java.util.List<java.lang.String>); + method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int); + method public final void unsnoozeNotification(@NonNull String); + field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; + field public static final int SOURCE_FROM_APP = 0; // 0x0 + field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1 + } + public abstract class NotificationListenerService extends android.app.Service { method public void onNotificationRemoved(@NonNull android.service.notification.StatusBarNotification, @NonNull android.service.notification.NotificationListenerService.RankingMap, @NonNull android.service.notification.NotificationStats, int); } @@ -2653,6 +2699,7 @@ package android.telephony { public class TelephonyManager { method public int checkCarrierPrivilegesForPackage(String); method public int getCarrierIdListVersion(); + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag(); method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile(); method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String); @@ -2835,31 +2882,6 @@ package android.util.proto { method public void writeRawZigZag64(long); } - public final class ProtoInputStream extends android.util.proto.ProtoStream { - ctor public ProtoInputStream(java.io.InputStream, int); - ctor public ProtoInputStream(java.io.InputStream); - ctor public ProtoInputStream(byte[]); - method public int decodeZigZag32(int); - method public long decodeZigZag64(long); - method public String dumpDebugData(); - method public void end(long); - method public int getFieldNumber(); - method public int getOffset(); - method public int getWireType(); - method public boolean isNextField(long) throws java.io.IOException; - method public int nextField() throws java.io.IOException; - method public boolean readBoolean(long) throws java.io.IOException; - method public byte[] readBytes(long) throws java.io.IOException; - method public double readDouble(long) throws java.io.IOException; - method public float readFloat(long) throws java.io.IOException; - method public int readInt(long) throws java.io.IOException; - method public long readLong(long) throws java.io.IOException; - method public String readString(long) throws java.io.IOException; - method public void skip() throws java.io.IOException; - method public long start(long) throws java.io.IOException; - field public static final int NO_MORE_FIELDS = -1; // 0xffffffff - } - public final class ProtoOutputStream extends android.util.proto.ProtoStream { ctor public ProtoOutputStream(); ctor public ProtoOutputStream(int); diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 017cb6d9221e..15d248f06c7c 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -235,6 +235,7 @@ cc_test { "tests/condition/CombinationConditionTracker_test.cpp", "tests/condition/SimpleConditionTracker_test.cpp", "tests/condition/StateTracker_test.cpp", + "tests/condition/ConditionTimer_test.cpp", "tests/metrics/OringDurationTracker_test.cpp", "tests/metrics/MaxDurationTracker_test.cpp", "tests/metrics/CountMetricProducer_test.cpp", diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index f7608f5320e8..90ba7ce0c812 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -176,23 +176,38 @@ message Atom { FlagFlipUpdateOccurred flag_flip_update_occurred = 101; BinaryPushStateChanged binary_push_state_changed = 102; DevicePolicyEvent device_policy_event = 103; - DocsUIFileOperationCanceledReported docs_ui_file_op_canceled = 104; - DocsUIFileOperationCopyMoveModeReported docs_ui_file_op_copy_move_mode_reported = 105; - DocsUIFileOperationFailureReported docs_ui_file_op_failure = 106; - DocsUIFileOperationReported docs_ui_provider_file_op = 107; - DocsUIInvalidScopedAccessRequestReported docs_ui_invalid_scoped_access_request = 108; - DocsUILaunchReported docs_ui_launch_reported = 109; - DocsUIRootVisitedReported docs_ui_root_visited = 110; - DocsUIStartupMsReported docs_ui_startup_ms = 111; - DocsUIUserActionReported docs_ui_user_action_reported = 112; + DocsUIFileOperationCanceledReported docs_ui_file_op_canceled = + 104 [(log_from_module) = "docsui"]; + DocsUIFileOperationCopyMoveModeReported + docs_ui_file_op_copy_move_mode_reported = + 105 [(log_from_module) = "docsui"]; + DocsUIFileOperationFailureReported docs_ui_file_op_failure = + 106 [(log_from_module) = "docsui"]; + DocsUIFileOperationReported docs_ui_provider_file_op = + 107 [(log_from_module) = "docsui"]; + DocsUIInvalidScopedAccessRequestReported + docs_ui_invalid_scoped_access_request = + 108 [(log_from_module) = "docsui"]; + DocsUILaunchReported docs_ui_launch_reported = + 109 [(log_from_module) = "docsui"]; + DocsUIRootVisitedReported docs_ui_root_visited = + 110 [(log_from_module) = "docsui"]; + DocsUIStartupMsReported docs_ui_startup_ms = + 111 [(log_from_module) = "docsui"]; + DocsUIUserActionReported docs_ui_user_action_reported = + 112 [(log_from_module) = "docsui"]; WifiEnabledStateChanged wifi_enabled_state_changed = 113; WifiRunningStateChanged wifi_running_state_changed = 114; AppCompacted app_compacted = 115; NetworkDnsEventReported network_dns_event_reported = 116 [(log_from_module) = "resolv"]; - DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported = 117; - DocsUIPickResultReported docs_ui_pick_result_reported = 118; - DocsUISearchModeReported docs_ui_search_mode_reported = 119; - DocsUISearchTypeReported docs_ui_search_type_reported = 120; + DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported = + 117 [(log_from_module) = "docsui"]; + DocsUIPickResultReported docs_ui_pick_result_reported = + 118 [(log_from_module) = "docsui"]; + DocsUISearchModeReported docs_ui_search_mode_reported = + 119 [(log_from_module) = "docsui"]; + DocsUISearchTypeReported docs_ui_search_type_reported = + 120 [(log_from_module) = "docsui"]; DataStallEvent data_stall_event = 121; RescuePartyResetReported rescue_party_reset_reported = 122; SignedConfigReported signed_config_reported = 123; @@ -3251,13 +3266,12 @@ message BinaryPushStateChanged { INSTALLER_ROLLBACK_BOOT_TRIGGERED_FAILURE = 14; INSTALLER_ROLLBACK_SUCCESS = 15; INSTALLER_ROLLBACK_FAILURE = 16; - INSTALLER_ROLLBACK_CANCEL_STAGED_REMOVE_FROM_QUEUE = 17; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_INITIATED = 18; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_SUCCESS = 19; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_FAILURE = 20; - INSTALL_STAGED_CANCEL_REQUESTED = 21; - INSTALL_STAGED_CANCEL_SUCCESS = 22; - INSTALL_STAGED_CANCEL_FAILURE = 23; + INSTALLER_ROLLBACK_STAGED_CANCEL_REQUESTED = 17; + INSTALLER_ROLLBACK_STAGED_CANCEL_SUCCESS = 18; + INSTALLER_ROLLBACK_STAGED_CANCEL_FAILURE = 19; + INSTALL_STAGED_CANCEL_REQUESTED = 20; + INSTALL_STAGED_CANCEL_SUCCESS = 21; + INSTALL_STAGED_CANCEL_FAILURE = 22; } optional State state = 6; // Possible experiment ids for monitoring this push. @@ -5748,13 +5762,12 @@ message TrainInfo { INSTALLER_ROLLBACK_BOOT_TRIGGERED_FAILURE = 14; INSTALLER_ROLLBACK_SUCCESS = 15; INSTALLER_ROLLBACK_FAILURE = 16; - INSTALLER_ROLLBACK_CANCEL_STAGED_REMOVE_FROM_QUEUE = 17; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_INITIATED = 18; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_SUCCESS = 19; - INSTALLER_ROLLBACK_CANCEL_STAGED_DELETE_SESSION_FAILURE = 20; - INSTALL_STAGED_CANCEL_REQUESTED = 21; - INSTALL_STAGED_CANCEL_SUCCESS = 22; - INSTALL_STAGED_CANCEL_FAILURE = 23; + INSTALLER_ROLLBACK_STAGED_CANCEL_REQUESTED = 17; + INSTALLER_ROLLBACK_STAGED_CANCEL_SUCCESS = 18; + INSTALLER_ROLLBACK_STAGED_CANCEL_FAILURE = 19; + INSTALL_STAGED_CANCEL_REQUESTED = 20; + INSTALL_STAGED_CANCEL_SUCCESS = 21; + INSTALL_STAGED_CANCEL_FAILURE = 22; } optional Status status = 4; } diff --git a/cmds/statsd/src/condition/ConditionTimer.h b/cmds/statsd/src/condition/ConditionTimer.h new file mode 100644 index 000000000000..442bc11934fe --- /dev/null +++ b/cmds/statsd/src/condition/ConditionTimer.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <gtest/gtest_prod.h> +#include <stdint.h> + +namespace android { +namespace os { +namespace statsd { + +/** + * A simple stopwatch to time the duration of condition being true. + * + * The owner of the stopwatch (MetricProducer) is responsible to notify the stopwatch when condition + * changes (start/pause), and when to start a new bucket (a new lap basically). All timestamps + * should be elapsedRealTime in nano seconds. + * + * Keep the timer simple and inline everything. This class is *NOT* thread safe. Caller is + * responsible for thread safety. + */ +class ConditionTimer { +public: + explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) { + if (initCondition) { + mLastConditionTrueTimestampNs = bucketStartNs; + } + }; + + // Tracks how long the condition has been stayed true in the *current* bucket. + // When a new bucket is created, this value will be reset to 0. + int64_t mTimerNs = 0; + + // Last elapsed real timestamp when condition turned to true + // When a new bucket is created and the condition is true, then the timestamp is set + // to be the bucket start timestamp. + int64_t mLastConditionTrueTimestampNs = 0; + + bool mCondition = false; + + int64_t newBucketStart(int64_t nextBucketStartNs) { + if (mCondition) { + mTimerNs += (nextBucketStartNs - mLastConditionTrueTimestampNs); + mLastConditionTrueTimestampNs = nextBucketStartNs; + } + + int64_t temp = mTimerNs; + mTimerNs = 0; + return temp; + } + + void onConditionChanged(bool newCondition, int64_t timestampNs) { + if (newCondition == mCondition) { + return; + } + mCondition = newCondition; + if (newCondition) { + mLastConditionTrueTimestampNs = timestampNs; + } else { + mTimerNs += (timestampNs - mLastConditionTrueTimestampNs); + } + } + + FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False); + FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_True); +}; + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 13eee5da52cf..d6411a748081 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -276,7 +276,8 @@ bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) { } bool StatsPullerManager::PullerForMatcherExists(int tagId) const { - return kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end(); + // Vendor pulled atoms might be registered after we parse the config. + return isVendorPulledAtom(tagId) || kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end(); } void StatsPullerManager::updateAlarmLocked() { @@ -449,9 +450,8 @@ void StatsPullerManager::RegisterPullerCallback(int32_t atomTag, const sp<IStatsPullerCallback>& callback) { AutoMutex _l(mLock); // Platform pullers cannot be changed. - if (atomTag < StatsdStats::kMaxPlatformAtomTag) { - VLOG("RegisterPullerCallback: atom tag %d is less than min tag %d", - atomTag, StatsdStats::kMaxPlatformAtomTag); + if (!isVendorPulledAtom(atomTag)) { + VLOG("RegisterPullerCallback: atom tag %d is not vendor pulled", atomTag); return; } VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag); @@ -462,7 +462,7 @@ void StatsPullerManager::RegisterPullerCallback(int32_t atomTag, void StatsPullerManager::UnregisterPullerCallback(int32_t atomTag) { AutoMutex _l(mLock); // Platform pullers cannot be changed. - if (atomTag < StatsdStats::kMaxPlatformAtomTag) { + if (!isVendorPulledAtom(atomTag)) { return; } StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 88ecccc32407..53f12acd3b84 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -160,6 +160,12 @@ public: // Max platform atom tag number. static const int32_t kMaxPlatformAtomTag = 100000; + // Vendor pulled atom start id. + static const int32_t kVendorPulledAtomStartTag = 150000; + + // Max accepted atom id. + static const int32_t kMaxAtomTag = 200000; + static const int64_t kInt64Max = 0x7fffffffffffffffLL; /** diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 18bfdfc46399..90a4e8b90051 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -72,6 +72,7 @@ const int FIELD_ID_VALUES = 9; const int FIELD_ID_BUCKET_NUM = 4; const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5; const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; +const int FIELD_ID_CONDITION_TRUE_NS = 10; const Value ZERO_LONG((int64_t)0); const Value ZERO_DOUBLE((int64_t)0); @@ -107,7 +108,8 @@ ValueMetricProducer::ValueMetricProducer( mCurrentBucketIsInvalid(false), mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC : StatsdStats::kPullMaxDelayNs), - mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()) { + mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()), + mConditionTimer(mCondition == ConditionState::kTrue, timeBaseNs) { int64_t bucketSizeMills = 0; if (metric.has_bucket()) { bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()); @@ -153,6 +155,7 @@ ValueMetricProducer::ValueMetricProducer( // flushIfNeeded to adjust start and end to bucket boundaries. // Adjust start for partial bucket mCurrentBucketStartTimeNs = startTimeNs; + mConditionTimer.newBucketStart(mCurrentBucketStartTimeNs); // Kicks off the puller immediately if condition is true and diff based. if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) { pullAndMatchEventsLocked(startTimeNs, mCondition); @@ -293,6 +296,11 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM, (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs))); } + // only write the condition timer value if the metric has a condition. + if (mConditionTrackerIndex >= 0) { + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS, + (long long)bucket.mConditionTrueNs); + } for (int i = 0; i < (int)bucket.valueIndex.size(); i ++) { int index = bucket.valueIndex[i]; const Value& value = bucket.values[i]; @@ -386,19 +394,19 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition, resetBase(); } mCondition = newCondition; - } else { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId); invalidateCurrentBucket(); - // Something weird happened. If we received another event if the future, the condition might + // Something weird happened. If we received another event in the future, the condition might // be wrong. mCondition = initialCondition(mConditionTrackerIndex); } // This part should alway be called. flushIfNeededLocked(eventTimeNs); + mConditionTimer.onConditionChanged(mCondition, eventTimeNs); } void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition) { @@ -799,12 +807,14 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, (int)mCurrentSlicedBucket.size()); int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs; - + // Close the current bucket. + int64_t conditionTrueDuration = mConditionTimer.newBucketStart(bucketEndTime); bool isBucketLargeEnough = bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs; if (isBucketLargeEnough && !mCurrentBucketIsInvalid) { // The current bucket is large enough to keep. for (const auto& slice : mCurrentSlicedBucket) { ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second); + bucket.mConditionTrueNs = conditionTrueDuration; // it will auto create new vector of ValuebucketInfo if the key is not found. if (bucket.valueIndex.size() > 0) { auto& bucketList = mPastBuckets[slice.first]; @@ -817,6 +827,8 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, appendToFullBucket(eventTimeNs, fullBucketEndTimeNs); initCurrentSlicedBucket(nextBucketStartTimeNs); + // Update the condition timer again, in case we skipped buckets. + mConditionTimer.newBucketStart(nextBucketStartTimeNs); mCurrentBucketNum += numBucketsForward; } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 12cec5d6dbec..0f5633732db9 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -19,12 +19,13 @@ #include <gtest/gtest_prod.h> #include <utils/threads.h> #include <list> -#include "../anomaly/AnomalyTracker.h" -#include "../condition/ConditionTracker.h" -#include "../external/PullDataReceiver.h" -#include "../external/StatsPullerManager.h" -#include "../matchers/EventMatcherWizard.h" -#include "../stats_log_util.h" +#include "anomaly/AnomalyTracker.h" +#include "condition/ConditionTimer.h" +#include "condition/ConditionTracker.h" +#include "external/PullDataReceiver.h" +#include "external/StatsPullerManager.h" +#include "matchers/EventMatcherWizard.h" +#include "stats_log_util.h" #include "MetricProducer.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" @@ -37,6 +38,9 @@ struct ValueBucket { int64_t mBucketEndNs; std::vector<int> valueIndex; std::vector<Value> values; + // If the metric has no condition, then this field is just wasted. + // When we tune statsd memory usage in the future, this is a candidate to optimize. + int64_t mConditionTrueNs; }; @@ -228,6 +232,8 @@ private: const bool mSplitBucketForAppUpgrade; + ConditionTimer mConditionTimer; + FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection); FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange); FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade); diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 1dfc433cf0e2..54ca757f5f61 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -129,6 +129,8 @@ message ValueBucketInfo { optional int64 start_bucket_elapsed_millis = 5; optional int64 end_bucket_elapsed_millis = 6; + + optional int64 condition_true_nanos = 10; } message ValueMetricData { diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index cdef87451f63..2a18e22adbb6 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -96,6 +96,10 @@ inline bool isPushedAtom(int atomId) { return atomId <= util::kMaxPushedAtomId && atomId > 1; } +inline bool isVendorPulledAtom(int atomId) { + return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag; +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/tests/condition/ConditionTimer_test.cpp b/cmds/statsd/tests/condition/ConditionTimer_test.cpp new file mode 100644 index 000000000000..ea02cd3a5ee1 --- /dev/null +++ b/cmds/statsd/tests/condition/ConditionTimer_test.cpp @@ -0,0 +1,68 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/condition/ConditionTimer.h" + +#include <gtest/gtest.h> +#include <stdio.h> + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +static int64_t time_base = 10; +static int64_t ct_start_time = 200; + +TEST(ConditionTimerTest, TestTimer_Inital_False) { + ConditionTimer timer(false, time_base); + EXPECT_EQ(false, timer.mCondition); + EXPECT_EQ(0, timer.mTimerNs); + + EXPECT_EQ(0, timer.newBucketStart(ct_start_time)); + EXPECT_EQ(0, timer.mTimerNs); + + timer.onConditionChanged(true, ct_start_time + 5); + EXPECT_EQ(ct_start_time + 5, timer.mLastConditionTrueTimestampNs); + EXPECT_EQ(true, timer.mCondition); + + EXPECT_EQ(95, timer.newBucketStart(ct_start_time + 100)); + EXPECT_EQ(ct_start_time + 100, timer.mLastConditionTrueTimestampNs); + EXPECT_EQ(true, timer.mCondition); +} + +TEST(ConditionTimerTest, TestTimer_Inital_True) { + ConditionTimer timer(true, time_base); + EXPECT_EQ(true, timer.mCondition); + EXPECT_EQ(0, timer.mTimerNs); + + EXPECT_EQ(ct_start_time - time_base, timer.newBucketStart(ct_start_time)); + EXPECT_EQ(true, timer.mCondition); + EXPECT_EQ(0, timer.mTimerNs); + EXPECT_EQ(ct_start_time, timer.mLastConditionTrueTimestampNs); + + timer.onConditionChanged(false, ct_start_time + 5); + EXPECT_EQ(5, timer.mTimerNs); + + EXPECT_EQ(5, timer.newBucketStart(ct_start_time + 100)); + EXPECT_EQ(0, timer.mTimerNs); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index afa05a93c55a..c12a59003bab 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -55,8 +55,11 @@ double epsilon = 0.001; static void assertPastBucketValuesSingleKey( const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets, - const std::initializer_list<int>& expectedValuesList) { + const std::initializer_list<int>& expectedValuesList, + const std::initializer_list<int64_t>& expectedDurationNsList) { std::vector<int> expectedValues(expectedValuesList); + std::vector<int64_t> expectedDurationNs(expectedDurationNsList); + ASSERT_EQ(expectedValues.size(), expectedDurationNs.size()); if (expectedValues.size() == 0) { ASSERT_EQ(0, mPastBuckets.size()); return; @@ -69,10 +72,11 @@ static void assertPastBucketValuesSingleKey( for (int i = 0; i < expectedValues.size(); i++) { EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value) << "Values differ at index " << i; + EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs) + << "Condition duration value differ at index " << i; } } - class ValueMetricProducerTestHelper { public: @@ -237,6 +241,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { EXPECT_EQ(8, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); allData.clear(); event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); @@ -256,7 +261,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size()); EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs); allData.clear(); event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); @@ -275,8 +282,11 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size()); EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs); EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs); } TEST(ValueMetricProducerTest, TestPartialBucketCreated) { @@ -326,8 +336,11 @@ TEST(ValueMetricProducerTest, TestPartialBucketCreated) { EXPECT_EQ(2UL, buckets.size()); // Full bucket (2 - 1) EXPECT_EQ(1, buckets[0].values[0].long_value); + EXPECT_EQ(bucketSizeNs, buckets[0].mConditionTrueNs); // Full bucket (5 - 3) EXPECT_EQ(3, buckets[1].values[0].long_value); + // partial bucket [bucket2StartTimeNs, bucket2StartTimeNs + 2] + EXPECT_EQ(2, buckets[1].mConditionTrueNs); } /* @@ -385,6 +398,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { EXPECT_EQ(8, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); allData.clear(); event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); @@ -402,6 +416,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { EXPECT_EQ(8, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); allData.clear(); event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); @@ -420,6 +435,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size()); EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs); } /* @@ -468,6 +484,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs); allData.clear(); event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); @@ -485,14 +502,16 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size()); EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs); } /* * Tests pulled atoms with no conditions and take zero value after reset */ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); sp<ValueMetricProducer> valueProducer = @@ -546,6 +565,7 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { EXPECT_EQ(26, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs); } /* @@ -574,6 +594,15 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { event->init(); data->push_back(event); return true; + })) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); + event->write(tagId); + event->write(180); + event->init(); + data->push_back(event); + return true; })); sp<ValueMetricProducer> valueProducer = @@ -598,7 +627,7 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { event->init(); allData.push_back(event); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}); // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); @@ -609,7 +638,7 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { EXPECT_EQ(10, curInterval.value.long_value); valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}); // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); @@ -617,6 +646,9 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(20, curInterval.value.long_value); EXPECT_EQ(false, curInterval.hasBase); + + valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1}); } TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { @@ -705,8 +737,7 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1); EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(20L, - valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150}); allData.clear(); event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); @@ -719,10 +750,12 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30}, + {150, bucketSizeNs - 150}); } TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { - ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.set_split_bucket_for_app_upgrade(false); UidMap uidMap; @@ -791,8 +824,10 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) { // Expect one full buckets already done and starting a partial bucket. EXPECT_EQ(bucket2StartTimeNs-50, valueProducer->mCurrentBucketStartTimeNs); EXPECT_EQ(1UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(20L, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); + EXPECT_EQ(bucketStartTimeNs, + valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, + {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)}); EXPECT_FALSE(valueProducer->mCondition); } @@ -835,7 +870,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { EXPECT_EQ(30, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs}); } TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { @@ -872,7 +907,8 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::Interval curInterval = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(20, curInterval.value.long_value); @@ -900,7 +936,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { EXPECT_EQ(50, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20}); } TEST(ValueMetricProducerTest, TestAnomalyDetection) { @@ -1008,7 +1044,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::Interval curInterval = + valueProducer->mCurrentSlicedBucket.begin()->second[0]; // startUpdated:true sum:0 start:11 EXPECT_EQ(true, curInterval.hasBase); @@ -1031,7 +1068,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(23, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}); // pull 3 come late. // The previous bucket gets closed with error. (Has start value 23, no ending) @@ -1050,7 +1087,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(36, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}); } /* @@ -1089,7 +1126,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::Interval curInterval = + valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(100, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -1098,7 +1136,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { // pull on bucket boundary come late, condition change happens before it valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); EXPECT_EQ(false, curInterval.hasBase); // Now the alarm is delivered. @@ -1107,7 +1145,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 110)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(false, curInterval.hasBase); EXPECT_EQ(false, curInterval.hasValue); @@ -1160,7 +1198,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::Interval curInterval = + valueProducer->mCurrentSlicedBucket.begin()->second[0]; // startUpdated:false sum:0 start:100 EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(100, curInterval.base.long_value); @@ -1169,7 +1208,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { // pull on bucket boundary come late, condition change happens before it valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(false, curInterval.hasBase); @@ -1177,7 +1216,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { // condition changed to true again, before the pull alarm is delivered valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(130, curInterval.base.long_value); @@ -1194,12 +1233,13 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { EXPECT_EQ(140, curInterval.base.long_value); EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(10, curInterval.value.long_value); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); allData.clear(); allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 160)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30}, + {bucketSizeNs - 8, bucketSizeNs - 24}); } TEST(ValueMetricProducerTest, TestPushedAggregateMin) { @@ -1230,7 +1270,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::Interval curInterval = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1242,7 +1283,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) { EXPECT_EQ(10, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs}); } TEST(ValueMetricProducerTest, TestPushedAggregateMax) { @@ -1273,7 +1314,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::Interval curInterval = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1335,7 +1377,9 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { valueProducer.flushIfNeededLocked(bucket2StartTimeNs); EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value - 12.5) < epsilon); + + EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value - + 12.5) < epsilon); } TEST(ValueMetricProducerTest, TestPushedAggregateSum) { @@ -1366,7 +1410,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::Interval curInterval = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1378,7 +1423,7 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) { EXPECT_EQ(25, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs}); } TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { @@ -1410,7 +1455,8 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::Interval curInterval = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(10, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); @@ -1449,7 +1495,7 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { valueProducer.flushIfNeededLocked(bucket3StartTimeNs); EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs}); } TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { @@ -1546,11 +1592,13 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second[0].values.size()); EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second[1].values.size()); + EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[0].mConditionTrueNs); EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].valueIndex[0]); EXPECT_EQ(2, valueProducer.mPastBuckets.begin()->second[0].values[1].long_value); EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].valueIndex[1]); + EXPECT_EQ(bucketSizeNs, valueProducer.mPastBuckets.begin()->second[1].mConditionTrueNs); EXPECT_EQ(3, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value); EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[1].valueIndex[0]); } @@ -1625,8 +1673,10 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); auto iterator = valueProducer->mPastBuckets.begin(); + EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); EXPECT_EQ(8, iterator->second[0].values[0].long_value); iterator++; + EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); EXPECT_EQ(4, iterator->second[0].values[0].long_value); } @@ -1795,7 +1845,7 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(8, interval1.value.long_value); EXPECT_FALSE(interval1.seenNewData); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}); auto it = valueProducer->mCurrentSlicedBucket.begin(); for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) { @@ -1810,7 +1860,7 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { EXPECT_EQ(4, interval2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_FALSE(interval2.seenNewData); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}); // next pull somehow did not happen, skip to end of bucket 3 allData.clear(); @@ -1828,7 +1878,7 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { EXPECT_EQ(5, interval2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_FALSE(interval2.seenNewData); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}); allData.clear(); event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1); @@ -1846,8 +1896,10 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); auto iterator = valueProducer->mPastBuckets.begin(); EXPECT_EQ(9, iterator->second[0].values[0].long_value); + EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); iterator++; EXPECT_EQ(8, iterator->second[0].values[0].long_value); + EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); } TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) { @@ -1932,6 +1984,15 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { EXPECT_CALL(*pullerManager, Pull(tagId, _)) .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs); + event->write(tagId); + event->write(50); + event->init(); + data->push_back(event); + return false; + })) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 8); event->write(tagId); event->write(100); @@ -1943,10 +2004,11 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { sp<ValueMetricProducer> valueProducer = ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer->mCondition = ConditionState::kTrue; + // Don't directly set mCondition; the real code never does that. Go through regular code path + // to avoid unexpected behaviors. + // valueProducer->mCondition = ConditionState::kTrue; + valueProducer->onConditionChanged(true, bucketStartTimeNs); - vector<shared_ptr<LogEvent>> allData; - valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); valueProducer->onConditionChanged(false, bucketStartTimeNs + 1); @@ -2406,7 +2468,7 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { EXPECT_EQ(true, valueProducer->mHasGlobalBase); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); - EXPECT_EQ(1, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1}); } TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { @@ -2539,13 +2601,15 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) { // Second onConditionChanged. .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); - data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 10, 5)); + data->push_back( + ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 10, 5)); return true; })) // Third onConditionChanged. .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); - data->push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs + 10, 7)); + data->push_back( + ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs + 10, 7)); return true; })); @@ -2572,7 +2636,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) { valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10); // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10}); } TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) { @@ -2592,7 +2656,7 @@ TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs}); } TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) { @@ -2619,7 +2683,7 @@ TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs}); } TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) { @@ -2636,7 +2700,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) { // notifyAppUpgrade. .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); - data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 2, 10)); + data->push_back( + ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 2, 10)); return true; })); @@ -2646,7 +2711,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) { valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1); // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs}); } TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { @@ -2678,6 +2743,12 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(2, curInterval.value.long_value); + + vector<shared_ptr<LogEvent>> allData; + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 1, 10)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1); + + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}); } TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) { @@ -2724,7 +2795,7 @@ TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // There was not global base available so all buckets are invalid. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}); } static StatsLogReport outputStreamToProto(ProtoOutputStream* proto) { @@ -2890,6 +2961,7 @@ TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { EXPECT_EQ(1, report.value_metrics().data_size()); EXPECT_EQ(1, report.value_metrics().data(0).bucket_info_size()); EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); + EXPECT_EQ(10, report.value_metrics().data(0).bucket_info(0).condition_true_nanos()); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 54fe65db499c..9079ace4b8a3 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -132,6 +132,7 @@ import android.widget.AdapterView; import android.widget.Toast; import android.widget.Toolbar; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; @@ -4904,6 +4905,17 @@ public class Activity extends ContextThemeWrapper mTaskDescription.setNavigationBarColor(navigationBarColor); } + final int targetSdk = getApplicationInfo().targetSdkVersion; + final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q; + if (!targetPreQ) { + mTaskDescription.setEnsureStatusBarContrastWhenTransparent(a.getBoolean( + R.styleable.ActivityTaskDescription_ensureStatusBarContrastWhenTransparent, + false)); + mTaskDescription.setEnsureNavigationBarContrastWhenTransparent(a.getBoolean( + R.styleable.ActivityTaskDescription_ensureNavigationBarContrastWhenTransparent, + true)); + } + a.recycle(); setTaskDescription(mTaskDescription); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 395c867de9d7..4f388a441aab 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -986,6 +986,8 @@ public class ActivityManager { private int mColorBackground; private int mStatusBarColor; private int mNavigationBarColor; + private boolean mEnsureStatusBarContrastWhenTransparent; + private boolean mEnsureNavigationBarContrastWhenTransparent; /** * Creates the TaskDescription to the specified values. @@ -998,7 +1000,7 @@ public class ActivityManager { */ @Deprecated public TaskDescription(String label, Bitmap icon, int colorPrimary) { - this(label, icon, 0, null, colorPrimary, 0, 0, 0); + this(label, icon, 0, null, colorPrimary, 0, 0, 0, false, false); if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) { throw new RuntimeException("A TaskDescription's primary color should be opaque"); } @@ -1014,7 +1016,7 @@ public class ActivityManager { * opaque. */ public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) { - this(label, null, iconRes, null, colorPrimary, 0, 0, 0); + this(label, null, iconRes, null, colorPrimary, 0, 0, 0, false, false); if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) { throw new RuntimeException("A TaskDescription's primary color should be opaque"); } @@ -1029,7 +1031,7 @@ public class ActivityManager { */ @Deprecated public TaskDescription(String label, Bitmap icon) { - this(label, icon, 0, null, 0, 0, 0, 0); + this(label, icon, 0, null, 0, 0, 0, 0, false, false); } /** @@ -1040,7 +1042,7 @@ public class ActivityManager { * activity. */ public TaskDescription(String label, @DrawableRes int iconRes) { - this(label, null, iconRes, null, 0, 0, 0, 0); + this(label, null, iconRes, null, 0, 0, 0, 0, false, false); } /** @@ -1049,19 +1051,21 @@ public class ActivityManager { * @param label A label and description of the current state of this activity. */ public TaskDescription(String label) { - this(label, null, 0, null, 0, 0, 0, 0); + this(label, null, 0, null, 0, 0, 0, 0, false, false); } /** * Creates an empty TaskDescription. */ public TaskDescription() { - this(null, null, 0, null, 0, 0, 0, 0); + this(null, null, 0, null, 0, 0, 0, 0, false, false); } /** @hide */ public TaskDescription(String label, Bitmap bitmap, int iconRes, String iconFilename, - int colorPrimary, int colorBackground, int statusBarColor, int navigationBarColor) { + int colorPrimary, int colorBackground, int statusBarColor, int navigationBarColor, + boolean ensureStatusBarContrastWhenTransparent, + boolean ensureNavigationBarContrastWhenTransparent) { mLabel = label; mIcon = bitmap; mIconRes = iconRes; @@ -1070,6 +1074,9 @@ public class ActivityManager { mColorBackground = colorBackground; mStatusBarColor = statusBarColor; mNavigationBarColor = navigationBarColor; + mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent; + mEnsureNavigationBarContrastWhenTransparent = + ensureNavigationBarContrastWhenTransparent; } /** @@ -1092,6 +1099,9 @@ public class ActivityManager { mColorBackground = other.mColorBackground; mStatusBarColor = other.mStatusBarColor; mNavigationBarColor = other.mNavigationBarColor; + mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent; + mEnsureNavigationBarContrastWhenTransparent = + other.mEnsureNavigationBarContrastWhenTransparent; } /** @@ -1114,6 +1124,9 @@ public class ActivityManager { if (other.mNavigationBarColor != 0) { mNavigationBarColor = other.mNavigationBarColor; } + mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent; + mEnsureNavigationBarContrastWhenTransparent = + other.mEnsureNavigationBarContrastWhenTransparent; } private TaskDescription(Parcel source) { @@ -1272,6 +1285,37 @@ public class ActivityManager { return mNavigationBarColor; } + /** + * @hide + */ + public boolean getEnsureStatusBarContrastWhenTransparent() { + return mEnsureStatusBarContrastWhenTransparent; + } + + /** + * @hide + */ + public void setEnsureStatusBarContrastWhenTransparent( + boolean ensureStatusBarContrastWhenTransparent) { + mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent; + } + + /** + * @hide + */ + public boolean getEnsureNavigationBarContrastWhenTransparent() { + return mEnsureNavigationBarContrastWhenTransparent; + } + + /** + * @hide + */ + public void setEnsureNavigationBarContrastWhenTransparent( + boolean ensureNavigationBarContrastWhenTransparent) { + mEnsureNavigationBarContrastWhenTransparent = + ensureNavigationBarContrastWhenTransparent; + } + /** @hide */ public void saveToXml(XmlSerializer out) throws IOException { if (mLabel != null) { @@ -1332,6 +1376,8 @@ public class ActivityManager { dest.writeInt(mColorBackground); dest.writeInt(mStatusBarColor); dest.writeInt(mNavigationBarColor); + dest.writeBoolean(mEnsureStatusBarContrastWhenTransparent); + dest.writeBoolean(mEnsureNavigationBarContrastWhenTransparent); if (mIconFilename == null) { dest.writeInt(0); } else { @@ -1348,6 +1394,8 @@ public class ActivityManager { mColorBackground = source.readInt(); mStatusBarColor = source.readInt(); mNavigationBarColor = source.readInt(); + mEnsureStatusBarContrastWhenTransparent = source.readBoolean(); + mEnsureNavigationBarContrastWhenTransparent = source.readBoolean(); mIconFilename = source.readInt() > 0 ? source.readString() : null; } @@ -1366,8 +1414,11 @@ public class ActivityManager { return "TaskDescription Label: " + mLabel + " Icon: " + mIcon + " IconRes: " + mIconRes + " IconFilename: " + mIconFilename + " colorPrimary: " + mColorPrimary + " colorBackground: " + mColorBackground + - " statusBarColor: " + mColorBackground + - " navigationBarColor: " + mNavigationBarColor; + " statusBarColor: " + mStatusBarColor + ( + mEnsureStatusBarContrastWhenTransparent ? " (contrast when transparent)" + : "") + " navigationBarColor: " + mNavigationBarColor + ( + mEnsureNavigationBarContrastWhenTransparent + ? " (contrast when transparent)" : ""); } } @@ -2001,7 +2052,10 @@ public class ActivityManager { @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int taskId, @MoveTaskFlags int flags, Bundle options) { try { - getTaskService().moveTaskToFront(taskId, flags, options); + ActivityThread thread = ActivityThread.currentActivityThread(); + IApplicationThread appThread = thread.getApplicationThread(); + String packageName = mContext.getPackageName(); + getTaskService().moveTaskToFront(appThread, packageName, taskId, flags, options); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -4212,7 +4266,10 @@ public class ActivityManager { */ public void moveToFront() { try { - mAppTaskImpl.moveToFront(); + ActivityThread thread = ActivityThread.currentActivityThread(); + IApplicationThread appThread = thread.getApplicationThread(); + String packageName = ActivityThread.currentPackageName(); + mAppTaskImpl.moveToFront(appThread, packageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 65f10808534f..1785d2a0843b 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -130,7 +130,8 @@ interface IActivityManager { List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType, int ignoreWindowingMode); @UnsupportedAppUsage - void moveTaskToFront(int task, int flags, in Bundle options); + void moveTaskToFront(in IApplicationThread caller, in String callingPackage, int task, + int flags, in Bundle options); @UnsupportedAppUsage int getTaskForActivity(in IBinder token, in boolean onlyRoot); ContentProviderHolder getContentProvider(in IApplicationThread caller, in String callingPackage, diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index a6b76cb0db60..7953d42514fc 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -149,7 +149,8 @@ interface IActivityTaskManager { boolean shouldUpRecreateTask(in IBinder token, in String destAffinity); boolean navigateUpTo(in IBinder token, in Intent target, int resultCode, in Intent resultData); - void moveTaskToFront(int task, int flags, in Bundle options); + void moveTaskToFront(in IApplicationThread app, in String callingPackage, int task, + int flags, in Bundle options); int getTaskForActivity(in IBinder token, in boolean onlyRoot); void finishSubActivity(in IBinder token, in String resultWho, int requestCode); ParceledListSlice getRecentTasks(int maxNum, int flags, int userId); diff --git a/core/java/android/app/IAppTask.aidl b/core/java/android/app/IAppTask.aidl index 61f6264408f8..3ce71908bbfd 100644 --- a/core/java/android/app/IAppTask.aidl +++ b/core/java/android/app/IAppTask.aidl @@ -17,6 +17,7 @@ package android.app; import android.app.ActivityManager; +import android.app.IApplicationThread; import android.content.Intent; import android.os.Bundle; @@ -25,7 +26,7 @@ interface IAppTask { void finishAndRemoveTask(); @UnsupportedAppUsage ActivityManager.RecentTaskInfo getTaskInfo(); - void moveToFront(); + void moveToFront(in IApplicationThread appThread, in String callingPackage); int startActivity(IBinder whoThread, String callingPackage, in Intent intent, String resolvedType, in Bundle options); void setExcludeFromRecents(boolean exclude); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 7884872a7ef3..b3c2429004b8 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -70,9 +70,9 @@ interface INotificationManager boolean areNotificationsEnabled(String pkg); int getPackageImportance(String pkg); - List<String> getAllowedAssistantCapabilities(String pkg); - void allowAssistantCapability(String adjustmentType); - void disallowAssistantCapability(String adjustmentType); + List<String> getAllowedAssistantAdjustments(String pkg); + void allowAssistantAdjustment(String adjustmentType); + void disallowAssistantAdjustment(String adjustmentType); boolean shouldHideSilentStatusIcons(String callingPkg); void setHideSilentStatusIcons(boolean hide); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 0ab1a85372f5..524832971864 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -618,9 +618,11 @@ public class Notification implements Parcelable public static final int FLAG_CAN_COLORIZE = 0x00000800; /** - * Bit to be bitswised-ored into the {@link #flags} field that should be - * set if this notification can be shown as a bubble. - * @hide + * Bit to be bitswised-ored into the {@link #flags} field that should be set if this + * notification is showing as a bubble. This will be set by the system if it is determined + * that your notification is allowed to be a bubble. + * + * @see {@link Notification.Builder#setBubbleMetadata(BubbleMetadata)} */ public static final int FLAG_BUBBLE = 0x00001000; @@ -3578,9 +3580,9 @@ public class Notification implements Parcelable * <p>This data will be ignored unless the notification is posted to a channel that * allows {@link NotificationChannel#canBubble() bubbles}.</p> * - * <b>Notifications with a valid and allowed bubble metadata will display in collapsed state - * outside of the notification shade on unlocked devices. When a user interacts with the - * collapsed state, the bubble intent will be invoked and displayed.</b> + * <p>Notifications allowed to bubble that have valid bubble metadata will display in + * collapsed state outside of the notification shade on unlocked devices. When a user + * interacts with the collapsed state, the bubble intent will be invoked and displayed.</p> */ @NonNull public Builder setBubbleMetadata(@Nullable BubbleMetadata data) { @@ -8555,16 +8557,16 @@ public class Notification implements Parcelable private static final int FLAG_AUTO_EXPAND_BUBBLE = 0x00000001; /** - * If set and the app creating the bubble is in the foreground, the bubble will be posted - * <b>without</b> the associated notification in the notification shade. Subsequent update - * notifications to this bubble will post a notification in the shade. + * If set and the app posting the bubble is in the foreground, the bubble will + * be posted <b>without</b> the associated notification in the notification shade. * - * <p>If the app creating the bubble is not in the foreground this flag has no effect.</p> + * <p>If the app posting the bubble is not in the foreground this flag has no effect.</p> * * <p>Generally this flag should only be set if the user has performed an action to request - * or create a bubble.</p> + * or create a bubble, or if the user has seen the content in the notification and the + * notification is no longer relevant.</p> */ - private static final int FLAG_SUPPRESS_INITIAL_NOTIFICATION = 0x00000002; + private static final int FLAG_SUPPRESS_NOTIFICATION = 0x00000002; private BubbleMetadata(PendingIntent expandIntent, PendingIntent deleteIntent, Icon icon, int height, @DimenRes int heightResId) { @@ -8643,9 +8645,20 @@ public class Notification implements Parcelable * @return whether this bubble should suppress the initial notification when it is posted. * * @see BubbleMetadata.Builder#setSuppressInitialNotification(boolean) + * @deprecated TO BE REMOVED, use {@link #getSuppressNotification()} instead. */ + @Deprecated public boolean getSuppressInitialNotification() { - return (mFlags & FLAG_SUPPRESS_INITIAL_NOTIFICATION) != 0; + return (mFlags & FLAG_SUPPRESS_NOTIFICATION) != 0; + } + + /** + * @return whether this bubble should suppress the notification when it is posted. + * + * @see BubbleMetadata.Builder#setSuppressInitialNotification(boolean) + */ + public boolean getSuppressNotification() { + return (mFlags & FLAG_SUPPRESS_NOTIFICATION) != 0; } public static final @android.annotation.NonNull Parcelable.Creator<BubbleMetadata> CREATOR = @@ -8804,11 +8817,31 @@ public class Notification implements Parcelable * * <p>Generally this flag should only be set if the user has performed an action to * request or create a bubble.</p> + * + * @deprecated TO BE REMOVED, use {@link #setSuppressNotification(boolean)} instead. */ + @Deprecated @NonNull public BubbleMetadata.Builder setSuppressInitialNotification( boolean shouldSupressNotif) { - setFlag(FLAG_SUPPRESS_INITIAL_NOTIFICATION, shouldSupressNotif); + setFlag(FLAG_SUPPRESS_NOTIFICATION, shouldSupressNotif); + return this; + } + + /** + * If set and the app posting the bubble is in the foreground, the bubble will be + * posted <b>without</b> the associated notification in the notification shade. + * + * <p>If the app posting the bubble is not in the foreground this flag has no effect. + * </p> + * + * <p>Generally this flag should only be set if the user has performed an action to + * request or create a bubble, or if the user has seen the content in the notification + * and the notification is no longer relevant.</p> + */ + @NonNull + public BubbleMetadata.Builder setSuppressNotification(boolean shouldSupressNotif) { + setFlag(FLAG_SUPPRESS_NOTIFICATION, shouldSupressNotif); return this; } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 0bec21fcda90..dd39376f80ca 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1169,6 +1169,7 @@ public class NotificationManager { * @hide */ @SystemApi + @TestApi public boolean isNotificationAssistantAccessGranted(@NonNull ComponentName assistant) { INotificationManager service = getService(); try { @@ -1204,10 +1205,37 @@ public class NotificationManager { * @hide */ @SystemApi - public @NonNull @Adjustment.Keys List<String> getAllowedAssistantCapabilities() { + @TestApi + public @NonNull @Adjustment.Keys List<String> getAllowedAssistantAdjustments() { + INotificationManager service = getService(); + try { + return service.getAllowedAssistantAdjustments(mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + @TestApi + public void allowAssistantAdjustment(String capability) { INotificationManager service = getService(); try { - return service.getAllowedAssistantCapabilities(mContext.getOpPackageName()); + service.allowAssistantAdjustment(capability); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + @TestApi + public void disallowAssistantAdjustment(String capability) { + INotificationManager service = getService(); + try { + service.disallowAssistantAdjustment(capability); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1310,6 +1338,7 @@ public class NotificationManager { * @hide */ @SystemApi + @TestApi public void setNotificationAssistantAccessGranted(@Nullable ComponentName assistant, boolean granted) { INotificationManager service = getService(); @@ -1332,6 +1361,7 @@ public class NotificationManager { /** @hide */ @SystemApi + @TestApi public @Nullable ComponentName getAllowedNotificationAssistant() { INotificationManager service = getService(); try { diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index b8a741ab2b72..31bbd16497cb 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -20,6 +20,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -37,7 +38,6 @@ import android.bluetooth.le.ScanSettings; import android.content.Context; import android.os.BatteryStats; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -61,6 +61,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -650,7 +651,7 @@ public final class BluetoothAdapter { private final Object mLock = new Object(); private final Map<LeScanCallback, ScanCallback> mLeScanClients; - private static final Map<BluetoothDevice, List<Pair<MetadataListener, Handler>>> + private static final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>> sMetadataListeners = new HashMap<>(); /** @@ -660,14 +661,15 @@ public final class BluetoothAdapter { private static final IBluetoothMetadataListener sBluetoothMetadataListener = new IBluetoothMetadataListener.Stub() { @Override - public void onMetadataChanged(BluetoothDevice device, int key, String value) { + public void onMetadataChanged(BluetoothDevice device, int key, byte[] value) { synchronized (sMetadataListeners) { if (sMetadataListeners.containsKey(device)) { - List<Pair<MetadataListener, Handler>> list = sMetadataListeners.get(device); - for (Pair<MetadataListener, Handler> pair : list) { - MetadataListener listener = pair.first; - Handler handler = pair.second; - handler.post(() -> { + List<Pair<OnMetadataChangedListener, Executor>> list = + sMetadataListeners.get(device); + for (Pair<OnMetadataChangedListener, Executor> pair : list) { + OnMetadataChangedListener listener = pair.first; + Executor executor = pair.second; + executor.execute(() -> { listener.onMetadataChanged(device, key, value); }); } @@ -3153,30 +3155,30 @@ public final class BluetoothAdapter { } /** - * Register a {@link #MetadataListener} to receive update about metadata + * Register a {@link #OnMetadataChangedListener} to receive update about metadata * changes for this {@link BluetoothDevice}. * Registration must be done when Bluetooth is ON and will last until - * {@link #unregisterMetadataListener(BluetoothDevice)} is called, even when Bluetooth + * {@link #removeOnMetadataChangedListener(BluetoothDevice)} is called, even when Bluetooth * restarted in the middle. * All input parameters should not be null or {@link NullPointerException} will be triggered. - * The same {@link BluetoothDevice} and {@link #MetadataListener} pair can only be registered - * once, double registration would cause {@link IllegalArgumentException}. + * The same {@link BluetoothDevice} and {@link #OnMetadataChangedListener} pair can only be + * registered once, double registration would cause {@link IllegalArgumentException}. * * @param device {@link BluetoothDevice} that will be registered - * @param listener {@link #MetadataListener} that will receive asynchronous callbacks - * @param handler the handler for listener callback + * @param executor the executor for listener callback + * @param listener {@link #OnMetadataChangedListener} that will receive asynchronous callbacks * @return true on success, false on error - * @throws NullPointerException If one of {@code listener}, {@code device} or {@code handler} + * @throws NullPointerException If one of {@code listener}, {@code device} or {@code executor} * is null. - * @throws IllegalArgumentException The same {@link #MetadataListener} and + * @throws IllegalArgumentException The same {@link #OnMetadataChangedListener} and * {@link BluetoothDevice} are registered twice. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean registerMetadataListener(BluetoothDevice device, MetadataListener listener, - Handler handler) { - if (DBG) Log.d(TAG, "registerMetdataListener()"); + public boolean addOnMetadataChangedListener(@NonNull BluetoothDevice device, + @NonNull Executor executor, @NonNull OnMetadataChangedListener listener) { + if (DBG) Log.d(TAG, "addOnMetadataChangedListener()"); final IBluetooth service = mService; if (service == null) { @@ -3189,14 +3191,15 @@ public final class BluetoothAdapter { if (device == null) { throw new NullPointerException("device is null"); } - if (handler == null) { - throw new NullPointerException("handler is null"); + if (executor == null) { + throw new NullPointerException("executor is null"); } synchronized (sMetadataListeners) { - List<Pair<MetadataListener, Handler>> listenerList = sMetadataListeners.get(device); + List<Pair<OnMetadataChangedListener, Executor>> listenerList = + sMetadataListeners.get(device); if (listenerList == null) { - // Create new listener/handler list for registeration + // Create new listener/executor list for registeration listenerList = new ArrayList<>(); sMetadataListeners.put(device, listenerList); } else { @@ -3207,7 +3210,7 @@ public final class BluetoothAdapter { } } - Pair<MetadataListener, Handler> listenerPair = new Pair(listener, handler); + Pair<OnMetadataChangedListener, Executor> listenerPair = new Pair(listener, executor); listenerList.add(listenerPair); boolean ret = false; @@ -3230,63 +3233,74 @@ public final class BluetoothAdapter { } /** - * Unregister all {@link MetadataListener} from this {@link BluetoothDevice}. + * Unregister a {@link #OnMetadataChangedListener} from a registered {@link BluetoothDevice}. * Unregistration can be done when Bluetooth is either ON or OFF. - * {@link #registerMetadataListener(MetadataListener, BluetoothDevice, Handler)} must - * be called before unregisteration. - * Unregistering a device that is not regestered would cause {@link IllegalArgumentException}. + * {@link #addOnMetadataChangedListener(OnMetadataChangedListener, BluetoothDevice, Executor)} + * must be called before unregisteration. * - * @param device {@link BluetoothDevice} that will be unregistered. it + * @param device {@link BluetoothDevice} that will be unregistered. It + * should not be null or {@link NullPointerException} will be triggered. + * @param listener {@link OnMetadataChangedListener} that will be unregistered. It * should not be null or {@link NullPointerException} will be triggered. * @return true on success, false on error - * @throws NullPointerException If {@code device} is null. + * @throws NullPointerException If {@code listener} or {@code device} is null. * @throws IllegalArgumentException If {@code device} has not been registered before. * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean unregisterMetadataListener(BluetoothDevice device) { - if (DBG) Log.d(TAG, "unregisterMetdataListener()"); + public boolean removeOnMetadataChangedListener(@NonNull BluetoothDevice device, + @NonNull OnMetadataChangedListener listener) { + if (DBG) Log.d(TAG, "removeOnMetadataChangedListener()"); if (device == null) { throw new NullPointerException("device is null"); } + if (listener == null) { + throw new NullPointerException("listener is null"); + } synchronized (sMetadataListeners) { - if (sMetadataListeners.containsKey(device)) { - sMetadataListeners.remove(device); - } else { + if (!sMetadataListeners.containsKey(device)) { throw new IllegalArgumentException("device was not registered"); } + // Remove issued listener from the registered device + sMetadataListeners.get(device).removeIf((pair) -> (pair.first.equals(listener))); - final IBluetooth service = mService; - if (service == null) { - // Bluetooth is OFF, do nothing to Bluetooth service. - return true; - } - try { - return service.unregisterMetadataListener(device); - } catch (RemoteException e) { - Log.e(TAG, "unregisterMetadataListener fail", e); - return false; + if (sMetadataListeners.get(device).isEmpty()) { + // Unregister to Bluetooth service if all listeners are removed from + // the registered device + sMetadataListeners.remove(device); + final IBluetooth service = mService; + if (service == null) { + // Bluetooth is OFF, do nothing to Bluetooth service. + return true; + } + try { + return service.unregisterMetadataListener(device); + } catch (RemoteException e) { + Log.e(TAG, "unregisterMetadataListener fail", e); + return false; + } } } + return true; } /** - * This abstract class is used to implement {@link BluetoothAdapter} metadata listener. + * This interface is used to implement {@link BluetoothAdapter} metadata listener. * @hide */ @SystemApi - public abstract static class MetadataListener { + public interface OnMetadataChangedListener { /** * Callback triggered if the metadata of {@link BluetoothDevice} registered in - * {@link #registerMetadataListener}. + * {@link #addOnMetadataChangedListener}. * * @param device changed {@link BluetoothDevice}. * @param key changed metadata key, one of BluetoothDevice.METADATA_*. - * @param value the new value of metadata. + * @param value the new value of metadata as byte array. */ - public void onMetadataChanged(BluetoothDevice device, int key, String value) { - } + void onMetadataChanged(@NonNull BluetoothDevice device, int key, + @Nullable byte[] value); } } diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 204d7e3ceca6..74ceeb92f751 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -351,6 +352,7 @@ public final class BluetoothDevice implements Parcelable { /** * Manufacturer name of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -358,6 +360,7 @@ public final class BluetoothDevice implements Parcelable { /** * Model name of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -365,6 +368,7 @@ public final class BluetoothDevice implements Parcelable { /** * Software version of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -372,6 +376,7 @@ public final class BluetoothDevice implements Parcelable { /** * Hardware version of this Bluetooth device + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -379,6 +384,7 @@ public final class BluetoothDevice implements Parcelable { /** * Package name of the companion app, if any + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi @@ -386,6 +392,7 @@ public final class BluetoothDevice implements Parcelable { /** * URI to the main icon shown on the settings UI + * Data type should be {@link Byte} array. * @hide */ @SystemApi @@ -393,80 +400,91 @@ public final class BluetoothDevice implements Parcelable { /** * Whether this device is an untethered headset with left, right and case + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_IS_UNTHETHERED_HEADSET = 6; + public static final int METADATA_IS_UNTETHERED_HEADSET = 6; /** * URI to icon of the left headset + * Data type should be {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_LEFT_ICON = 7; + public static final int METADATA_UNTETHERED_LEFT_ICON = 7; /** * URI to icon of the right headset + * Data type should be {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8; + public static final int METADATA_UNTETHERED_RIGHT_ICON = 8; /** * URI to icon of the headset charging case + * Data type should be {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_CASE_ICON = 9; + public static final int METADATA_UNTETHERED_CASE_ICON = 9; /** - * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} - * is invalid, of the left headset + * Battery level of left headset + * Data type should be {@String} 0-100 as {@link Byte} array, otherwise + * as invalid. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10; + public static final int METADATA_UNTETHERED_LEFT_BATTERY = 10; /** - * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} - * is invalid, of the right headset + * Battery level of rigth headset + * Data type should be {@String} 0-100 as {@link Byte} array, otherwise + * as invalid. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11; + public static final int METADATA_UNTETHERED_RIGHT_BATTERY = 11; /** - * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} - * is invalid, of the headset charging case + * Battery level of the headset charging case + * Data type should be {@String} 0-100 as {@link Byte} array, otherwise + * as invalid. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12; + public static final int METADATA_UNTETHERED_CASE_BATTERY = 12; /** * Whether the left headset is charging + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13; + public static final int METADATA_UNTETHERED_LEFT_CHARGING = 13; /** * Whether the right headset is charging + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14; + public static final int METADATA_UNTETHERED_RIGHT_CHARGING = 14; /** * Whether the headset charging case is charging + * Data type should be {@String} as {@link Byte} array. * @hide */ @SystemApi - public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15; + public static final int METADATA_UNTETHERED_CASE_CHARGING = 15; /** - * URI to the enhanced settings UI slice, null or empty String means - * the UI does not exist + * URI to the enhanced settings UI slice + * Data type should be {@String} as {@link Byte} array, null means + * the UI does not exist. * @hide */ @SystemApi @@ -2243,21 +2261,21 @@ public final class BluetoothDevice implements Parcelable { * {@link #BOND_NONE}. * * @param key must be within the list of BluetoothDevice.METADATA_* - * @param value the string data to set for key. Must be less than + * @param value a byte array data to set for key. Must be less than * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length * @return true on success, false on error * @hide */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public boolean setMetadata(int key, String value) { + public boolean setMetadata(int key, @NonNull byte[] value) { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata"); return false; } - if (value.length() > METADATA_MAX_LENGTH) { - throw new IllegalArgumentException("value length is " + value.length() + if (value.length > METADATA_MAX_LENGTH) { + throw new IllegalArgumentException("value length is " + value.length + ", should not over " + METADATA_MAX_LENGTH); } try { @@ -2272,12 +2290,13 @@ public final class BluetoothDevice implements Parcelable { * Get a keyed metadata for this {@link BluetoothDevice} as {@link String} * * @param key must be within the list of BluetoothDevice.METADATA_* - * @return Metadata of the key as string, null on error or not found + * @return Metadata of the key as byte array, null on error or not found * @hide */ @SystemApi + @Nullable @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) - public String getMetadata(int key) { + public byte[] getMetadata(int key) { final IBluetooth service = sService; if (service == null) { Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata"); diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 791c55196ecf..00f1e43c8493 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -3398,7 +3398,7 @@ public abstract class ContentResolver implements ContentInterface { * * @param mimeType Valid, concrete MIME type. */ - public final @NonNull TypeInfo getTypeInfo(@NonNull String mimeType) { + public final @NonNull MimeTypeInfo getTypeInfo(@NonNull String mimeType) { Objects.requireNonNull(mimeType); return MimeIconUtils.getTypeInfo(mimeType); } @@ -3407,13 +3407,13 @@ public abstract class ContentResolver implements ContentInterface { * Detailed description of a specific MIME type, including an icon and label * that describe the type. */ - public static final class TypeInfo { + public static final class MimeTypeInfo { private final Icon mIcon; private final CharSequence mLabel; private final CharSequence mContentDescription; /** {@hide} */ - public TypeInfo(@NonNull Icon icon, @NonNull CharSequence label, + public MimeTypeInfo(@NonNull Icon icon, @NonNull CharSequence label, @NonNull CharSequence contentDescription) { mIcon = Objects.requireNonNull(icon); mLabel = Objects.requireNonNull(label); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index d87171e39595..8628d32123bc 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4731,6 +4731,18 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC"; + /** + * Used with {@link #ACTION_MAIN} to launch the files application. + * The activity should be able to browse and manage files stored on the device. + * <p>NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String)} to generate a main + * Intent with this category in the selector.</p> + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard extra data keys. diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 7fe840c11dfa..a71f7d2c6455 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -30,7 +30,6 @@ import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager; import android.app.AppGlobals; import android.content.Intent; -import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.PackageManager.DeleteFlags; import android.content.pm.PackageManager.InstallReason; @@ -504,12 +503,14 @@ public class PackageInstaller { * * <p>Staged session is active iff: * <ul> - * <li>It is committed. - * <li>It is not applied. - * <li>It is not failed. + * <li>It is committed, i.e. {@link SessionInfo#isCommitted()} is {@code true}, and + * <li>it is not applied, i.e. {@link SessionInfo#isStagedSessionApplied()} is {@code + * false}, and + * <li>it is not failed, i.e. {@link SessionInfo#isStagedSessionFailed()} is {@code false}. * </ul> * - * <p>In case of a multi-apk session, parent session will be returned. + * <p>In case of a multi-apk session, reasoning above is applied to the parent session, since + * that is the one that should been {@link Session#commit committed}. */ public @Nullable SessionInfo getActiveStagedSession() { final List<SessionInfo> stagedSessions = getStagedSessions(); @@ -2307,7 +2308,8 @@ public class PackageInstaller { } /** - * Whenever this session was committed. + * Returns {@code true} if {@link Session#commit(IntentSender)}} was called for this + * session. */ public boolean isCommitted() { return isCommitted; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 2b23e7a2a633..bdd80e325c4b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1602,8 +1602,8 @@ public class PackageParser { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); XmlResourceParser parser = null; + ApkAssets apkAssets = null; try { - final ApkAssets apkAssets; try { apkAssets = fd != null ? ApkAssets.loadFromFd(fd, debugPathName, false, false) @@ -1640,7 +1640,13 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - // TODO(b/72056911): Implement and call close() on ApkAssets. + if (apkAssets != null) { + try { + apkAssets.close(); + } catch (Throwable ignored) { + } + } + // TODO(b/72056911): Implement AutoCloseable on ApkAssets. } } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index dc1d052da64f..69462ab99483 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -36,7 +36,9 @@ import java.io.IOException; */ public final class ApkAssets { @GuardedBy("this") private final long mNativePtr; - @GuardedBy("this") private StringBlock mStringBlock; + @GuardedBy("this") private final StringBlock mStringBlock; + + @GuardedBy("this") private boolean mOpen = true; /** * Creates a new ApkAssets instance from the given path on disk. @@ -180,7 +182,20 @@ public final class ApkAssets { @Override protected void finalize() throws Throwable { - nativeDestroy(mNativePtr); + close(); + } + + /** + * Closes this class and the contained {@link #mStringBlock}. + */ + public void close() throws Throwable { + synchronized (this) { + if (mOpen) { + mOpen = false; + mStringBlock.close(); + nativeDestroy(mNativePtr); + } + } } private static native long nativeLoad( diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java index b5ec0f9f3712..b7bc8229fa45 100644 --- a/core/java/android/content/res/StringBlock.java +++ b/core/java/android/content/res/StringBlock.java @@ -18,13 +18,34 @@ package android.content.res; import android.annotation.UnsupportedAppUsage; import android.graphics.Color; -import android.text.*; -import android.text.style.*; -import android.util.Log; -import android.util.SparseArray; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; +import android.text.Annotation; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannedString; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.style.AbsoluteSizeSpan; +import android.text.style.BackgroundColorSpan; +import android.text.style.BulletSpan; +import android.text.style.CharacterStyle; +import android.text.style.ForegroundColorSpan; +import android.text.style.LineHeightSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.StrikethroughSpan; +import android.text.style.StyleSpan; +import android.text.style.SubscriptSpan; +import android.text.style.SuperscriptSpan; +import android.text.style.TextAppearanceSpan; +import android.text.style.TypefaceSpan; +import android.text.style.URLSpan; +import android.text.style.UnderlineSpan; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; import java.util.Arrays; @@ -40,8 +61,12 @@ final class StringBlock { private final long mNative; private final boolean mUseSparse; private final boolean mOwnsNative; + private CharSequence[] mStrings; private SparseArray<CharSequence> mSparseStrings; + + @GuardedBy("this") private boolean mOpen = true; + StyleIDs mStyleIDs = null; public StringBlock(byte[] data, boolean useSparse) { @@ -141,12 +166,23 @@ final class StringBlock { } } + @Override protected void finalize() throws Throwable { try { super.finalize(); } finally { - if (mOwnsNative) { - nativeDestroy(mNative); + close(); + } + } + + public void close() throws Throwable { + synchronized (this) { + if (mOpen) { + mOpen = false; + + if (mOwnsNative) { + nativeDestroy(mNative); + } } } } diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index 014bc242e17a..3523e956656a 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -52,7 +52,7 @@ public class SQLiteQueryBuilder { private static final Pattern sLimitPattern = Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?"); private static final Pattern sAggregationPattern = Pattern.compile( - "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL)\\((.+)\\)"); + "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL|GROUP_CONCAT)\\((.+)\\)"); private Map<String, String> mProjectionMap = null; private List<Pattern> mProjectionGreylist = null; diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index a696eeb6bcc7..6c497d47c645 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -207,5 +207,22 @@ public class BiometricManager { Slog.w(TAG, "onConfirmDeviceCredentialError(): Service not connected"); } } + + /** + * TODO(b/123378871): Remove when moved. + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void registerCancellationCallback(IBiometricConfirmDeviceCredentialCallback callback) { + if (mService != null) { + try { + mService.registerCancellationCallback(callback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } else { + Slog.w(TAG, "registerCancellationCallback(): Service not connected"); + } + } } diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 08035972a0db..1142a07bc66c 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -82,6 +82,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * @hide */ public static final String KEY_ALLOW_DEVICE_CREDENTIAL = "allow_device_credential"; + /** + * @hide + */ + public static final String KEY_FROM_CONFIRM_DEVICE_CREDENTIAL + = "from_confirm_device_credential"; /** * Error/help message will show for this amount of time. @@ -271,6 +276,17 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** + * TODO(123378871): Remove when moved. + * @return + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + @NonNull public Builder setFromConfirmDeviceCredential() { + mBundle.putBoolean(KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, true); + return this; + } + + /** * Creates a {@link BiometricPrompt}. * @return a {@link BiometricPrompt} * @throws IllegalArgumentException if any of the required fields are not set. @@ -494,7 +510,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan public void authenticateUser(@NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, - int userId) { + int userId, + IBiometricConfirmDeviceCredentialCallback confirmDeviceCredentialCallback) { if (cancel == null) { throw new IllegalArgumentException("Must supply a cancellation signal"); } @@ -504,7 +521,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (callback == null) { throw new IllegalArgumentException("Must supply a callback"); } - authenticateInternal(null /* crypto */, cancel, executor, callback, userId); + authenticateInternal(null /* crypto */, cancel, executor, callback, userId, + confirmDeviceCredentialCallback); } /** @@ -555,7 +573,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (mBundle.getBoolean(KEY_ALLOW_DEVICE_CREDENTIAL)) { throw new IllegalArgumentException("Device credential not supported with crypto"); } - authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId()); + authenticateInternal(crypto, cancel, executor, callback, mContext.getUserId(), + null /* confirmDeviceCredentialCallback */); } /** @@ -597,7 +616,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (callback == null) { throw new IllegalArgumentException("Must supply a callback"); } - authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId()); + authenticateInternal(null /* crypto */, cancel, executor, callback, mContext.getUserId(), + null /* confirmDeviceCredentialCallback */); } private void cancelAuthentication() { @@ -614,7 +634,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, - int userId) { + int userId, + IBiometricConfirmDeviceCredentialCallback confirmDeviceCredentialCallback) { try { if (cancel.isCanceled()) { Log.w(TAG, "Authentication already canceled"); @@ -629,7 +650,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan final long sessionId = crypto != null ? crypto.getOpId() : 0; if (BiometricManager.hasBiometrics(mContext)) { mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver, - mContext.getOpPackageName(), mBundle); + mContext.getOpPackageName(), mBundle, confirmDeviceCredentialCallback); } else { mExecutor.execute(() -> { callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT, diff --git a/core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl b/core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl new file mode 100644 index 000000000000..8b35852efd31 --- /dev/null +++ b/core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.biometrics; + +/** + * Communication channel between ConfirmDeviceCredential / ConfirmLock* and BiometricService. + * @hide + */ +interface IBiometricConfirmDeviceCredentialCallback { + // Invoked when authentication should be canceled. + oneway void cancel(); +}
\ No newline at end of file diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index 4971911eb87c..90d4921c3c18 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -17,6 +17,7 @@ package android.hardware.biometrics; import android.os.Bundle; +import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricServiceReceiver; @@ -30,8 +31,10 @@ import android.hardware.biometrics.IBiometricServiceReceiver; interface IBiometricService { // Requests authentication. The service choose the appropriate biometric to use, and show // the corresponding BiometricDialog. + // TODO(b/123378871): Remove callback when moved. void authenticate(IBinder token, long sessionId, int userId, - IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle); + IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle, + IBiometricConfirmDeviceCredentialCallback callback); // Cancel authentication for the given sessionId void cancelAuthentication(IBinder token, String opPackageName); @@ -59,4 +62,8 @@ interface IBiometricService { void onConfirmDeviceCredentialSuccess(); // TODO(b/123378871): Remove when moved. void onConfirmDeviceCredentialError(int error, String message); + // TODO(b/123378871): Remove when moved. + // When ConfirmLock* is invoked from BiometricPrompt, it needs to register a callback so that + // it can receive the cancellation signal. + void registerCancellationCallback(IBiometricConfirmDeviceCredentialCallback callback); } diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index dbb894f92f55..a46c410bd55e 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -15,9 +15,16 @@ */ package android.net; +import static android.Manifest.permission.NETWORK_STACK; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.content.Context; +import java.util.ArrayList; +import java.util.Arrays; /** * * Constants for client code communicating with the network stack service. @@ -37,4 +44,52 @@ public class NetworkStack { "android.permission.MAINLINE_NETWORK_STACK"; private NetworkStack() {} + + /** + * If the NetworkStack, MAINLINE_NETWORK_STACK are not allowed for a particular process, throw a + * {@link SecurityException}. + * + * @param context {@link android.content.Context} for the process. + * + * @hide + */ + public static void checkNetworkStackPermission(final @NonNull Context context) { + checkNetworkStackPermissionOr(context); + } + + /** + * If the NetworkStack, MAINLINE_NETWORK_STACK or other specified permissions are not allowed + * for a particular process, throw a {@link SecurityException}. + * + * @param context {@link android.content.Context} for the process. + * @param otherPermissions The set of permissions that could be the candidate permissions , or + * empty string if none of other permissions needed. + * @hide + */ + public static void checkNetworkStackPermissionOr(final @NonNull Context context, + final @NonNull String... otherPermissions) { + ArrayList<String> permissions = new ArrayList<String>(Arrays.asList(otherPermissions)); + permissions.add(NETWORK_STACK); + permissions.add(PERMISSION_MAINLINE_NETWORK_STACK); + enforceAnyPermissionOf(context, permissions.toArray(new String[0])); + } + + private static void enforceAnyPermissionOf(final @NonNull Context context, + final @NonNull String... permissions) { + if (!checkAnyPermissionOf(context, permissions)) { + throw new SecurityException("Requires one of the following permissions: " + + String.join(", ", permissions) + "."); + } + } + + private static boolean checkAnyPermissionOf(final @NonNull Context context, + final @NonNull String... permissions) { + for (String permission : permissions) { + if (context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { + return true; + } + } + return false; + } + } diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 53503f47ca74..e56b6e0a33b8 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -188,11 +188,16 @@ public class GraphicsEnvironment { if (gpuDebugLayerApp != null && !gpuDebugLayerApp.isEmpty()) { Log.i(TAG, "GPU debug layer app: " + gpuDebugLayerApp); - final String paths = getDebugLayerAppPaths(pm, gpuDebugLayerApp); - if (paths != null) { - // Append the path so files placed in the app's base directory will - // override the external path - layerPaths += paths + ":"; + // If a colon is present, treat this as multiple apps, so Vulkan and GLES + // layer apps can be provided at the same time. + String[] layerApps = gpuDebugLayerApp.split(":"); + for (int i = 0; i < layerApps.length; i++) { + String paths = getDebugLayerAppPaths(pm, layerApps[i]); + if (paths != null) { + // Append the path so files placed in the app's base directory will + // override the external path + layerPaths += paths + ":"; + } } } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index bd70f23c8b5d..ab19fd6e8ca1 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -646,9 +646,14 @@ public class ZygoteProcess { ZygoteConfig.USAP_POOL_ENABLED, USAP_POOL_ENABLED_DEFAULT); if (!propertyString.isEmpty()) { - mUsapPoolEnabled = Zygote.getConfigurationPropertyBoolean( + if (SystemProperties.get("dalvik.vm.boot-image", "").endsWith("apex.art")) { + // TODO(b/119800099): Tweak usap configuration in jitzygote mode. + mUsapPoolEnabled = false; + } else { + mUsapPoolEnabled = Zygote.getConfigurationPropertyBoolean( ZygoteConfig.USAP_POOL_ENABLED, Boolean.parseBoolean(USAP_POOL_ENABLED_DEFAULT)); + } } boolean valueChanged = origVal != mUsapPoolEnabled; diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 075b650ed8f4..080ff7301ed2 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1130,7 +1130,7 @@ public class StorageManager { public @NonNull StorageVolume getStorageVolume(@NonNull Uri uri) { final String volumeName = MediaStore.getVolumeName(uri); switch (volumeName) { - case MediaStore.VOLUME_EXTERNAL: + case MediaStore.VOLUME_EXTERNAL_PRIMARY: return getPrimaryStorageVolume(); default: for (StorageVolume vol : getStorageVolumes()) { diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index 225ecfa315aa..6280600823d7 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -265,8 +265,13 @@ public final class StorageVolume implements Parcelable { } /** {@hide} */ + public static @Nullable String normalizeUuid(@Nullable String fsUuid) { + return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null; + } + + /** {@hide} */ public @Nullable String getNormalizedUuid() { - return mFsUuid != null ? mFsUuid.toLowerCase(Locale.US) : null; + return normalizeUuid(mFsUuid); } /** diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index bda6ed19d3c1..da19d59367a0 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -102,20 +102,40 @@ public final class MediaStore { public static final @NonNull Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); /** - * Volume name used for content on "internal" storage of device. This - * volume contains media distributed with the device, such as built-in - * ringtones and wallpapers. + * Synthetic volume name that provides a view of all content across the + * "internal" storage of the device. + * <p> + * This synthetic volume provides a merged view of all media distributed + * with the device, such as built-in ringtones and wallpapers. + * <p> + * Because this is a synthetic volume, you can't insert new content into + * this volume. */ public static final String VOLUME_INTERNAL = "internal"; /** - * Volume name used for content on "external" storage of device. This only - * includes media on the primary shared storage device; the contents of any - * secondary storage devices can be obtained using - * {@link #getAllVolumeNames(Context)}. + * Synthetic volume name that provides a view of all content across the + * "external" storage of the device. + * <p> + * This synthetic volume provides a merged view of all media across all + * currently attached external storage devices. + * <p> + * Because this is a synthetic volume, you can't insert new content into + * this volume. Instead, you can insert content into a specific storage + * volume obtained from {@link #getExternalVolumeNames(Context)}. */ public static final String VOLUME_EXTERNAL = "external"; + /** + * Specific volume name that represents the primary external storage device + * at {@link Environment#getExternalStorageDirectory()}. + * <p> + * This volume may not always be available, such as when the user has + * ejected the device. You can find a list of all specific volume names + * using {@link #getExternalVolumeNames(Context)}. + */ + public static final String VOLUME_EXTERNAL_PRIMARY = "external_primary"; + /** {@hide} */ public static final String SCAN_FILE_CALL = "scan_file"; /** {@hide} */ @@ -1037,6 +1057,16 @@ public final class MediaStore { public static final String OWNER_PACKAGE_NAME = "owner_package_name"; /** + * Volume name of the specific storage device where this media item is + * persisted. The value is typically one of the volume names returned + * from {@link MediaStore#getExternalVolumeNames(Context)}. + * <p> + * This is a read-only column that is automatically computed. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String VOLUME_NAME = "volume_name"; + + /** * Relative path of this media item within the storage device where it * is persisted. For example, an item stored at * {@code /storage/0000-0000/DCIM/Vacation/IMG1024.JPG} would have a @@ -1408,7 +1438,7 @@ public final class MediaStore { final StorageVolume sv = sm.getStorageVolume(path); if (sv != null) { if (sv.isPrimary()) { - return VOLUME_EXTERNAL; + return VOLUME_EXTERNAL_PRIMARY; } else { return checkArgumentVolumeName(sv.getNormalizedUuid()); } @@ -1710,7 +1740,7 @@ public final class MediaStore { String stringUrl = null; /* value to be returned */ try { - url = cr.insert(EXTERNAL_CONTENT_URI, values); + url = cr.insert(getContentUri(VOLUME_EXTERNAL_PRIMARY), values); if (source != null) { try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream( @@ -2676,7 +2706,13 @@ public final class MediaStore { public static final String ALBUM = "album"; /** - * The artist whose songs appear on this album + * The ID of the artist whose songs appear on this album. + */ + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) + public static final String ARTIST_ID = "artist_id"; + + /** + * The name of the artist whose songs appear on this album. */ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ARTIST = "artist"; @@ -3218,22 +3254,29 @@ public final class MediaStore { } } + /** @removed */ + @Deprecated + public static @NonNull Set<String> getAllVolumeNames(@NonNull Context context) { + return getExternalVolumeNames(context); + } + /** - * Return list of all volume names currently available. This includes a - * unique name for each shared storage device that is currently mounted. + * Return list of all specific volume names that make up + * {@link #VOLUME_EXTERNAL}. This includes a unique volume name for each + * shared storage device that is currently attached, which typically + * includes {@link MediaStore#VOLUME_EXTERNAL_PRIMARY}. * <p> - * Each name can be passed to APIs like - * {@link MediaStore.Images.Media#getContentUri(String)} to query media at - * that location. + * Each specific volume name can be passed to APIs like + * {@link MediaStore.Images.Media#getContentUri(String)} to interact with + * media on that storage device. */ - public static @NonNull Set<String> getAllVolumeNames(@NonNull Context context) { + public static @NonNull Set<String> getExternalVolumeNames(@NonNull Context context) { final StorageManager sm = context.getSystemService(StorageManager.class); final Set<String> volumeNames = new ArraySet<>(); - volumeNames.add(VOLUME_INTERNAL); for (VolumeInfo vi : sm.getVolumes()) { if (vi.isVisibleForUser(UserHandle.myUserId()) && vi.isMountedReadable()) { if (vi.isPrimary()) { - volumeNames.add(VOLUME_EXTERNAL); + volumeNames.add(VOLUME_EXTERNAL_PRIMARY); } else { volumeNames.add(vi.getNormalizedFsUuid()); } @@ -3264,6 +3307,8 @@ public final class MediaStore { return volumeName; } else if (VOLUME_EXTERNAL.equals(volumeName)) { return volumeName; + } else if (VOLUME_EXTERNAL_PRIMARY.equals(volumeName)) { + return volumeName; } // When not one of the well-known values above, it must be a hex UUID @@ -3279,8 +3324,9 @@ public final class MediaStore { } /** - * Return path where the given volume is mounted. Not valid for - * {@link #VOLUME_INTERNAL}. + * Return path where the given specific volume is mounted. Not valid for + * {@link #VOLUME_INTERNAL} or {@link #VOLUME_EXTERNAL}, since those are + * broad collections that cover many paths. * * @hide */ @@ -3291,8 +3337,12 @@ public final class MediaStore { throw new IllegalArgumentException(); } - if (VOLUME_EXTERNAL.equals(volumeName)) { - return Environment.getExternalStorageDirectory(); + switch (volumeName) { + case VOLUME_INTERNAL: + case VOLUME_EXTERNAL: + throw new FileNotFoundException(volumeName + " has no associated path"); + case VOLUME_EXTERNAL_PRIMARY: + return Environment.getExternalStorageDirectory(); } final StorageManager sm = AppGlobals.getInitialApplication() @@ -3322,23 +3372,31 @@ public final class MediaStore { throw new IllegalArgumentException(); } + final Context context = AppGlobals.getInitialApplication(); + final UserManager um = context.getSystemService(UserManager.class); + final ArrayList<File> res = new ArrayList<>(); if (VOLUME_INTERNAL.equals(volumeName)) { - addCanoncialFile(res, new File(Environment.getRootDirectory(), "media")); - addCanoncialFile(res, new File(Environment.getOemDirectory(), "media")); - addCanoncialFile(res, new File(Environment.getProductDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getRootDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getOemDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getProductDirectory(), "media")); + } else if (VOLUME_EXTERNAL.equals(volumeName)) { + for (String exactVolume : getExternalVolumeNames(context)) { + addCanonicalFile(res, getVolumePath(exactVolume)); + } + if (um.isDemoUser()) { + addCanonicalFile(res, Environment.getDataPreloadsMediaDirectory()); + } } else { - addCanoncialFile(res, getVolumePath(volumeName)); - final UserManager um = AppGlobals.getInitialApplication() - .getSystemService(UserManager.class); - if (VOLUME_EXTERNAL.equals(volumeName) && um.isDemoUser()) { - addCanoncialFile(res, Environment.getDataPreloadsMediaDirectory()); + addCanonicalFile(res, getVolumePath(volumeName)); + if (VOLUME_EXTERNAL_PRIMARY.equals(volumeName) && um.isDemoUser()) { + addCanonicalFile(res, Environment.getDataPreloadsMediaDirectory()); } } return res; } - private static void addCanoncialFile(List<File> list, File file) { + private static void addCanonicalFile(List<File> list, File file) { try { list.add(file.getCanonicalFile()); } catch (IOException e) { @@ -3376,12 +3434,12 @@ public final class MediaStore { * <p> * No other assumptions should be made about the meaning of the version. * <p> - * This method returns the version for {@link MediaStore#VOLUME_EXTERNAL}; - * to obtain a version for a different volume, use - * {@link #getVersion(Context, String)}. + * This method returns the version for + * {@link MediaStore#VOLUME_EXTERNAL_PRIMARY}; to obtain a version for a + * different volume, use {@link #getVersion(Context, String)}. */ public static @NonNull String getVersion(@NonNull Context context) { - return getVersion(context, VOLUME_EXTERNAL); + return getVersion(context, VOLUME_EXTERNAL_PRIMARY); } /** @@ -3395,7 +3453,7 @@ public final class MediaStore { * * @param volumeName specific volume to obtain an opaque version string for. * Must be one of the values returned from - * {@link #getAllVolumeNames(Context)}. + * {@link #getExternalVolumeNames(Context)}. */ public static @NonNull String getVersion(@NonNull Context context, @NonNull String volumeName) { final ContentResolver resolver = context.getContentResolver(); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6e897975963f..e3b2d898f9b6 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -955,6 +955,20 @@ public final class Settings { "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; /** + * Activity Action: Open the advanced power usage details page of an associated app. + * <p> + * Input: Intent's data URI set with an application name, using the + * "package" schema (like "package:com.my.app") + * <p> + * Output: Nothing. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_VIEW_ADVANCED_POWER_USAGE_DETAIL = + "android.settings.VIEW_ADVANCED_POWER_USAGE_DETAIL"; + + /** * Activity Action: Show screen for controlling background data * restrictions for a particular application. * <p> diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java index 8ba9a8357c65..e81ce7f85ac1 100644 --- a/core/java/android/service/notification/Adjustment.java +++ b/core/java/android/service/notification/Adjustment.java @@ -18,6 +18,7 @@ package android.service.notification; import android.annotation.NonNull; import android.annotation.StringDef; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.Notification; import android.os.Bundle; import android.os.Parcel; @@ -40,6 +41,7 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi +@TestApi public final class Adjustment implements Parcelable { private final String mPackage; private final String mKey; @@ -130,6 +132,7 @@ public final class Adjustment implements Parcelable { * @hide */ @SystemApi + @TestApi public Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user) { mPackage = pkg; mKey = key; @@ -212,6 +215,7 @@ public final class Adjustment implements Parcelable { /** @hide */ @SystemApi + @TestApi public int getUser() { return mUser; } diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index 22104b5089cf..5977bafd7cf1 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -53,4 +53,5 @@ oneway interface INotificationListener void onNotificationDirectReply(String key); void onSuggestedReplySent(String key, in CharSequence reply, int source); void onActionClicked(String key, in Notification.Action action, int source); + void onAllowedAdjustmentsChanged(); } diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index b81725d99d2b..cafeb87691bd 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -65,6 +66,7 @@ import java.util.List; * @hide */ @SystemApi +@TestApi public abstract class NotificationAssistantService extends NotificationListenerService { private static final String TAG = "NotificationAssistants"; @@ -218,10 +220,10 @@ public abstract class NotificationAssistantService extends NotificationListenerS /** * Implement this to know when a user has changed which features of * their notifications the assistant can modify. - * <p> Query {@link NotificationManager#getAllowedAssistantCapabilities()} to see what + * <p> Query {@link NotificationManager#getAllowedAssistantAdjustments()} to see what * {@link Adjustment adjustments} you are currently allowed to make.</p> */ - public void onCapabilitiesChanged() { + public void onAllowedAdjustmentsChanged() { } /** @@ -357,6 +359,11 @@ public abstract class NotificationAssistantService extends NotificationListenerS args.argi2 = source; mHandler.obtainMessage(MyHandler.MSG_ON_ACTION_INVOKED, args).sendToTarget(); } + + @Override + public void onAllowedAdjustmentsChanged() { + mHandler.obtainMessage(MyHandler.MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED).sendToTarget(); + } } private final class MyHandler extends Handler { @@ -367,6 +374,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5; public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6; public static final int MSG_ON_ACTION_INVOKED = 7; + public static final int MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED = 8; public MyHandler(Looper looper) { super(looper, null, false); @@ -448,6 +456,10 @@ public abstract class NotificationAssistantService extends NotificationListenerS onActionInvoked(key, action, source); break; } + case MSG_ON_ALLOWED_ADJUSTMENTS_CHANGED: { + onAllowedAdjustmentsChanged(); + break; + } } } } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 333868a2f08a..3ec21e39e514 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -16,6 +16,7 @@ package android.service.notification; +import android.annotation.CurrentTimeMillisLong; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SdkConstant; @@ -1399,6 +1400,11 @@ public abstract class NotificationListenerService extends Service { } @Override + public void onAllowedAdjustmentsChanged() { + // no-op in the listener + } + + @Override public void onNotificationChannelModification(String pkgName, UserHandle user, NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType) { @@ -1675,6 +1681,7 @@ public abstract class NotificationListenerService extends Service { * * @return the time of the last alerting behavior, in milliseconds. */ + @CurrentTimeMillisLong public long getLastAudiblyAlertedMillis() { return mLastAudiblyAlertedMs; } diff --git a/core/java/android/util/proto/ProtoInputStream.java b/core/java/android/util/proto/ProtoInputStream.java index cd2b6ce3dc67..c290dffc42c9 100644 --- a/core/java/android/util/proto/ProtoInputStream.java +++ b/core/java/android/util/proto/ProtoInputStream.java @@ -16,8 +16,6 @@ package android.util.proto; -import android.annotation.TestApi; - import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -64,7 +62,6 @@ import java.util.ArrayList; * * @hide */ -@TestApi public final class ProtoInputStream extends ProtoStream { public static final int NO_MORE_FIELDS = -1; diff --git a/core/java/android/util/proto/TEST_MAPPING b/core/java/android/util/proto/TEST_MAPPING new file mode 100644 index 000000000000..cf9f0772ac2d --- /dev/null +++ b/core/java/android/util/proto/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "ProtoInputStreamTests" + } + ] +}
\ No newline at end of file diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 597b34bf8554..956161acd762 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -41,9 +41,11 @@ interface IRecentsAnimationController { * with remote animation targets should be relinquished. If {@param moveHomeToTop} is true, then * the home activity should be moved to the top. Otherwise, the home activity is hidden and the * user is returned to the app. + * @param sendUserLeaveHint If set to true, {@link Activity#onUserLeaving} will be sent to the + * top resumed app, false otherwise. */ @UnsupportedAppUsage - void finish(boolean moveHomeToTop); + void finish(boolean moveHomeToTop, boolean sendUserLeaveHint); /** * Called by the handler to indicate that the recents animation input consumer should be diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index f9b629c85fb0..1fc7f0e36095 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -416,23 +416,8 @@ public abstract class LayoutInflater { } private void initPrecompiledViews() { - // Use the device config if enabled, otherwise default to the system property. - String usePrecompiledLayout = null; - try { - usePrecompiledLayout = DeviceConfig.getProperty( - DeviceConfig.NAMESPACE_RUNTIME, - USE_PRECOMPILED_LAYOUT); - } catch (Exception e) { - // May be caused by permission errors reading the property (i.e. instant apps). - } + // Precompiled layouts are not supported in this release. boolean enabled = false; - if (TextUtils.isEmpty(usePrecompiledLayout)) { - enabled = SystemProperties.getBoolean( - USE_PRECOMPILED_LAYOUT, - false); - } else { - enabled = Boolean.parseBoolean(usePrecompiledLayout); - } initPrecompiledViews(enabled); } diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index 2896bd049e7c..c50a3aa8ac7c 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -932,7 +932,8 @@ public final class ViewTreeObserver { * @param listener listener to add * @see View#setSystemGestureExclusionRects(List) */ - public void addOnSystemGestureExclusionRectsChangedListener(Consumer<List<Rect>> listener) { + public void addOnSystemGestureExclusionRectsChangedListener( + @NonNull Consumer<List<Rect>> listener) { checkIsAlive(); if (mGestureExclusionListeners == null) { mGestureExclusionListeners = new CopyOnWriteArray<>(); @@ -945,7 +946,8 @@ public final class ViewTreeObserver { * @see #addOnSystemGestureExclusionRectsChangedListener(Consumer) * @see View#setSystemGestureExclusionRects(List) */ - public void removeOnSystemGestureExclusionRectsChangedListener(Consumer<List<Rect>> listener) { + public void removeOnSystemGestureExclusionRectsChangedListener( + @NonNull Consumer<List<Rect>> listener) { checkIsAlive(); if (mGestureExclusionListeners == null) { return; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 3544a8733c68..a9463e998bbb 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -2329,6 +2329,70 @@ public abstract class Window { return 0; } + /** + * Sets whether the system should ensure that the status bar has enough + * contrast when a fully transparent background is requested. + * + * <p>If set to this value, the system will determine whether a scrim is necessary + * to ensure that the status bar has enough contrast with the contents of + * this app, and set an appropriate effective bar background color accordingly. + * + * <p>When the status bar color has a non-zero alpha value, the value of this + * property has no effect. + * + * @see android.R.attr#ensureStatusBarContrastWhenTransparent + * @hide pending API + */ + public void setEnsureStatusBarContrastWhenTransparent(boolean ensureContrast) { + } + + /** + * Returns whether the system is ensuring that the status bar has enough contrast when a + * fully transparent background is requested. + * + * <p>When the status bar color has a non-zero alpha value, the value of this + * property has no effect. + * + * @see android.R.attr#ensureStatusBarContrastWhenTransparent + * @return true, if the system is ensuring contrast, false otherwise. + * @hide pending API + */ + public boolean isEnsureStatusBarContrastWhenTransparent() { + return false; + } + + /** + * Sets whether the system should ensure that the navigation bar has enough + * contrast when a fully transparent background is requested. + * + * <p>If set to this value, the system will determine whether a scrim is necessary + * to ensure that the navigation bar has enough contrast with the contents of + * this app, and set an appropriate effective bar background color accordingly. + * + * <p>When the navigation bar color has a non-zero alpha value, the value of this + * property has no effect. + * + * @see android.R.attr#ensureNavigationBarContrastWhenTransparent + * @hide pending API + */ + public void setEnsureNavigationBarContrastWhenTransparent(boolean ensureContrast) { + } + + /** + * Returns whether the system is ensuring that the navigation bar has enough contrast when a + * fully transparent background is requested. + * + * <p>When the navigation bar color has a non-zero alpha value, the value of this + * property has no effect. + * + * @return true, if the system is ensuring contrast, false otherwise. + * @see android.R.attr#ensureNavigationBarContrastWhenTransparent + * @hide pending API + */ + public boolean isEnsureNavigationBarContrastWhenTransparent() { + return false; + } + /** @hide */ public void setTheme(int resId) { } diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java index cf171d738524..6f9d4d30909f 100644 --- a/core/java/android/view/contentcapture/ContentCaptureCondition.java +++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java @@ -54,7 +54,9 @@ public final class ContentCaptureCondition implements Parcelable { * * @param locusId id of the condition, as defined by * {@link ContentCaptureContext#getLocusId()}. - * @param flags either {@link ContentCaptureCondition#FLAG_IS_REGEX} or {@code 0}. + * @param flags either {@link ContentCaptureCondition#FLAG_IS_REGEX} (to use a regular + * expression match) or {@code 0} (in which case the {@code LocusId} must be an exact match of + * the {@code LocusId} used in the {@link ContentCaptureContext}). */ public ContentCaptureCondition(@NonNull LocusId locusId, @Flags int flags) { this.mLocusId = Preconditions.checkNotNull(locusId); diff --git a/core/java/android/view/textclassifier/ConversationAction.java b/core/java/android/view/textclassifier/ConversationAction.java index f2d878a8bf54..6070b5341cf9 100644 --- a/core/java/android/view/textclassifier/ConversationAction.java +++ b/core/java/android/view/textclassifier/ConversationAction.java @@ -200,13 +200,11 @@ public final class ConversationAction implements Parcelable { /** * Returns the extended data related to this conversation action. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** Builder class to construct {@link ConversationAction}. */ @@ -268,7 +266,7 @@ public final class ConversationAction implements Parcelable { mAction, mTextReply, mScore, - mExtras == null ? Bundle.EMPTY : mExtras.deepCopy()); + mExtras == null ? Bundle.EMPTY : mExtras); } } } diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java index dc7521296e9f..b408129231e7 100644 --- a/core/java/android/view/textclassifier/ConversationActions.java +++ b/core/java/android/view/textclassifier/ConversationActions.java @@ -214,13 +214,11 @@ public final class ConversationActions implements Parcelable { /** * Returns the extended data related to this conversation action. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** Builder class to construct a {@link Message} */ @@ -277,7 +275,7 @@ public final class ConversationActions implements Parcelable { mAuthor, mReferenceTime, mText == null ? null : new SpannedString(mText), - mExtras == null ? new Bundle() : mExtras.deepCopy()); + mExtras == null ? Bundle.EMPTY : mExtras); } } } diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 9ede8fbd176e..63210516b3e1 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -265,13 +265,11 @@ public final class TextClassification implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } @Override @@ -523,7 +521,7 @@ public final class TextClassification implements Parcelable { } private Bundle buildExtras(EntityConfidence entityConfidence) { - final Bundle extras = mExtras == null ? new Bundle() : mExtras.deepCopy(); + final Bundle extras = mExtras == null ? new Bundle() : mExtras; if (mActionIntents.stream().anyMatch(Objects::nonNull)) { ExtrasUtils.putActionsIntents(extras, mActionIntents); } @@ -635,13 +633,11 @@ public final class TextClassification implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** @@ -717,7 +713,7 @@ public final class TextClassification implements Parcelable { public Request build() { return new Request(new SpannedString(mText), mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime, - mExtras == null ? Bundle.EMPTY : mExtras.deepCopy()); + mExtras == null ? Bundle.EMPTY : mExtras); } } diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java index eaf4d7f5d5d1..6c75ffbea0cd 100644 --- a/core/java/android/view/textclassifier/TextLanguage.java +++ b/core/java/android/view/textclassifier/TextLanguage.java @@ -113,12 +113,11 @@ public final class TextLanguage implements Parcelable { * Returns a bundle containing non-structured extra information about this result. What is * returned in the extras is specific to the {@link TextClassifier} implementation. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should prefer - * to hold a reference to the returned bundle rather than frequently calling this method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mBundle.deepCopy(); + return mBundle; } @Override @@ -199,7 +198,7 @@ public final class TextLanguage implements Parcelable { */ @NonNull public TextLanguage build() { - mBundle = mBundle == null ? new Bundle() : mBundle.deepCopy(); + mBundle = mBundle == null ? Bundle.EMPTY : mBundle; return new TextLanguage( mId, new EntityConfidence(mEntityConfidenceMap), @@ -263,13 +262,11 @@ public final class TextLanguage implements Parcelable { /** * Returns a bundle containing non-structured extra information about this request. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtra.deepCopy(); + return mExtra; } @Override @@ -327,8 +324,7 @@ public final class TextLanguage implements Parcelable { */ @NonNull public Request build() { - mBundle = mBundle == null ? new Bundle() : mBundle.deepCopy(); - return new Request(mText.toString(), mBundle); + return new Request(mText.toString(), mBundle == null ? Bundle.EMPTY : mBundle); } } } diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index cde27a08fc79..66a72f9b2e9e 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -125,13 +125,11 @@ public final class TextLinks implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** @@ -413,13 +411,11 @@ public final class TextLinks implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** @@ -497,7 +493,7 @@ public final class TextLinks implements Parcelable { return new Request( mText, mDefaultLocales, mEntityConfig, mLegacyFallback, - mExtras == null ? Bundle.EMPTY : mExtras.deepCopy()); + mExtras == null ? Bundle.EMPTY : mExtras); } } @@ -706,7 +702,7 @@ public final class TextLinks implements Parcelable { @NonNull public TextLinks build() { return new TextLinks(mFullText, mLinks, - mExtras == null ? Bundle.EMPTY : mExtras.deepCopy()); + mExtras == null ? Bundle.EMPTY : mExtras); } } } diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index 52989397e473..75c27bdbc1d5 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -112,13 +112,11 @@ public final class TextSelection implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } @Override @@ -197,7 +195,7 @@ public final class TextSelection implements Parcelable { public TextSelection build() { return new TextSelection( mStartIndex, mEndIndex, mEntityConfidence, mId, - mExtras == null ? Bundle.EMPTY : mExtras.deepCopy()); + mExtras == null ? Bundle.EMPTY : mExtras); } } @@ -296,13 +294,11 @@ public final class TextSelection implements Parcelable { /** * Returns the extended data. * - * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should - * prefer to hold a reference to the returned bundle rather than frequently calling this - * method. + * <p><b>NOTE: </b>Do not modify this bundle. */ @NonNull public Bundle getExtras() { - return mExtras.deepCopy(); + return mExtras; } /** @@ -382,7 +378,7 @@ public final class TextSelection implements Parcelable { public Request build() { return new Request(new SpannedString(mText), mStartIndex, mEndIndex, mDefaultLocales, mDarkLaunchAllowed, - mExtras == null ? Bundle.EMPTY : mExtras.deepCopy()); + mExtras == null ? Bundle.EMPTY : mExtras); } } diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java index 62f54b943e11..2bfbe4bdfba7 100644 --- a/core/java/android/webkit/WebViewZygote.java +++ b/core/java/android/webkit/WebViewZygote.java @@ -17,7 +17,6 @@ package android.webkit; import android.content.pm.PackageInfo; -import android.os.AsyncTask; import android.os.Build; import android.os.ChildZygoteProcess; import android.os.Process; @@ -81,17 +80,9 @@ public class WebViewZygote { synchronized (sLock) { sMultiprocessEnabled = enabled; - // When toggling between multi-process being on/off, start or stop the - // zygote. If it is enabled and the zygote is not yet started, launch it. - // Otherwise, kill it. The name may be null if the package information has - // not yet been resolved. - if (enabled) { - // Run on a background thread as this waits for the zygote to start and we don't - // want to block the caller on this. It's okay if this is delayed as anyone trying - // to use the zygote will call it first anyway. - AsyncTask.THREAD_POOL_EXECUTOR.execute(WebViewZygote::getProcess); - } else { - // No need to run this in the background, it's very brief. + // When multi-process is disabled, kill the zygote. When it is enabled, + // the zygote will be started when it is first needed in getProcess(). + if (!enabled) { stopZygoteLocked(); } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 988fad2fb28e..b51f8080569f 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -1047,6 +1047,12 @@ public class ChooserActivity extends ResolverActivity { value -= mChooserListAdapter.getCallerTargetCount() + mChooserListAdapter.getSelectableServiceTargetCount(); break; + case ChooserListAdapter.TARGET_STANDARD_AZ: + // A-Z targets are unranked standard targets; we use -1 to mark that they + // are from the alphabetical pool. + value = -1; + cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET; + break; } if (cat != 0) { @@ -1842,7 +1848,7 @@ public class ChooserActivity extends ResolverActivity { int offset = 0; int rowsToShow = mChooserRowAdapter.getContentPreviewRowCount() + mChooserRowAdapter.getServiceTargetRowCount() - + mChooserRowAdapter.getCallerTargetRowCount(); + + mChooserRowAdapter.getCallerAndRankedTargetRowCount(); // then this is most likely not a SEND_* action, so check // the app target count @@ -1886,6 +1892,7 @@ public class ChooserActivity extends ResolverActivity { public static final int TARGET_CALLER = 0; public static final int TARGET_SERVICE = 1; public static final int TARGET_STANDARD = 2; + public static final int TARGET_STANDARD_AZ = 3; private static final int MAX_SUGGESTED_APP_TARGETS = 4; private static final int MAX_TARGETS_PER_SERVICE = 2; @@ -1896,8 +1903,6 @@ public class ChooserActivity extends ResolverActivity { private ChooserTargetInfo mPlaceHolderTargetInfo = new PlaceHolderTargetInfo(); private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); private final List<TargetInfo> mCallerTargets = new ArrayList<>(); - private boolean mShowServiceTargets; - private boolean mTargetsNeedPruning = false; private final BaseChooserTargetComparator mBaseTargetComparator @@ -2037,7 +2042,7 @@ public class ChooserActivity extends ResolverActivity { @Override public int getCount() { - return getStandardTargetCount() + getAlphaTargetCount() + return getRankedTargetCount() + getAlphaTargetCount() + getSelectableServiceTargetCount() + getCallerTargetCount(); } @@ -2075,16 +2080,17 @@ public class ChooserActivity extends ResolverActivity { return 0; } - public int getStandardTargetCount() { - int standardCount = super.getCount(); - return standardCount > MAX_RANKED_TARGETS ? MAX_RANKED_TARGETS : standardCount; - } - int getAlphaTargetCount() { int standardCount = super.getCount(); return standardCount > MAX_RANKED_TARGETS ? standardCount : 0; } + int getRankedTargetCount() { + int spacesAvailable = MAX_RANKED_TARGETS - getCallerTargetCount(); + return Math.min(spacesAvailable, super.getCount()); + } + + public int getPositionTargetType(int position) { int offset = 0; @@ -2100,10 +2106,16 @@ public class ChooserActivity extends ResolverActivity { } offset += callerTargetCount; - final int standardTargetCount = getStandardTargetCount(); - if (position - offset < standardTargetCount) { + final int rankedTargetCount = getRankedTargetCount(); + if (position - offset < rankedTargetCount) { return TARGET_STANDARD; } + offset += rankedTargetCount; + + final int standardTargetCount = getAlphaTargetCount(); + if (position - offset < standardTargetCount) { + return TARGET_STANDARD_AZ; + } return TARGET_BAD; } @@ -2138,25 +2150,23 @@ public class ChooserActivity extends ResolverActivity { } offset += callerTargetCount; - // Ranked app targets - if (position - offset < MAX_RANKED_TARGETS) { + // Ranked standard app targets + final int rankedTargetCount = getRankedTargetCount(); + if (position - offset < rankedTargetCount) { return filtered ? super.getItem(position - offset) : getDisplayResolveInfo(position - offset); } - offset += MAX_RANKED_TARGETS; + offset += rankedTargetCount; // Alphabetical complete app target list. - Log.e(TAG, mSortedList.toString()); - if (position - offset < mSortedList.size()) { + if (position - offset < getAlphaTargetCount() && !mSortedList.isEmpty()) { return mSortedList.get(position - offset); } return null; - } - /** * Evaluate targets for inclusion in the direct share area. May not be included * if score is too low. @@ -2383,13 +2393,11 @@ public class ChooserActivity extends ResolverActivity { @Override public int getCount() { + return (int) ( getContentPreviewRowCount() - + getCallerTargetRowCount() + getServiceTargetRowCount() - + Math.ceil( - (float) mChooserListAdapter.getStandardTargetCount() - / getMaxTargetsPerRow()) + + getCallerAndRankedTargetRowCount() + Math.ceil( (float) mChooserListAdapter.getAlphaTargetCount() / getMaxTargetsPerRow()) @@ -2408,9 +2416,10 @@ public class ChooserActivity extends ResolverActivity { return 1; } - public int getCallerTargetRowCount() { + public int getCallerAndRankedTargetRowCount() { return (int) Math.ceil( - (float) mChooserListAdapter.getCallerTargetCount() / getMaxTargetsPerRow()); + ((float) mChooserListAdapter.getCallerTargetCount() + + mChooserListAdapter.getRankedTargetCount()) / getMaxTargetsPerRow()); } // There can be at most one row in the listview, that is internally @@ -2536,8 +2545,8 @@ public class ChooserActivity extends ResolverActivity { if (isDirectShare) { DirectShareViewHolder dsvh = (DirectShareViewHolder) holder; - setViewHeight(dsvh.getRow(0), holder.getMeasuredRowHeight()); - setViewHeight(dsvh.getRow(1), holder.getMeasuredRowHeight()); + setViewHeight(dsvh.getRow(0), dsvh.getMinRowHeight()); + setViewHeight(dsvh.getRow(1), dsvh.getMinRowHeight()); } viewGroup.setTag(holder); @@ -2592,10 +2601,8 @@ public class ChooserActivity extends ResolverActivity { if (startType != lastStartType || rowPosition == getContentPreviewRowCount()) { row.setBackground(mChooserRowLayer); - setVertPadding(row, 0, 0); } else { row.setBackground(null); - setVertPadding(row, 0, 0); } int columnCount = holder.getColumnCount(); @@ -2642,10 +2649,6 @@ public class ChooserActivity extends ResolverActivity { } } - private void setVertPadding(ViewGroup row, int top, int bottom) { - row.setPadding(row.getPaddingLeft(), top, row.getPaddingRight(), bottom); - } - int getFirstRowPosition(int row) { row -= getContentPreviewRowCount(); @@ -2825,6 +2828,10 @@ public class ChooserActivity extends ResolverActivity { return mDirectShareCurrHeight; } + public int getMinRowHeight() { + return mDirectShareMinHeight; + } + public void setViewVisibility(int i, int visibility) { final View v = getView(i); if (visibility == View.VISIBLE) { @@ -2847,15 +2854,20 @@ public class ChooserActivity extends ResolverActivity { } public void handleScroll(AbsListView view, int y, int oldy, int maxTargetsPerRow) { - if (mHideDirectShareExpansion) { - return; - } + // only exit early if fully collapsed, otherwise onListRebuilt() with shifting + // targets can lock us into an expanded mode + boolean notExpanded = mDirectShareCurrHeight == mDirectShareMinHeight; + if (notExpanded) { + if (mHideDirectShareExpansion) { + return; + } - // only expand if we have more than maxTargetsPerRow, and delay that decision - // until they start to scroll - if (mChooserListAdapter.getSelectableServiceTargetCount() <= maxTargetsPerRow) { - mHideDirectShareExpansion = true; - return; + // only expand if we have more than maxTargetsPerRow, and delay that decision + // until they start to scroll + if (mChooserListAdapter.getSelectableServiceTargetCount() <= maxTargetsPerRow) { + mHideDirectShareExpansion = true; + return; + } } int yDiff = (int) ((oldy - y) * DIRECT_SHARE_EXPANSION_RATE); @@ -3055,6 +3067,7 @@ public class ChooserActivity extends ResolverActivity { private int mRadius = 0; private Path mPath = new Path(); private Paint mOverlayPaint = new Paint(0); + private Paint mRoundRectPaint = new Paint(0); private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private String mExtraImageCount = null; @@ -3078,6 +3091,11 @@ public class ChooserActivity extends ResolverActivity { mOverlayPaint.setColor(0x99000000); mOverlayPaint.setStyle(Paint.Style.FILL); + mRoundRectPaint.setColor(context.getResources().getColor(R.color.chooser_row_divider)); + mRoundRectPaint.setStyle(Paint.Style.STROKE); + mRoundRectPaint.setStrokeWidth(context.getResources() + .getDimensionPixelSize(R.dimen.chooser_preview_image_border)); + mTextPaint.setColor(Color.WHITE); mTextPaint.setTextSize(context.getResources() .getDimensionPixelSize(R.dimen.chooser_preview_image_font_size)); @@ -3087,8 +3105,8 @@ public class ChooserActivity extends ResolverActivity { private void updatePath(int width, int height) { mPath.reset(); - int imageWidth = width - getPaddingRight(); - int imageHeight = height - getPaddingBottom(); + int imageWidth = width - getPaddingRight() - getPaddingLeft(); + int imageHeight = height - getPaddingBottom() - getPaddingTop(); mPath.addRoundRect(getPaddingLeft(), getPaddingTop(), imageWidth, imageHeight, mRadius, mRadius, Path.Direction.CW); } @@ -3120,7 +3138,6 @@ public class ChooserActivity extends ResolverActivity { updatePath(width, height); } - @Override protected void onDraw(Canvas canvas) { if (mRadius != 0) { @@ -3129,8 +3146,12 @@ public class ChooserActivity extends ResolverActivity { super.onDraw(canvas); + int x = getPaddingLeft(); + int y = getPaddingRight(); + int width = getWidth() - getPaddingRight() - getPaddingLeft(); + int height = getHeight() - getPaddingBottom() - getPaddingTop(); if (mExtraImageCount != null) { - canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mOverlayPaint); + canvas.drawRect(x, y, width, height, mOverlayPaint); int xPos = canvas.getWidth() / 2; int yPos = (int) ((canvas.getHeight() / 2.0f) @@ -3138,6 +3159,8 @@ public class ChooserActivity extends ResolverActivity { canvas.drawText(mExtraImageCount, xPos, yPos, mTextPaint); } + + canvas.drawRoundRect(x, y, width, height, mRadius, mRadius, mRoundRectPaint); } } } diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java index 7735d84ab751..015238788191 100644 --- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java +++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java @@ -16,11 +16,11 @@ package com.android.internal.app; -import com.android.internal.R; - import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityTaskManager; +import android.app.ActivityThread; +import android.app.IApplicationThread; import android.content.Intent; import android.content.IntentSender; import android.content.pm.ApplicationInfo; @@ -29,13 +29,14 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.RemoteException; import android.util.Log; -import android.util.TypedValue; import android.view.View; -import android.view.Window; import android.view.View.OnClickListener; +import android.view.Window; import android.widget.ImageView; import android.widget.TextView; +import com.android.internal.R; + /** * This activity is displayed when the system attempts to start an Intent for * which there is more than one matching activity, allowing the user to decide @@ -127,7 +128,10 @@ public class HeavyWeightSwitcherActivity extends Activity { private OnClickListener mSwitchOldListener = new OnClickListener() { public void onClick(View v) { try { - ActivityTaskManager.getService().moveTaskToFront(mCurTask, 0, null); + ActivityThread thread = ActivityThread.currentActivityThread(); + IApplicationThread appThread = thread.getApplicationThread(); + ActivityTaskManager.getService().moveTaskToFront(appThread, getPackageName(), + mCurTask, 0, null); } catch (RemoteException e) { } finish(); diff --git a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java b/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java index bf151c39271a..7bd7acfab9f9 100644 --- a/core/java/com/android/internal/colorextraction/drawable/GradientDrawable.java +++ b/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java @@ -11,7 +11,7 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.internal.colorextraction.drawable; @@ -21,64 +21,42 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.Context; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; -import android.graphics.RadialGradient; -import android.graphics.Rect; -import android.graphics.Shader; import android.graphics.Xfermode; import android.graphics.drawable.Drawable; import android.view.animation.DecelerateInterpolator; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.graphics.ColorUtils; /** - * Draws a gradient based on a Palette + * Drawable used on SysUI scrims. */ -public class GradientDrawable extends Drawable { - private static final String TAG = "GradientDrawable"; - - private static final float CENTRALIZED_CIRCLE_1 = -2; - private static final int GRADIENT_RADIUS = 480; // in dp +public class ScrimDrawable extends Drawable { + private static final String TAG = "ScrimDrawable"; private static final long COLOR_ANIMATION_DURATION = 2000; - private int mAlpha = 255; - - private float mDensity; private final Paint mPaint; - private final Rect mWindowBounds; - private final Splat mSplat; - + private int mAlpha = 255; private int mMainColor; - private int mSecondaryColor; private ValueAnimator mColorAnimation; private int mMainColorTo; - private int mSecondaryColorTo; - - public GradientDrawable(@NonNull Context context) { - mDensity = context.getResources().getDisplayMetrics().density; - mSplat = new Splat(0.50f, 1.00f, GRADIENT_RADIUS, CENTRALIZED_CIRCLE_1); - mWindowBounds = new Rect(); + public ScrimDrawable() { mPaint = new Paint(); mPaint.setStyle(Paint.Style.FILL); } - public void setColors(@NonNull ColorExtractor.GradientColors colors) { - setColors(colors.getMainColor(), colors.getSecondaryColor(), true); - } - - public void setColors(@NonNull ColorExtractor.GradientColors colors, boolean animated) { - setColors(colors.getMainColor(), colors.getSecondaryColor(), animated); - } - - public void setColors(int mainColor, int secondaryColor, boolean animated) { - if (mainColor == mMainColorTo && secondaryColor == mSecondaryColorTo) { + /** + * Sets the background color. + * @param mainColor the color. + * @param animated if transition should be interpolated. + */ + public void setColor(int mainColor, boolean animated) { + if (mainColor == mMainColorTo) { return; } @@ -87,19 +65,15 @@ public class GradientDrawable extends Drawable { } mMainColorTo = mainColor; - mSecondaryColorTo = mainColor; if (animated) { final int mainFrom = mMainColor; - final int secFrom = mSecondaryColor; ValueAnimator anim = ValueAnimator.ofFloat(0, 1); anim.setDuration(COLOR_ANIMATION_DURATION); anim.addUpdateListener(animation -> { float ratio = (float) animation.getAnimatedValue(); mMainColor = ColorUtils.blendARGB(mainFrom, mainColor, ratio); - mSecondaryColor = ColorUtils.blendARGB(secFrom, secondaryColor, ratio); - buildPaints(); invalidateSelf(); }); anim.addListener(new AnimatorListenerAdapter() { @@ -115,8 +89,6 @@ public class GradientDrawable extends Drawable { mColorAnimation = anim; } else { mMainColor = mainColor; - mSecondaryColor = secondaryColor; - buildPaints(); invalidateSelf(); } } @@ -125,7 +97,6 @@ public class GradientDrawable extends Drawable { public void setAlpha(int alpha) { if (alpha != mAlpha) { mAlpha = alpha; - mPaint.setAlpha(mAlpha); invalidateSelf(); } } @@ -156,73 +127,15 @@ public class GradientDrawable extends Drawable { return PixelFormat.TRANSLUCENT; } - public void setScreenSize(int width, int height) { - mWindowBounds.set(0, 0, width, height); - setBounds(0, 0, width, height); - buildPaints(); - } - - private void buildPaints() { - Rect bounds = mWindowBounds; - if (bounds.width() == 0) { - return; - } - - float w = bounds.width(); - float h = bounds.height(); - - float x = mSplat.x * w; - float y = mSplat.y * h; - - float radius = mSplat.radius * mDensity; - - // When we have only a single alpha gradient, we increase quality - // (avoiding banding) by merging the background solid color into - // the gradient directly - RadialGradient radialGradient = new RadialGradient(x, y, radius, - mSecondaryColor, mMainColor, Shader.TileMode.CLAMP); - mPaint.setShader(radialGradient); - } - @Override public void draw(@NonNull Canvas canvas) { - Rect bounds = mWindowBounds; - if (bounds.width() == 0) { - throw new IllegalStateException("You need to call setScreenSize before drawing."); - } - - // Splat each gradient - float w = bounds.width(); - float h = bounds.height(); - - float x = mSplat.x * w; - float y = mSplat.y * h; - - float radius = Math.max(w, h); - canvas.drawRect(x - radius, y - radius, x + radius, y + radius, mPaint); + mPaint.setColor(mMainColor); + mPaint.setAlpha(mAlpha); + canvas.drawRect(getBounds(), mPaint); } @VisibleForTesting public int getMainColor() { return mMainColor; } - - @VisibleForTesting - public int getSecondaryColor() { - return mSecondaryColor; - } - - static final class Splat { - final float x; - final float y; - final float radius; - final float colorIndex; - - Splat(float x, float y, float radius, float colorIndex) { - this.x = x; - this.y = y; - this.radius = radius; - this.colorIndex = colorIndex; - } - } -}
\ No newline at end of file +} diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java index b9aab21b1e4c..d2e71c85e7f9 100644 --- a/core/java/com/android/internal/colorextraction/types/Tonal.java +++ b/core/java/com/android/internal/colorextraction/types/Tonal.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WallpaperColors; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Color; import android.util.Log; import android.util.MathUtils; @@ -51,11 +52,13 @@ public class Tonal implements ExtractionType { private static final boolean DEBUG = true; - public static final int MAIN_COLOR_LIGHT = 0xffe0e0e0; - public static final int MAIN_COLOR_DARK = 0xff212121; + public static final int MAIN_COLOR_LIGHT = 0xffdadce0; + public static final int MAIN_COLOR_DARK = 0xff202124; + public static final int MAIN_COLOR_REGULAR = 0xff000000; private final TonalPalette mGreyPalette; private final ArrayList<TonalPalette> mTonalPalettes; + private final Context mContext; // Temporary variable to avoid allocations private float[] mTmpHSL = new float[3]; @@ -64,6 +67,7 @@ public class Tonal implements ExtractionType { ConfigParser parser = new ConfigParser(context); mTonalPalettes = parser.getTonalPalettes(); + mContext = context; mGreyPalette = mTonalPalettes.get(0); mTonalPalettes.remove(0); @@ -247,7 +251,20 @@ public class Tonal implements ExtractionType { boolean light = inWallpaperColors != null && (inWallpaperColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0; - final int color = light ? MAIN_COLOR_LIGHT : MAIN_COLOR_DARK; + boolean dark = inWallpaperColors != null + && (inWallpaperColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_THEME) + != 0; + final int color; + final boolean inNightMode = (mContext.getResources().getConfiguration().uiMode + & android.content.res.Configuration.UI_MODE_NIGHT_MASK) + == Configuration.UI_MODE_NIGHT_YES; + if (light) { + color = MAIN_COLOR_LIGHT; + } else if (dark || inNightMode) { + color = MAIN_COLOR_DARK; + } else { + color = MAIN_COLOR_REGULAR; + } final float[] hsl = new float[3]; ColorUtils.colorToHSL(color, hsl); diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java index b0855f494ffd..1aef573f5189 100644 --- a/core/java/com/android/internal/os/RoSystemProperties.java +++ b/core/java/com/android/internal/os/RoSystemProperties.java @@ -60,7 +60,7 @@ public class RoSystemProperties { public static final boolean FW_SYSTEM_USER_SPLIT = SystemProperties.getBoolean("ro.fw.system_user_split", false); public static final boolean MULTIUSER_HEADLESS_SYSTEM_USER = - SystemProperties.getBoolean("ro.fw.multiuser.headless_system_user", false); + SystemProperties.getBoolean("ro.fw.multiuser.headless_system_user", true); // ------ ro.crypto.* -------- // public static final CryptoProperties.state_values CRYPTO_STATE = diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 1c0030d20c63..d945e139dd0f 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -38,6 +38,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; + import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL; import android.animation.Animator; @@ -125,6 +126,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // The height of a window which has not in DIP. private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5; + private static final int SCRIM_LIGHT = 0x99ffffff; // 60% white + public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES = new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, Gravity.TOP, Gravity.LEFT, Gravity.RIGHT, @@ -1237,19 +1240,31 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private int calculateStatusBarColor() { return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_STATUS, - mSemiTransparentBarColor, mWindow.mStatusBarColor); + mSemiTransparentBarColor, mWindow.mStatusBarColor, + getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_STATUS_BAR, + mWindow.mEnsureStatusBarContrastWhenTransparent); } private int calculateNavigationBarColor() { return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_NAVIGATION, - mSemiTransparentBarColor, mWindow.mNavigationBarColor); + mSemiTransparentBarColor, mWindow.mNavigationBarColor, + getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + mWindow.mEnsureNavigationBarContrastWhenTransparent + && getContext().getResources().getBoolean(R.bool.config_navBarNeedsScrim)); } public static int calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor, - int barColor) { - return (flags & translucentFlag) != 0 ? semiTransparentBarColor - : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? barColor - : Color.BLACK; + int barColor, int sysuiVis, int lightSysuiFlag, boolean scrimTransparent) { + if ((flags & translucentFlag) != 0) { + return semiTransparentBarColor; + } else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { + return Color.BLACK; + } else if (scrimTransparent && barColor == Color.TRANSPARENT) { + boolean light = (sysuiVis & lightSysuiFlag) != 0; + return light ? SCRIM_LIGHT : semiTransparentBarColor; + } else { + return barColor; + } } private int getCurrentColor(ColorViewState state) { diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 04559e4e55c6..16d6c52d82ea 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -50,6 +50,7 @@ import android.media.AudioManager; import android.media.session.MediaController; import android.media.session.MediaSessionManager; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Parcel; @@ -247,6 +248,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private boolean mForcedStatusBarColor = false; private boolean mForcedNavigationBarColor = false; + boolean mEnsureStatusBarContrastWhenTransparent; + boolean mEnsureNavigationBarContrastWhenTransparent; + @UnsupportedAppUsage private CharSequence mTitle = null; @@ -2439,6 +2443,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB; final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP; + final boolean targetPreQ = targetSdk < Build.VERSION_CODES.Q; final boolean targetHcNeedsOptions = context.getResources().getBoolean( R.bool.target_honeycomb_needs_options_menu); final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE); @@ -2457,6 +2462,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mNavigationBarDividerColor = a.getColor(R.styleable.Window_navigationBarDividerColor, 0x00000000); } + if (!targetPreQ) { + mEnsureStatusBarContrastWhenTransparent = a.getBoolean( + R.styleable.Window_ensureStatusBarContrastWhenTransparent, false); + mEnsureNavigationBarContrastWhenTransparent = a.getBoolean( + R.styleable.Window_ensureNavigationBarContrastWhenTransparent, true); + } WindowManager.LayoutParams params = getAttributes(); @@ -3845,6 +3856,32 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return mNavigationBarDividerColor; } + @Override + public void setEnsureStatusBarContrastWhenTransparent(boolean ensureContrast) { + mEnsureStatusBarContrastWhenTransparent = ensureContrast; + if (mDecor != null) { + mDecor.updateColorViews(null, false /* animate */); + } + } + + @Override + public boolean isEnsureStatusBarContrastWhenTransparent() { + return mEnsureStatusBarContrastWhenTransparent; + } + + @Override + public void setEnsureNavigationBarContrastWhenTransparent(boolean ensureContrast) { + mEnsureNavigationBarContrastWhenTransparent = ensureContrast; + if (mDecor != null) { + mDecor.updateColorViews(null, false /* animate */); + } + } + + @Override + public boolean isEnsureNavigationBarContrastWhenTransparent() { + return mEnsureNavigationBarContrastWhenTransparent; + } + public void setIsStartingWindow(boolean isStartingWindow) { mIsStartingWindow = isStartingWindow; } diff --git a/core/java/com/android/internal/util/MimeIconUtils.java b/core/java/com/android/internal/util/MimeIconUtils.java index 0b5fa6d4538a..8523b4ea9b43 100644 --- a/core/java/com/android/internal/util/MimeIconUtils.java +++ b/core/java/com/android/internal/util/MimeIconUtils.java @@ -18,7 +18,7 @@ package com.android.internal.util; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ContentResolver.TypeInfo; +import android.content.ContentResolver.MimeTypeInfo; import android.content.res.Resources; import android.graphics.drawable.Icon; import android.text.TextUtils; @@ -34,9 +34,9 @@ import java.util.Objects; public class MimeIconUtils { @GuardedBy("sCache") - private static final ArrayMap<String, TypeInfo> sCache = new ArrayMap<>(); + private static final ArrayMap<String, MimeTypeInfo> sCache = new ArrayMap<>(); - private static TypeInfo buildTypeInfo(String mimeType, int iconId, + private static MimeTypeInfo buildTypeInfo(String mimeType, int iconId, int labelId, int extLabelId) { final Resources res = Resources.getSystem(); @@ -49,10 +49,10 @@ public class MimeIconUtils { label = res.getString(labelId); } - return new TypeInfo(Icon.createWithResource(res, iconId), label, label); + return new MimeTypeInfo(Icon.createWithResource(res, iconId), label, label); } - private static @Nullable TypeInfo buildTypeInfo(@NonNull String mimeType) { + private static @Nullable MimeTypeInfo buildTypeInfo(@NonNull String mimeType) { switch (mimeType) { case "inode/directory": case "vnd.android.document/directory": @@ -222,7 +222,7 @@ public class MimeIconUtils { } } - private static @Nullable TypeInfo buildGenericTypeInfo(@NonNull String mimeType) { + private static @Nullable MimeTypeInfo buildGenericTypeInfo(@NonNull String mimeType) { // Look for partial matches if (mimeType.startsWith("audio/")) { return buildTypeInfo(mimeType, R.drawable.ic_doc_audio, @@ -252,12 +252,12 @@ public class MimeIconUtils { R.string.mime_type_generic, R.string.mime_type_generic_ext); } - public static @NonNull TypeInfo getTypeInfo(@NonNull String mimeType) { + public static @NonNull MimeTypeInfo getTypeInfo(@NonNull String mimeType) { // Normalize MIME type mimeType = mimeType.toLowerCase(Locale.US); synchronized (sCache) { - TypeInfo res = sCache.get(mimeType); + MimeTypeInfo res = sCache.get(mimeType); if (res == null) { res = buildTypeInfo(mimeType); sCache.put(mimeType, res); diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp index 29051f14c72f..1c247cbb7743 100644 --- a/core/jni/android_hardware_camera2_DngCreator.cpp +++ b/core/jni/android_hardware_camera2_DngCreator.cpp @@ -19,6 +19,7 @@ #include <inttypes.h> #include <string.h> #include <algorithm> +#include <array> #include <memory> #include <vector> #include <cmath> @@ -976,6 +977,153 @@ static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8 return OK; } +static void undistort(/*inout*/double& x, /*inout*/double& y, + const std::array<float, 6>& distortion, + const float cx, const float cy, const float f) { + double xp = (x - cx) / f; + double yp = (y - cy) / f; + + double x2 = xp * xp; + double y2 = yp * yp; + double r2 = x2 + y2; + double xy2 = 2.0 * xp * yp; + + const float k0 = distortion[0]; + const float k1 = distortion[1]; + const float k2 = distortion[2]; + const float k3 = distortion[3]; + const float p1 = distortion[4]; + const float p2 = distortion[5]; + + double kr = k0 + ((k3 * r2 + k2) * r2 + k1) * r2; + double xpp = xp * kr + p1 * xy2 + p2 * (r2 + 2.0 * x2); + double ypp = yp * kr + p1 * (r2 + 2.0 * y2) + p2 * xy2; + + x = xpp * f + cx; + y = ypp * f + cy; + return; +} + +static inline bool unDistortWithinPreCorrArray( + double x, double y, + const std::array<float, 6>& distortion, + const float cx, const float cy, const float f, + int preCorrW, int preCorrH) { + undistort(x, y, distortion, cx, cy, f); + if (x < 0.0 || y < 0.0 || x > preCorrW - 1 || y > preCorrH - 1) { + return false; + } + return true; +} + +static inline bool boxWithinPrecorrectionArray( + int left, int top, int right, int bottom, + const std::array<float, 6>& distortion, + const float& cx, const float& cy, const float& f, + const int& preCorrW, const int& preCorrH){ + // Top row + if (!unDistortWithinPreCorrArray(left, top, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + if (!unDistortWithinPreCorrArray(cx, top, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + if (!unDistortWithinPreCorrArray(right, top, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + // Middle row + if (!unDistortWithinPreCorrArray(left, cy, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + if (!unDistortWithinPreCorrArray(right, cy, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + // Bottom row + if (!unDistortWithinPreCorrArray(left, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + if (!unDistortWithinPreCorrArray(cx, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + + if (!unDistortWithinPreCorrArray(right, bottom, distortion, cx, cy, f, preCorrW, preCorrH)) { + return false; + } + return true; +} + +static inline bool scaledBoxWithinPrecorrectionArray( + double scale/*must be <= 1.0*/, + const std::array<float, 6>& distortion, + const float cx, const float cy, const float f, + const int preCorrW, const int preCorrH){ + + double left = cx * (1.0 - scale); + double right = (preCorrW - 1) * scale + cx * (1.0 - scale); + double top = cy * (1.0 - scale); + double bottom = (preCorrH - 1) * scale + cy * (1.0 - scale); + + return boxWithinPrecorrectionArray(left, top, right, bottom, + distortion, cx, cy, f, preCorrW, preCorrH); +} + +static status_t findPostCorrectionScale( + double stepSize, double minScale, + const std::array<float, 6>& distortion, + const float cx, const float cy, const float f, + const int preCorrW, const int preCorrH, + /*out*/ double* outScale) { + if (outScale == nullptr) { + ALOGE("%s: outScale must not be null", __FUNCTION__); + return BAD_VALUE; + } + + for (double scale = 1.0; scale > minScale; scale -= stepSize) { + if (scaledBoxWithinPrecorrectionArray( + scale, distortion, cx, cy, f, preCorrW, preCorrH)) { + *outScale = scale; + return OK; + } + } + ALOGE("%s: cannot find cropping scale for lens distortion: stepSize %f, minScale %f", + __FUNCTION__, stepSize, minScale); + return BAD_VALUE; +} + +// Apply a scale factor to distortion coefficients so that the image is zoomed out and all pixels +// are sampled within the precorrection array +static void normalizeLensDistortion( + /*inout*/std::array<float, 6>& distortion, + float cx, float cy, float f, int preCorrW, int preCorrH) { + ALOGV("%s: distortion [%f, %f, %f, %f, %f, %f], (cx,cy) (%f, %f), f %f, (W,H) (%d, %d)", + __FUNCTION__, distortion[0], distortion[1], distortion[2], + distortion[3], distortion[4], distortion[5], + cx, cy, f, preCorrW, preCorrH); + + // Only update distortion coeffients if we can find a good bounding box + double scale = 1.0; + if (OK == findPostCorrectionScale(0.002, 0.5, + distortion, cx, cy, f, preCorrW, preCorrH, + /*out*/&scale)) { + ALOGV("%s: scaling distortion coefficients by %f", __FUNCTION__, scale); + // The formula: + // xc = xi * (k0 + k1*r^2 + k2*r^4 + k3*r^6) + k4 * (2*xi*yi) + k5 * (r^2 + 2*xi^2) + // To create effective zoom we want to replace xi by xi *m, yi by yi*m and r^2 by r^2*m^2 + // Factor the extra m power terms into k0~k6 + std::array<float, 6> scalePowers = {1, 3, 5, 7, 2, 2}; + for (size_t i = 0; i < 6; i++) { + distortion[i] *= pow(scale, scalePowers[i]); + } + } + return; +} + // ---------------------------------------------------------------------------- extern "C" { @@ -1086,9 +1234,9 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image uint32_t pixHeight = static_cast<uint32_t>(pixelArrayEntry.data.i32[1]); if (!((imageWidth == preWidth && imageHeight == preHeight) || - (imageWidth == pixWidth && imageHeight == pixHeight))) { + (imageWidth == pixWidth && imageHeight == pixHeight))) { jniThrowException(env, "java/lang/AssertionError", - "Height and width of imate buffer did not match height and width of" + "Height and width of image buffer did not match height and width of" "either the preCorrectionActiveArraySize or the pixelArraySize."); return nullptr; } @@ -1793,7 +1941,7 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image status_t err = OK; // Set up rectilinear distortion correction - float distortion[6] {1.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + std::array<float, 6> distortion = {1.f, 0.f, 0.f, 0.f, 0.f, 0.f}; bool gotDistortion = false; camera_metadata_entry entry4 = @@ -1810,6 +1958,19 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image results.find(ANDROID_LENS_DISTORTION); if (entry3.count == 5) { gotDistortion = true; + + + // Scale the distortion coefficients to create a zoom in warpped image so that all + // pixels are drawn within input image. + for (size_t i = 0; i < entry3.count; i++) { + distortion[i+1] = entry3.data.f[i]; + } + + // TODO b/118690688: deal with the case where RAW size != preCorrSize + if (preWidth == imageWidth && preHeight == imageHeight) { + normalizeLensDistortion(distortion, cx, cy, f, preWidth, preHeight); + } + float m_x = std::fmaxf(preWidth-1 - cx, cx); float m_y = std::fmaxf(preHeight-1 - cy, cy); float m_sq = m_x*m_x + m_y*m_y; @@ -1831,7 +1992,7 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image m / f }; for (size_t i = 0; i < entry3.count; i++) { - distortion[i+1] = convCoeff[i] * entry3.data.f[i]; + distortion[i+1] *= convCoeff[i]; } } else { entry3 = results.find(ANDROID_LENS_RADIAL_DISTORTION); @@ -1859,8 +2020,8 @@ static sp<TiffWriter> DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image } } if (gotDistortion) { - err = builder.addWarpRectilinearForMetadata(distortion, preWidth, preHeight, cx, - cy); + err = builder.addWarpRectilinearForMetadata( + distortion.data(), preWidth, preHeight, cx, cy); if (err != OK) { ALOGE("%s: Could not add distortion correction.", __FUNCTION__); jniThrowRuntimeException(env, "failed to add distortion correction."); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b94eb1626e37..cc3b3a4c7ccb 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1637,8 +1637,8 @@ android:label="@string/permlab_bluetooth" android:protectionLevel="normal" /> - <!-- @SystemApi Allows an application to suspend other apps, which will prevent the user - from using them until they are unsuspended. + <!-- @SystemApi @TestApi Allows an application to suspend other apps, which will prevent the + user from using them until they are unsuspended. @hide --> <permission android:name="android.permission.SUSPEND_APPS" diff --git a/core/res/res/drawable/ic_bluetooth_share_icon.xml b/core/res/res/drawable/ic_bluetooth_share_icon.xml index 2446402be93f..2152af55d5b6 100644 --- a/core/res/res/drawable/ic_bluetooth_share_icon.xml +++ b/core/res/res/drawable/ic_bluetooth_share_icon.xml @@ -19,9 +19,9 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24" - android:tint="@android:color/accent_device_default"> + android:tint="@android:color/accent_device_default_light"> <path android:fillColor="@android:color/white" android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" /> -</vector>
\ No newline at end of file +</vector> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 132e51525ec5..c7c293b4e5ac 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -309,8 +309,8 @@ <string name="permgroupdesc_microphone" msgid="4988812113943554584">"تسجيل الصوت"</string> <string name="permgrouprequest_microphone" msgid="9167492350681916038">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بتسجيل الصوت؟"</string> <string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"النشاط البدني"</string> - <string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"الوصول إلى نشاطك البدني"</string> - <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"هل تريد السماح للتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى نشاطك البدني؟"</string> + <string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"الوصول إلى بيانات نشاطك البدني"</string> + <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"هل تريد السماح للتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالوصول إلى بيانات نشاطك البدني؟"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"الكاميرا"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"التقاط صور وتسجيل فيديو"</string> <string name="permgrouprequest_camera" msgid="1299833592069671756">"هل تريد السماح لتطبيق <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> بالتقاط صور وتسجيل فيديو؟"</string> @@ -1897,7 +1897,7 @@ <string name="package_deleted_device_owner" msgid="2307122077550236438">"تم الحذف بواسطة المشرف"</string> <string name="confirm_battery_saver" msgid="639106420541753635">"موافق"</string> <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"لإطالة عمر البطارية، تساعد ميزة \"توفير شحن البطارية\" على إيقاف أو تقييد نشاط الخلفية وبعض التأثيرات المرئية وغيرها من الميزات التي تستنفد طاقة البطارية. "<annotation id="url">"مزيد من المعلومات"</annotation></string> - <string name="battery_saver_description" msgid="6413346684861241431">"لإطالة عمر البطارية، تساعد ميزة \"توفير شحن البطارية\" على إيقاف أو تقييد نشاط الخلفية وبعض التأثيرات المرئية وغيرها من الميزات التي تستنفد طاقة البطارية."</string> + <string name="battery_saver_description" msgid="6413346684861241431">"لإطالة عمر البطارية، تساعد ميزة \"توفير شحن البطارية\" على إيقاف أو تقييد النشاط في الخلفية وبعض التأثيرات المرئية وغيرها من الميزات التي تستنفد طاقة البطارية."</string> <string name="data_saver_description" msgid="6015391409098303235">"للمساعدة في خفض استخدام البيانات، يمنع توفير البيانات بعض التطبيقات من إرسال البيانات وتلقيها في الخلفية. يمكن للتطبيق الذي تستخدمه الآن الوصول إلى البيانات، ولكن لا يمكنه تنفيذ ذلك كثيرًا. وهذا يعني أن الصور على سبيل المثال لا تظهر حتى تنقر عليها."</string> <string name="data_saver_enable_title" msgid="4674073932722787417">"هل تريد تشغيل توفير البيانات؟"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"تشغيل"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 63437e398e25..778442bff128 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -519,8 +519,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"এপক আপোনাৰ ফট’ সংগ্ৰহ সালসলনি কৰিবলৈ দিয়ে।"</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"আপোনাৰ মিডিয়া সংগ্ৰহৰ অৱস্থান পঢ়িবলৈ"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"এপক আপোনাৰ মিডিয়া সংগ্ৰহৰ অৱস্থান পঢ়িবলৈ দিয়ে।"</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"এইজন আপুনিয়েই বুলি সত্যাপন কৰক"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"বায়োমেট্ৰিক হাৰ্ডৱেৰ উপলব্ধ নহয়"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"বিশ্বাসযোগ্যতাৰ প্ৰমাণীকৰণ বাতিল কৰা হৈছে"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"চিনাক্ত কৰিব পৰা নাই"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 1d0ae2d2706c..144d99c06005 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -1988,7 +1988,7 @@ <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"Сховішча на прыладзе"</string> <string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"Адладка USB"</string> <string name="time_picker_hour_label" msgid="2979075098868106450">"гадз"</string> - <string name="time_picker_minute_label" msgid="5168864173796598399">"хвіліна"</string> + <string name="time_picker_minute_label" msgid="5168864173796598399">"хв"</string> <string name="time_picker_header_text" msgid="143536825321922567">"Задаць час"</string> <string name="time_picker_input_error" msgid="7574999942502513765">"Увядзіце дапушчальны час"</string> <string name="time_picker_prompt_label" msgid="7588093983899966783">"Увядзіце час"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 47da68abdb81..21a11950fcbb 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -1920,7 +1920,7 @@ <string name="device_storage_monitor_notification_channel" msgid="3295871267414816228">"Хранилище на устройството"</string> <string name="adb_debugging_notification_channel_tv" msgid="5537766997350092316">"Отстраняване на грешки през USB"</string> <string name="time_picker_hour_label" msgid="2979075098868106450">"час"</string> - <string name="time_picker_minute_label" msgid="5168864173796598399">"минута"</string> + <string name="time_picker_minute_label" msgid="5168864173796598399">"минути"</string> <string name="time_picker_header_text" msgid="143536825321922567">"Задаване на час"</string> <string name="time_picker_input_error" msgid="7574999942502513765">"Въведете валиден час"</string> <string name="time_picker_prompt_label" msgid="7588093983899966783">"Въведете часа"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 8198ee4d873b..dddd0fc4c760 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -283,7 +283,7 @@ <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে এই ডিভাইসের লোকেশন অ্যাক্সেস করতে দেবেন?"</string> <string name="permgrouprequestdetail_location" msgid="1347189607421252902">"আপনি এই অ্যাপ ব্যবহার করার সময়েই শুধু সেটি আপনার লোকেশন অ্যাক্সেস করতে পারবে"</string> <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> অ্যাপকে এই ডিভাইসের লোকেশন <b>সব সময়</b> অ্যাক্সেস করার অনুমতি দিতে চান?"</string> - <string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"এই অ্যাপ ব্যবহার করার সময় বর্তমানে সেটি আপনার লোকেশন অ্যাক্সেস করতে পারে"</string> + <string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"আপনি যখন অ্যাপটি ব্যবহার করবেন শুধুমাত্র তখনই অ্যাপটি বর্তমান লোকেশন অ্যাক্সেস করতে পারবে।"</string> <string name="permgrouplab_calendar" msgid="5863508437783683902">"ক্যালেন্ডার"</string> <string name="permgroupdesc_calendar" msgid="3889615280211184106">"আপনার ক্যালেন্ডারে অ্যাক্সেস"</string> <string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-কে আপনার ক্যালেন্ডারে অ্যাক্সেস দেবেন?"</string> @@ -504,7 +504,7 @@ <string name="permlab_disableKeyguard" msgid="3598496301486439258">"আপনার স্ক্রিন লক অক্ষম করুন"</string> <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"কী-লক এবং যেকোনো সংশ্লিষ্ট পাসওয়ার্ড সুরক্ষা অক্ষম করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ উদাহরণস্বরূপ, একটি ইনকামিং ফোন কল গ্রহণ করার সময়ে ফোনটি কী-লক অক্ষম করে, তারপরে কল শেষ হয়ে গেলে কী-লকটিকে আবার সক্ষম করে৷"</string> <string name="permlab_requestPasswordComplexity" msgid="202650535669249674">"স্ক্রিন লকের জটিলতা জানার অনুরোধ করুন"</string> - <string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"এটি অ্যাপটিকে স্ক্রিন লকের জটিলতার লেভেল বুঝতে সাহায্য করে (খুব বেশি, মাঝারি, অল্প জটিল বা কোনও জটিলতা নেই), যা স্ক্রিন লকটি সম্ভত কত দীর্ঘ ও সেটির ধরন কীরকম, তার ইঙ্গিত দেয়। এই অ্যাপটি একটি নির্দিষ্ট লেভেল পর্যন্ত স্ক্রিন লক আপডেট করার সাজেশনও দিতে পারে, তবে ব্যবহারকারী তা উপেক্ষা করে অন্য কোথাও চলে যেতে পারেন। মনে রাখবেন যে স্ক্রিন লক প্লেন টেক্সট হিসেবে সংরক্ষণ করা হয় না, তাই অ্যাপ কখনও আসল পাসওয়ার্ড জানতে পারে না।"</string> + <string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"এটি অ্যাপটিকে স্ক্রিন লকের জটিলতার লেভেল বুঝতে সাহায্য করে (খুব বেশি, মাঝারি, অল্প জটিল বা কোনও জটিলতা নেই), যা স্ক্রিন লকটি সম্ভবত কত দীর্ঘ ও সেটির ধরন কীরকম, তার ইঙ্গিত দেয়। এই অ্যাপটি একটি নির্দিষ্ট লেভেল পর্যন্ত স্ক্রিন লক আপডেট করার সাজেশনও দিতে পারে, তবে ব্যবহারকারী তা উপেক্ষা করে অন্য কোথাও চলে যেতে পারেন। মনে রাখবেন যে স্ক্রিন লক প্লেন টেক্সট হিসেবে সংরক্ষণ করা হয় না, তাই অ্যাপ কখনও আসল পাসওয়ার্ড জানতে পারে না।"</string> <string name="permlab_useBiometric" msgid="8837753668509919318">"বায়োমেট্রিক হার্ডওয়্যার ব্যবহার করুন"</string> <string name="permdesc_useBiometric" msgid="8389855232721612926">"অ্যাপটিকে যাচাইকরণের জন্য বায়োমেট্রিক হার্ডওয়্যার ব্যবহার করার অনুমতি দেয়"</string> <string name="permlab_manageFingerprint" msgid="5640858826254575638">"আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার পরিচালনা করুন"</string> @@ -519,8 +519,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"অ্যাপকে আপনার ফটো সংগ্রহ পরিবর্তন করার অনুমতি দিন।"</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"ডিয়া সংগ্রহ থেকে লোকেশন দেখতে দিন"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"আপনার মিডিয়া সংগ্রহ থেকে লোকেশন দেখতে অ্যাপকে অনুমতি দিন।"</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"আপনার পরিচয় যাচাই করুন"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"বায়োমেট্রিক হার্ডওয়্যার পাওয়া যাবে না"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"যাচাইকরণ বাতিল হয়েছে"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"স্বীকৃত নয়"</string> @@ -1798,8 +1797,8 @@ <string name="package_updated_device_owner" msgid="1847154566357862089">"আপনার প্রশাসক আপডেট করেছেন"</string> <string name="package_deleted_device_owner" msgid="2307122077550236438">"আপনার প্রশাসক মুছে দিয়েছেন"</string> <string name="confirm_battery_saver" msgid="639106420541753635">"ঠিক আছে"</string> - <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"ব্যাটারি আরও বেশিক্ষণ চালাতে ব্যাটারি সেভার ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট ও অতিরিক্ত শক্তি খরচ হয় এমন অন্যান্য ফিচার বন্ধ বা সীমাবদ্ধ করে। "<annotation id="url">"আরও জানুন"</annotation></string> - <string name="battery_saver_description" msgid="6413346684861241431">"ব্যাটারি আরও বেশিক্ষণ চালাতে ব্যাটারি সেভার ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজ্যুয়াল এফেক্ট ও অতিরিক্ত শক্তি খরচ হয় এমন অন্যান্য ফিচার বন্ধ বা সীমাবদ্ধ করে।"</string> + <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"ব্যাটারি আরও বেশিক্ষণ চালাতে ব্যাটারি সেভার ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজুয়াল এফেক্ট ও অতিরিক্ত শক্তি খরচ হয় এমন অন্যান্য ফিচার বন্ধ বা সীমাবদ্ধ করে। "<annotation id="url">"আরও জানুন"</annotation></string> + <string name="battery_saver_description" msgid="6413346684861241431">"ব্যাটারি আরও বেশিক্ষণ চালাতে ব্যাটারি সেভার ব্যাকগ্রাউন্ড অ্যাক্টিভিটি, কিছু ভিজুয়াল এফেক্ট ও অতিরিক্ত শক্তি খরচ হয় এমন অন্যান্য ফিচার বন্ধ বা সীমাবদ্ধ করে।"</string> <string name="data_saver_description" msgid="6015391409098303235">"ডেটার ব্যবহার কমাতে সহায়তা করার জন্য, ডেটা সেভার ব্যাকগ্রাউন্ডে কিছু অ্যাপ্লিকেশনকে ডেটা পাঠাতে বা গ্রহণ করতে বাধা দেয়৷ আপনি বর্তমানে এমন একটি অ্যাপ্লিকেশন ব্যবহার করছেন যেটি ডেটা অ্যাক্সেস করতে পারে, তবে সেটি কমই করে৷ এর ফলে যা হতে পারে, উদাহরণস্বরূপ, আপনি ছবির উপর ট্যাপ না করা পর্যন্ত সেগুলি দেখানো হবে না৷"</string> <string name="data_saver_enable_title" msgid="4674073932722787417">"ডেটা সেভার চালু করবেন?"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"চালু করুন"</string> @@ -1995,8 +1994,8 @@ <string name="dynamic_mode_notification_title" msgid="508815255807182035">"সাধারণত যখন চার্জ দেন, তার আগে চার্জ শেষ হয়ে যেতে পারে"</string> <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"ডিভাইস বেশিক্ষণ চালু রাখতে ব্যাটারি সেভার চালু করা হয়েছে"</string> <string name="battery_saver_notification_channel_name" msgid="2083316159716201806">"ব্যাটারি সেভার"</string> - <string name="battery_saver_sticky_disabled_notification_title" msgid="6376147579378764641">"চার্জ আবার কম না হওয়া পর্যন্ত ব্যাটারি সেভার আবার চালু হবে না"</string> - <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"ব্যাটারি পর্যাপ্ত পরিমাণ চার্জ করা হয়েছে। চার্জ আবার কম না হওয়া পর্যন্ত ব্যাটারি সেভার আবার চালু হবে না।"</string> + <string name="battery_saver_sticky_disabled_notification_title" msgid="6376147579378764641">"চার্জ কম না হওয়া পর্যন্ত ব্যাটারি সেভার আবার চালু হবে না"</string> + <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"ব্যাটারি পর্যাপ্ত পরিমাণ চার্জ করা হয়েছে। চার্জ কম না হওয়া পর্যন্ত ব্যাটারি সেভার আবার চালু হবে না।"</string> <string name="battery_saver_charged_notification_title" product="default" msgid="2960978289873161288">"ফোনে <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> চার্জ আছে"</string> <string name="battery_saver_charged_notification_title" product="tablet" msgid="7555713825806482451">"ট্যাবলেটে <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> চার্জ আছে"</string> <string name="battery_saver_charged_notification_title" product="device" msgid="5954873381559605660">"ডিভাইসে <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> চার্জ আছে"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index a7248273307c..93705a298b68 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -283,7 +283,7 @@ <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Dozvoliti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristup vašim kontaktima?"</string> <string name="permgrouplab_location" msgid="7275582855722310164">"Lokacija"</string> <string name="permgroupdesc_location" msgid="1346617465127855033">"pristupa lokaciji ovog uređaja"</string> - <string name="permgrouprequest_location" msgid="3788275734953323491">"Dozvoliti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristup lokaciji ovog uređaja?"</string> + <string name="permgrouprequest_location" msgid="3788275734953323491">"Dozvoliti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> da pristupi lokaciji ovog uređaja?"</string> <string name="permgrouprequestdetail_location" msgid="1347189607421252902">"Aplikacija će imati pristup lokaciji isključivo dok je koristite"</string> <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"Dozvoliti aplikaciji <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> pristup lokaciji uređaja <b>sve vrijeme</b>?"</string> <string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"Aplikacija trenutno može pristupati lokaciji isključivo dok je koristite"</string> @@ -1462,7 +1462,7 @@ <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Želite li dozvoliti taj zahtjev?"</string> <string name="grant_permissions_header_text" msgid="6874497408201826708">"Zahtjev za pristup"</string> <string name="allow" msgid="7225948811296386551">"Dozvoli"</string> - <string name="deny" msgid="2081879885755434506">"Odbijte"</string> + <string name="deny" msgid="2081879885755434506">"Odbij"</string> <string name="permission_request_notification_title" msgid="6486759795926237907">"Upućen zahtjev za odobrenje"</string> <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"Upućen zahtjev za dozvolu\nza račun <xliff:g id="ACCOUNT">%s</xliff:g>."</string> <string name="forward_intent_to_owner" msgid="1207197447013960896">"Aplikaciju koristite van poslovnog profila"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index b76d4089be36..b3e1ac68cd6c 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -94,7 +94,7 @@ <string name="notification_channel_sms" msgid="3441746047346135073">"Missatges SMS"</string> <string name="notification_channel_voice_mail" msgid="3954099424160511919">"Missatges de veu"</string> <string name="notification_channel_wfc" msgid="2130802501654254801">"Trucades per Wi-Fi"</string> - <string name="notification_channel_sim" msgid="4052095493875188564">"Estat de la targeta SIM"</string> + <string name="notification_channel_sim" msgid="4052095493875188564">"Estat de la SIM"</string> <string name="peerTtyModeFull" msgid="6165351790010341421">"L\'altre dispositiu ha sol·licitat el mode TTY COMPLET."</string> <string name="peerTtyModeHco" msgid="5728602160669216784">"L\'altre dispositiu ha sol·licitat el mode TTY HCO."</string> <string name="peerTtyModeVco" msgid="1742404978686538049">"L\'altre dispositiu ha sol·licitat el mode TTY VCO."</string> @@ -300,8 +300,8 @@ <string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"accedir a la teva activitat física"</string> <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi a la teva activitat física?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Càmera"</string> - <string name="permgroupdesc_camera" msgid="3250611594678347720">"fer fotos i vídeos"</string> - <string name="permgrouprequest_camera" msgid="1299833592069671756">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> faci fotos i vídeos?"</string> + <string name="permgroupdesc_camera" msgid="3250611594678347720">"fer fotos i gravar vídeos"</string> + <string name="permgrouprequest_camera" msgid="1299833592069671756">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> faci fotos i gravi vídeos?"</string> <string name="permgrouplab_calllog" msgid="8798646184930388160">"Registres de trucades"</string> <string name="permgroupdesc_calllog" msgid="3006237336748283775">"llegir i editar el registre de trucades del telèfon"</string> <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Vols permetre que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> accedeixi als registres de trucades del telèfon?"</string> @@ -655,7 +655,7 @@ <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Fa un seguiment del nombre de contrasenyes incorrectes que s\'han escrit en intentar desbloquejar la pantalla i bloqueja el televisor o n\'esborra totes les dades de l\'usuari si s\'escriuen massa contrasenyes incorrectes."</string> <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Fa un seguiment del nombre de contrasenyes incorrectes que s\'han escrit en intentar desbloquejar la pantalla i bloqueja el telèfon o n\'esborra totes les dades de l\'usuari si s\'escriuen massa contrasenyes incorrectes."</string> <string name="policylab_resetPassword" msgid="4934707632423915395">"Canviar el bloqueig de pantalla"</string> - <string name="policydesc_resetPassword" msgid="1278323891710619128">"Canvia el bloqueig de pantalla"</string> + <string name="policydesc_resetPassword" msgid="1278323891710619128">"Canvia el bloqueig de pantalla."</string> <string name="policylab_forceLock" msgid="2274085384704248431">"Bloquejar la pantalla"</string> <string name="policydesc_forceLock" msgid="1141797588403827138">"Controla com i quan es bloqueja la pantalla."</string> <string name="policylab_wipeData" msgid="3910545446758639713">"Esborrar totes les dades"</string> @@ -1796,8 +1796,8 @@ <string name="package_updated_device_owner" msgid="1847154566357862089">"Actualitzat per l\'administrador"</string> <string name="package_deleted_device_owner" msgid="2307122077550236438">"Suprimit per l\'administrador"</string> <string name="confirm_battery_saver" msgid="639106420541753635">"D\'acord"</string> - <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"Per tal d\'allargar la durada de la bateria, l\'estalvi de bateria desactiva o restringeix les activitats en segon pla, alguns efectes visuals i altres funcions que consumeixen molta energia. "<annotation id="url">"Més informació"</annotation></string> - <string name="battery_saver_description" msgid="6413346684861241431">"Per tal d\'allargar la durada de la bateria, l\'estalvi de bateria desactiva o restringeix les activitats en segon pla, alguns efectes visuals i altres funcions que consumeixen molta energia."</string> + <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"Per tal de prolongar la durada de la bateria, el mode Estalvi de bateria desactiva o restringeix les activitats en segon pla, alguns efectes visuals i altres funcions que consumeixen molta energia. "<annotation id="url">"Més informació"</annotation></string> + <string name="battery_saver_description" msgid="6413346684861241431">"Per tal de prolongar la durada de la bateria, el mode Estalvi de bateria desactiva o restringeix les activitats en segon pla, alguns efectes visuals i altres funcions que consumeixen molta energia."</string> <string name="data_saver_description" msgid="6015391409098303235">"Per reduir l\'ús de dades, la funció Economitzador de dades evita que determinades aplicacions enviïn o rebin dades en segon pla. L\'aplicació que estiguis fent servir podrà accedir a dades, però potser ho farà menys sovint. Això vol dir, per exemple, que les imatges no es mostraran fins que no les toquis."</string> <string name="data_saver_enable_title" msgid="4674073932722787417">"Activar Economitzador de dades?"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"Activa"</string> @@ -1991,7 +1991,7 @@ <string name="notification_appops_overlay_active" msgid="633813008357934729">"es mostra sobre altres aplicacions a la pantalla"</string> <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notificació d\'informació del mode de rutina"</string> <string name="dynamic_mode_notification_title" msgid="508815255807182035">"És possible que la bateria s\'esgoti abans de la càrrega habitual"</string> - <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"S\'ha activat l\'estalvi de bateria per allargar-ne la durada"</string> + <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"S\'ha activat l\'estalvi de bateria per prolongar-ne la durada"</string> <string name="battery_saver_notification_channel_name" msgid="2083316159716201806">"Estalvi de bateria"</string> <string name="battery_saver_sticky_disabled_notification_title" msgid="6376147579378764641">"L\'estalvi de bateria no es tornarà a activar fins que no tornis a tenir poca bateria"</string> <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"La bateria ja està suficientment carregada. L\'estalvi de bateria no es tornarà a activar fins que no tornis a tenir poca bateria."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 6a17d708c9f3..6322a4ab5453 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -577,7 +577,7 @@ </string-array> <string name="face_error_hw_not_available" msgid="396883585636963908">"Gesicht nicht erkannt. Hardware nicht verfügbar."</string> <string name="face_error_timeout" msgid="2605673935810019129">"Erkennungszeit überschritten. Noch mal versuchen."</string> - <string name="face_error_no_space" msgid="2712120617457553825">"Kein Speicherplatz frei. Bitte alte Gesichtsdaten löschen."</string> + <string name="face_error_no_space" msgid="2712120617457553825">"Kein Speicherplatz frei. Bitte erst ein Gesicht löschen."</string> <string name="face_error_canceled" msgid="2768146728600802422">"Gesichtserkennung abgebrochen"</string> <string name="face_error_user_canceled" msgid="9003022830076496163">"Gesichtserkennung vom Nutzer abgebrochen"</string> <string name="face_error_lockout" msgid="3407426963155388504">"Zu viele Versuche, bitte später noch einmal versuchen"</string> @@ -1206,8 +1206,8 @@ <string name="dump_heap_notification_detail" msgid="3993078784053054141">"Heap-Dump wurde erfasst. Tippe, um ihn zu teilen."</string> <string name="dump_heap_title" msgid="5864292264307651673">"Heap-Dump teilen?"</string> <string name="dump_heap_text" msgid="8546022920319781701">"Für den Prozess \"<xliff:g id="PROC">%1$s</xliff:g>\" wurde das Speicherlimit von <xliff:g id="SIZE">%2$s</xliff:g> überschritten. Es steht ein Heap-Dump zur Verfügung, den du mit dem Entwickler teilen kannst. Beachte jedoch unbedingt, dass der Heap-Dump personenbezogene Daten von dir enthalten kann, auf die die App zugreifen kann."</string> - <string name="dump_heap_system_text" msgid="3236094872980706024">"Für den Prozess \"<xliff:g id="PROC">%1$s</xliff:g>\" wurde das Prozessspeicherlimit von <xliff:g id="SIZE">%2$s</xliff:g> überschritten. Es steht ein Heap-Dump zur Verfügung, den du teilen kannst. Beachte jedoch unbedingt, dass der Heap-Dump vertrauliche personenbezogene Daten von dir enthalten kann, auf die der Prozess Zugriff hat, einschließlich der von dir eingegebenen Informationen."</string> - <string name="dump_heap_ready_text" msgid="1778041771455343067">"Es steht ein Heap-Dump des Prozesses \"<xliff:g id="PROC">%1$s</xliff:g>\" zur Verfügung, den du teilen kannst. Beachte jedoch unbedingt, dass der Heap-Dump vertrauliche personenbezogene Daten von dir enthalten kann, auf die der Prozess Zugriff hat, einschließlich der von dir eingegebenen Informationen."</string> + <string name="dump_heap_system_text" msgid="3236094872980706024">"Für den Prozess \"<xliff:g id="PROC">%1$s</xliff:g>\" wurde das Prozessspeicherlimit von <xliff:g id="SIZE">%2$s</xliff:g> überschritten. Es gibt einen Heap-Dump, den du teilen kannst. Beachte jedoch unbedingt, dass der Heap-Dump vertrauliche personenbezogene Daten von dir enthalten kann, auf die der Prozess Zugriff hat, einschließlich der von dir eingegebenen Informationen."</string> + <string name="dump_heap_ready_text" msgid="1778041771455343067">"Es gibt einen Heap-Dump des Prozesses \"<xliff:g id="PROC">%1$s</xliff:g>\", den du teilen kannst. Beachte jedoch unbedingt, dass der Heap-Dump vertrauliche personenbezogene Daten von dir enthalten kann, auf die der Prozess Zugriff hat, einschließlich der von dir eingegebenen Informationen."</string> <string name="sendText" msgid="5209874571959469142">"Aktion für Text auswählen"</string> <string name="volume_ringtone" msgid="6885421406845734650">"Klingeltonlautstärke"</string> <string name="volume_music" msgid="5421651157138628171">"Medienlautstärke"</string> @@ -1844,7 +1844,7 @@ <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Abends unter der Woche"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Wochenende"</string> <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Termin"</string> - <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Beim Schlafen"</string> + <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Schlafen"</string> <string name="muted_by" msgid="5942954724562097128">"Einige Töne werden von <xliff:g id="THIRD_PARTY">%1$s</xliff:g> stummgeschaltet"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"Es liegt ein internes Problem mit deinem Gerät vor. Möglicherweise verhält es sich instabil, bis du es auf die Werkseinstellungen zurücksetzt."</string> <string name="system_error_manufacturer" msgid="8086872414744210668">"Es liegt ein internes Problem mit deinem Gerät vor. Bitte wende dich diesbezüglich an den Hersteller."</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index c6557c154bd6..7964e646a5d3 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -1797,7 +1797,7 @@ <string name="package_deleted_device_owner" msgid="2307122077550236438">"Tu administrador borró este paquete"</string> <string name="confirm_battery_saver" msgid="639106420541753635">"Aceptar"</string> <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"El Ahorro de batería desactiva o restringe la actividad en segundo plano, algunos efectos visuales y otras funciones que consumen mucha energía a fin de extender la duración de batería. "<annotation id="url">"Más información"</annotation></string> - <string name="battery_saver_description" msgid="6413346684861241431">"El Ahorro de batería desactiva o restringe la actividad en segundo plano, algunos efectos visuales y otras funciones que consumen mucha energía a fin de extender la duración de batería."</string> + <string name="battery_saver_description" msgid="6413346684861241431">"El Ahorro de batería desactiva o restringe la actividad en segundo plano, algunos efectos visuales y otras funciones que consumen mucha energía para extender la duración de la batería."</string> <string name="data_saver_description" msgid="6015391409098303235">"Para reducir el uso de datos, Ahorro de datos evita que algunas apps envíen y reciban datos en segundo plano. La app que estés usando podrá acceder a los datos, pero con menor frecuencia. De esta forma, por ejemplo, las imágenes no se mostrarán hasta que las presiones."</string> <string name="data_saver_enable_title" msgid="4674073932722787417">"¿Activar Ahorro de datos?"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"Activar"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index d15ba97cfd71..5e79620a2cf8 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -133,7 +133,7 @@ <string name="wfcSpnFormat_spn_wifi_calling" msgid="136001023263502280">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi bidezko deiak"</string> <string name="wfcSpnFormat_wlan_call" msgid="2533371081782489793">"WLAN bidezko deia"</string> <string name="wfcSpnFormat_spn_wlan_call" msgid="2315240198303197168">"<xliff:g id="SPN">%s</xliff:g> WLAN bidezko deia"</string> - <string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> Wi-Fi"</string> + <string name="wfcSpnFormat_spn_wifi" msgid="6546481665561961938">"<xliff:g id="SPN">%s</xliff:g> wifia"</string> <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="1726178784338466265">"Wi-Fi bidezko deiak | <xliff:g id="SPN">%s</xliff:g>"</string> <string name="wfcSpnFormat_spn_vowifi" msgid="4444638298656953681">"<xliff:g id="SPN">%s</xliff:g> VoWifi"</string> <string name="wfcSpnFormat_wifi_calling" msgid="4990486735013125329">"Wi-Fi bidezko deiak"</string> @@ -277,22 +277,22 @@ <string name="managed_profile_label" msgid="8947929265267690522">"Aldatu laneko profilera"</string> <string name="permgrouplab_contacts" msgid="3657758145679177612">"Kontaktuak"</string> <string name="permgroupdesc_contacts" msgid="6951499528303668046">"atzitu kontaktuak"</string> - <string name="permgrouprequest_contacts" msgid="6032805601881764300">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari kontaktuak atzitzea baimendu nahi diozu?"</string> + <string name="permgrouprequest_contacts" msgid="6032805601881764300">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari kontaktuak atzitzeko baimena eman nahi diozu?"</string> <string name="permgrouplab_location" msgid="7275582855722310164">"Kokapena"</string> <string name="permgroupdesc_location" msgid="1346617465127855033">"atzitu gailuaren kokapena"</string> - <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari gailuaren kokapena atzitzea baimendu nahi diozu?"</string> + <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari gailuaren kokapena atzitzeko baimena eman nahi diozu?"</string> <string name="permgrouprequestdetail_location" msgid="1347189607421252902">"Hura erabiltzen ari zarenean soilik atzituko du aplikazioak kokapena"</string> - <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari gailu honen kokapena <b>beti</b> atzitzeko baimena eman nahi diozu?"</string> + <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari gailuaren kokapena <b>beti</b> atzitzeko baimena eman nahi diozu?"</string> <string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"Aplikazioak hura darabilzunean atzi dezake kokapena"</string> <string name="permgrouplab_calendar" msgid="5863508437783683902">"Egutegia"</string> <string name="permgroupdesc_calendar" msgid="3889615280211184106">"atzitu egutegia"</string> - <string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari egutegia atzitzea baimendu nahi diozu?"</string> + <string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari egutegia atzitzeko baimena eman nahi diozu?"</string> <string name="permgrouplab_sms" msgid="228308803364967808">"SMS mezuak"</string> <string name="permgroupdesc_sms" msgid="4656988620100940350">"bidali eta ikusi SMS mezuak"</string> <string name="permgrouprequest_sms" msgid="7168124215838204719">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari SMS mezuak bidaltzea eta ikustea baimendu nahi diozu?"</string> <string name="permgrouplab_storage" msgid="1971118770546336966">"Memoria"</string> <string name="permgroupdesc_storage" msgid="637758554581589203">"atzitu gailuko argazkiak, multimedia-edukia eta fitxategiak"</string> - <string name="permgrouprequest_storage" msgid="7885942926944299560">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari gailuko argazkiak, multimedia-edukia eta fitxategiak atzitzea baimendu nahi diozu?"</string> + <string name="permgrouprequest_storage" msgid="7885942926944299560">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari gailuko argazkiak, multimedia-edukia eta fitxategiak atzitzeko baimena eman nahi diozu?"</string> <string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofonoa"</string> <string name="permgroupdesc_microphone" msgid="4988812113943554584">"grabatu audioa"</string> <string name="permgrouprequest_microphone" msgid="9167492350681916038">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari audioa grabatzea baimendu nahi diozu?"</string> @@ -304,13 +304,13 @@ <string name="permgrouprequest_camera" msgid="1299833592069671756">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari argazkiak ateratzea eta bideoak grabatzea baimendu nahi diozu?"</string> <string name="permgrouplab_calllog" msgid="8798646184930388160">"Deien erregistroa"</string> <string name="permgroupdesc_calllog" msgid="3006237336748283775">"irakurri telefonoko deien erregistroa eta idatzi bertan"</string> - <string name="permgrouprequest_calllog" msgid="8487355309583773267">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari telefonoko deien erregistroa atzitzea baimendu nahi diozu?"</string> + <string name="permgrouprequest_calllog" msgid="8487355309583773267">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari telefonoko deien erregistroa atzitzeko baimena eman nahi diozu?"</string> <string name="permgrouplab_phone" msgid="5229115638567440675">"Telefonoa"</string> <string name="permgroupdesc_phone" msgid="6234224354060641055">"egin eta kudeatu telefono-deiak"</string> <string name="permgrouprequest_phone" msgid="9166979577750581037">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari telefono-deiak egitea eta kudeatzea baimendu nahi diozu?"</string> <string name="permgrouplab_sensors" msgid="4838614103153567532">"Gorputz-sentsoreak"</string> <string name="permgroupdesc_sensors" msgid="7147968539346634043">"atzitu bizi-konstanteei buruzko sentsorearen datuak"</string> - <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Bizi-konstanteei buruzko sentsorearen datuak atzitzea baimendu nahi diozu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari?"</string> + <string name="permgrouprequest_sensors" msgid="6349806962814556786">"Bizi-konstanteei buruzko sentsorearen datuak atzitzeko baimena eman nahi diozu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> aplikazioari?"</string> <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"Eskuratu leihoko edukia"</string> <string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"Arakatu irekita daukazun leihoko edukia."</string> <string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"Aktibatu \"Arakatu ukituta\""</string> @@ -485,7 +485,7 @@ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Wi-Fi sarearen bidez gailu guztiei bidalitako paketeak jasotzeko baimena ematen die aplikazioei multidifusio-helbideak erabilita, ez tableta soilik. Multidifusiokoa ez den moduak baino bateria gehiago erabiltzen du."</string> <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Wi-Fi sareko gailu guztiei bidalitako paketeak jasotzea baimentzen die aplikazioei multidifusio-helbideak erabilita, ez telebista soilik. Multidifusiokoa ez den moduak baino bateria gehiago erabiltzen du."</string> <string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Wi-Fi sarearen bidez gailu guztiei bidalitako paketeak jasotzeko baimena ematen die aplikazioei multidifusio-helbideak erabilita, ez telefonoa soilik. Multidifusiokoa ez den moduak baino bateria gehiago erabiltzen du."</string> - <string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"atzitu Bluetooth-ezarpenak"</string> + <string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"atzitu Bluetooth ezarpenak"</string> <string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Tokiko Bluetooth tableta konfiguratzea eta urruneko gailuak detektatzea eta haiekin parekatzea baimentzen die aplikazioei."</string> <string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Tokiko Bluetooth telebista konfiguratzea eta urruneko gailuak hautematea eta haiekin parekatzea baimentzen die aplikazioei."</string> <string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Tokiko Bluetooth telefonoa konfiguratzea eta urruneko gailuak detektatzea eta haiekin parekatzea baimentzen die aplikazioei."</string> @@ -1206,8 +1206,8 @@ <string name="dump_heap_notification_detail" msgid="3993078784053054141">"Sortu da uneko memoria-prozesuaren txostena. Sakatu partekatzeko."</string> <string name="dump_heap_title" msgid="5864292264307651673">"Uneko memoria-prozesuaren txostena partekatu nahi duzu?"</string> <string name="dump_heap_text" msgid="8546022920319781701">"<xliff:g id="PROC">%1$s</xliff:g> prozesuak memoria-muga (<xliff:g id="SIZE">%2$s</xliff:g>) gainditu du. Uneko memoria-prozesuaren txostena sortu da, garatzailearekin parteka dezazun. Kontuz: aplikazioak atzi dezakeen informazio pertsonala izan dezake txosten horrek."</string> - <string name="dump_heap_system_text" msgid="3236094872980706024">"<xliff:g id="PROC">%1$s</xliff:g> prozesuak bere memoria-muga (<xliff:g id="SIZE">%2$s</xliff:g>) gainditu du. Memoria-prozesuaren txosten bat duzu erabilgarri, hura partekatu nahi baduzu ere. Kontuz: baliteke txosten horrek prozesuak atzi dezakeen kontuzko informazio pertsonala izatea, eta datu horien barnean zuk idatzitakoak egon daitezke, besteak beste."</string> - <string name="dump_heap_ready_text" msgid="1778041771455343067">"<xliff:g id="PROC">%1$s</xliff:g> prozesuaren memoria-prozesuaren txosten bat duzu erabilgarri, hura partekatu nahi baduzu ere. Kontuz: baliteke txosten horrek prozesuak atzi dezakeen kontuzko informazio pertsonala izatea, eta datu horien barnean zuk idatzitakoak egon daitezke, besteak beste."</string> + <string name="dump_heap_system_text" msgid="3236094872980706024">"<xliff:g id="PROC">%1$s</xliff:g> prozesuak bere memoria-muga (<xliff:g id="SIZE">%2$s</xliff:g>) gainditu du. Memoria-iraulketaren txosten bat duzu erabilgarri, hura partekatu nahi baduzu ere. Kontuz: baliteke txosten horrek prozesuak atzi dezakeen kontuzko informazio pertsonala izatea eta datu horien barnean zuk idatzitakoak egotea, besteak beste."</string> + <string name="dump_heap_ready_text" msgid="1778041771455343067">"<xliff:g id="PROC">%1$s</xliff:g> prozesuaren memoria-iraulketaren txosten bat duzu erabilgarri, hura partekatu nahi baduzu ere. Kontuz: baliteke txosten horrek prozesuak atzi dezakeen kontuzko informazio pertsonala izatea eta datu horien barnean zuk idatzitakoak egotea, besteak beste."</string> <string name="sendText" msgid="5209874571959469142">"Aukeratu testurako ekintza"</string> <string name="volume_ringtone" msgid="6885421406845734650">"Tonu-jotzailearen bolumena"</string> <string name="volume_music" msgid="5421651157138628171">"Multimedia-edukiaren bolumena"</string> @@ -1272,7 +1272,7 @@ <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> erabiltzen ari zinen, baina <xliff:g id="NEW_NETWORK">%2$s</xliff:g> erabiltzen ari zara orain"</string> <string-array name="network_switch_type_name"> <item msgid="3979506840912951943">"datu-konexioa"</item> - <item msgid="75483255295529161">"Wi-Fi"</item> + <item msgid="75483255295529161">"Wifia"</item> <item msgid="6862614801537202646">"Bluetooth-a"</item> <item msgid="5447331121797802871">"Ethernet"</item> <item msgid="8257233890381651999">"VPN"</item> @@ -1845,7 +1845,7 @@ <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Lanegunetako gaua"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Asteburua"</string> <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Gertaera"</string> - <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Lo egitean"</string> + <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Lo egiteko"</string> <string name="muted_by" msgid="5942954724562097128">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> soinu batzuk isilarazten ari da"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"Barneko arazo bat dago zure gailuan eta agian ezegonkor egongo da jatorrizko datuak berrezartzen dituzun arte."</string> <string name="system_error_manufacturer" msgid="8086872414744210668">"Barneko arazo bat dago zure gailuan. Xehetasunak jakiteko, jarri fabrikatzailearekin harremanetan."</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 11cf22a8dc16..50c71851ad74 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -301,7 +301,7 @@ <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à accéder à vos activités physiques?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Appareil photo"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"prendre des photos et filmer des vidéos"</string> - <string name="permgrouprequest_camera" msgid="1299833592069671756">"Autoriser « <xliff:g id="APP_NAME">%1$s</xliff:g> » à prendre des photos et à filmer des vidéos?"</string> + <string name="permgrouprequest_camera" msgid="1299833592069671756">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à prendre des photos et à filmer des vidéos?"</string> <string name="permgrouplab_calllog" msgid="8798646184930388160">"Journaux d\'appels"</string> <string name="permgroupdesc_calllog" msgid="3006237336748283775">"lire et écrire le journal des appels téléphoniques"</string> <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder à vos journaux d\'appels?"</string> @@ -1844,7 +1844,7 @@ <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Soirs de semaine"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fin de semaine"</string> <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Événement"</string> - <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Dormir"</string> + <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Sommeil"</string> <string name="muted_by" msgid="5942954724562097128">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> désactive certains sons"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"Un problème interne est survenu avec votre appareil. Il se peut qu\'il soit instable jusqu\'à ce que vous le réinitialisiez à sa configuration d\'usine."</string> <string name="system_error_manufacturer" msgid="8086872414744210668">"Un problème interne est survenu avec votre appareil. Communiquez avec le fabricant pour obtenir plus de détails."</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 12660fb6b7c5..23e424e93d56 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -280,7 +280,7 @@ <string name="permgrouprequest_contacts" msgid="6032805601881764300">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'accéder à vos contacts ?"</string> <string name="permgrouplab_location" msgid="7275582855722310164">"Localisation"</string> <string name="permgroupdesc_location" msgid="1346617465127855033">"accéder à la position de l\'appareil"</string> - <string name="permgrouprequest_location" msgid="3788275734953323491">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'accéder à la position de cet appareil ?"</string> + <string name="permgrouprequest_location" msgid="3788275734953323491">"Permettre à l\'application <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'accéder à la position de cet appareil ?"</string> <string name="permgrouprequestdetail_location" msgid="1347189607421252902">"L\'application n\'a accès à la position de l\'appareil que lorsqu\'elle est ouverte"</string> <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à accéder <b>en permanence</b> à la position de cet appareil ?"</string> <string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"L\'application peut actuellement accéder à la position uniquement pendant que vous l\'utilisez"</string> @@ -301,7 +301,7 @@ <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"Autoriser <xliff:g id="APP_NAME">%1$s</xliff:g> à accéder à votre activité physique ?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Appareil photo"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"prendre des photos et enregistrer des vidéos"</string> - <string name="permgrouprequest_camera" msgid="1299833592069671756">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> de prendre des photos et de filmer des vidéos ?"</string> + <string name="permgrouprequest_camera" msgid="1299833592069671756">"Autoriser <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> à prendre des photos et enregistrer des vidéos ?"</string> <string name="permgrouplab_calllog" msgid="8798646184930388160">"Journaux d\'appels"</string> <string name="permgroupdesc_calllog" msgid="3006237336748283775">"Lire et écrire les journaux d\'appels du téléphone"</string> <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Permettre à <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> d\'accéder aux journaux d\'appels de votre téléphone ?"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index d7a241ace193..393fcbe0fd62 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -1202,12 +1202,12 @@ <string name="new_app_action" msgid="6694851182870774403">"Abrir a aplicación <xliff:g id="NEW_APP">%1$s</xliff:g>"</string> <string name="new_app_description" msgid="5894852887817332322">"A aplicación <xliff:g id="OLD_APP">%1$s</xliff:g> pecharase sen gardar o contido"</string> <string name="dump_heap_notification" msgid="2618183274836056542">"<xliff:g id="PROC">%1$s</xliff:g> superou o límite de memoria"</string> - <string name="dump_heap_ready_notification" msgid="1162196579925048701">"O baleirado de montóns de <xliff:g id="PROC">%1$s</xliff:g> está listo"</string> - <string name="dump_heap_notification_detail" msgid="3993078784053054141">"Recompilouse un baleirado de montóns. Toca para compartilo."</string> - <string name="dump_heap_title" msgid="5864292264307651673">"Queres compartir o baleirado de montóns?"</string> - <string name="dump_heap_text" msgid="8546022920319781701">"O proceso <xliff:g id="PROC">%1$s</xliff:g> superou o seu límite de memoria de <xliff:g id="SIZE">%2$s</xliff:g>. Tes dispoñible un baleirado de montóns para compartir co seu programador. Ten coidado, xa que pode conter información persoal á que pode acceder a aplicación."</string> - <string name="dump_heap_system_text" msgid="3236094872980706024">"O proceso <xliff:g id="PROC">%1$s</xliff:g> superou o seu límite de memoria, <xliff:g id="SIZE">%2$s</xliff:g>. Tes dispoñible un baleirado de montóns para compartilo. Ten coidado, xa que quizais conteña información persoal confidencial á que se pode acceder durante o proceso e que pode incluír os datos que escribiches."</string> - <string name="dump_heap_ready_text" msgid="1778041771455343067">"Tes dispoñible un baleirado de montóns do proceso <xliff:g id="PROC">%1$s</xliff:g> para compartilo. Ten coidado, xa que é posible que conteña información persoal confidencial á que se pode acceder durante o proceso e que pode incluír os datos que escribiches."</string> + <string name="dump_heap_ready_notification" msgid="1162196579925048701">"O baleirado da zona de memoria dinámica de <xliff:g id="PROC">%1$s</xliff:g> está listo"</string> + <string name="dump_heap_notification_detail" msgid="3993078784053054141">"Recompilouse un baleirado da zona de memoria dinámica. Toca para compartilo."</string> + <string name="dump_heap_title" msgid="5864292264307651673">"Queres compartir o baleirado da zona de memoria dinámica?"</string> + <string name="dump_heap_text" msgid="8546022920319781701">"O proceso <xliff:g id="PROC">%1$s</xliff:g> superou o seu límite de memoria de <xliff:g id="SIZE">%2$s</xliff:g>. Tes dispoñible un baleirado da zona de memoria dinámica para compartir co seu programador. Ten coidado, xa que pode conter información persoal á que pode acceder a aplicación."</string> + <string name="dump_heap_system_text" msgid="3236094872980706024">"O proceso <xliff:g id="PROC">%1$s</xliff:g> superou o seu límite de memoria, <xliff:g id="SIZE">%2$s</xliff:g>. Tes dispoñible un baleirado da zona de memoria dinámica para compartilo. Ten coidado, xa que quizais conteña información persoal confidencial á que se pode acceder durante o proceso e que pode incluír os datos que escribiches."</string> + <string name="dump_heap_ready_text" msgid="1778041771455343067">"Tes dispoñible un baleirado da zona de memoria dinámica do proceso <xliff:g id="PROC">%1$s</xliff:g> para compartilo. Ten coidado, xa que é posible que conteña información persoal confidencial á que se pode acceder durante o proceso e que pode incluír os datos que escribiches."</string> <string name="sendText" msgid="5209874571959469142">"Seleccionar unha acción para o texto"</string> <string name="volume_ringtone" msgid="6885421406845734650">"Volume do timbre"</string> <string name="volume_music" msgid="5421651157138628171">"Volume dos elementos multimedia"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index d2b6fc9816c5..80206a3d95e0 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -519,8 +519,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"એપને તમારો ફોટો સંગ્રહ સંશોધિત કરવાની મંજૂરી આપે છે."</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"આપના મીડિયા સંગ્રહમાંથી સ્થાનો વાંચવા"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"એપને તમારા મીડિયા સંગ્રહમાંથી સ્થાનો વાંચવાની મંજૂરી આપે છે."</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"આ તમે જ છો તેનો પુરાવો આપો"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"બાયોમેટ્રિક હાર્ડવેર ઉપલબ્ધ નથી"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"પ્રમાણીકરણ રદ કર્યું"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"ઓળખાયેલ નથી"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 5933297a4b18..0e5123beb9bf 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -160,15 +160,15 @@ <string name="httpErrorAuth" msgid="1435065629438044534">"प्रमाणीकृत नहीं किया जा सका."</string> <string name="httpErrorProxyAuth" msgid="1788207010559081331">"प्रॉक्सी सर्वर द्वारा प्रमाणीकरण असफल था."</string> <string name="httpErrorConnect" msgid="8714273236364640549">"सर्वर से कनेक्ट नहीं किया जा सका."</string> - <string name="httpErrorIO" msgid="2340558197489302188">"सर्वर से संचार नहीं किया जा सका. बाद में पुन: प्रयास करें."</string> - <string name="httpErrorTimeout" msgid="4743403703762883954">"सर्वर से कनेक्शन का समय समाप्त हुआ."</string> + <string name="httpErrorIO" msgid="2340558197489302188">"सर्वर से संचार नहीं किया जा सका. बाद में फिर से प्रयास करें."</string> + <string name="httpErrorTimeout" msgid="4743403703762883954">"सर्वर से कनेक्शन का समय खत्म हुआ."</string> <string name="httpErrorRedirectLoop" msgid="8679596090392779516">"पेज में कई ऐसे कई वेबलिंक हैं जो दूसरे सर्वर पर ले जाते हैं."</string> <string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"प्रोटोकॉल समर्थित नहीं है."</string> <string name="httpErrorFailedSslHandshake" msgid="96549606000658641">"सुरक्षित कनेक्शन स्थापित नहीं किया जा सका."</string> <string name="httpErrorBadUrl" msgid="3636929722728881972">"यूआरएल गलत होने की वजह से पेज नहीं खोला जा सका."</string> <string name="httpErrorFile" msgid="2170788515052558676">"फ़ाइल पर नहीं पहुंचा जा सका."</string> <string name="httpErrorFileNotFound" msgid="6203856612042655084">"अनुरोधित फ़ाइल नहीं मिल सकी."</string> - <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"बहुत सारे अनुरोधों का संसाधन हो रहा है. बाद में पुन: प्रयास करें."</string> + <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"बहुत सारे अनुरोधों का संसाधन हो रहा है. बाद में फिर से प्रयास करें."</string> <string name="notification_title" msgid="8967710025036163822">"<xliff:g id="ACCOUNT">%1$s</xliff:g> के लिए प्रवेश गड़बड़ी"</string> <string name="contentServiceSync" msgid="8353523060269335667">"समन्वयन"</string> <string name="contentServiceSyncNotificationTitle" msgid="7036196943673524858">"सिंक नहीं किया जा सकता"</string> @@ -234,7 +234,7 @@ <skip /> <string name="bugreport_message" msgid="398447048750350456">"इससे ईमेल भेजने के लिए, आपके डिवाइस की मौजूदा स्थिति से जुड़ी जानकारी इकट्ठा की जाएगी. गड़बड़ी की रिपोर्ट बनना शुरू होने से लेकर भेजने के लिए तैयार होने तक कुछ समय लगेगा; कृपया इंतज़ार करें."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"सहभागी रिपोर्ट"</string> - <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"अधिकांश परिस्थितियों में इसका उपयोग करें. यह आपको रिपोर्ट की प्रगति ट्रैक करने देता है, समस्या के बारे में अधिक विवरण डालने देता है और स्क्रीनशॉट लेने देता है. यह आपको ऐसे कम उपयोग किए गए अनुभाग मिटाने दे सकता है जिनकी रिपोर्ट करने में अधिक समय लगता है."</string> + <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"ज़्यादातर परिस्थितियों में इसका उपयोग करें. यह आपको रिपोर्ट की प्रगति ट्रैक करने देता है, समस्या के बारे में अधिक विवरण डालने देता है और स्क्रीनशॉट लेने देता है. यह आपको ऐसे कम उपयोग किए गए अनुभाग मिटाने दे सकता है जिनकी रिपोर्ट करने में अधिक समय लगता है."</string> <string name="bugreport_option_full_title" msgid="6354382025840076439">"पूर्ण रिपोर्ट"</string> <string name="bugreport_option_full_summary" msgid="7210859858969115745">"जब आपका डिवाइस ठीक से काम नहीं कर रहा हो या बहुत धीमा हो या जब आपको रिपोर्ट के सभी भागों की ज़रूरत हो, तो सिस्टम से कम से कम रोक-टोक के लिए इस विकल्प का इस्तेमाल करें. यह आपको ज़्यादा जानकारी डालने या अतिरिक्त स्क्रीनशॉट लेने नहीं देता."</string> <plurals name="bugreport_countdown" formatted="false" msgid="6878900193900090368"> @@ -283,7 +283,7 @@ <string name="permgrouprequest_contacts" msgid="6032805601881764300">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को अपने संपर्क देखने की अनुमति देना चाहते हैं?"</string> <string name="permgrouplab_location" msgid="7275582855722310164">"जगह"</string> <string name="permgroupdesc_location" msgid="1346617465127855033">"इस डिवाइस की जगह तक पहुंचने दें"</string> - <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को इस डिवाइस की \'जगह की जानकारी\' ऐक्सेस करने की अनुमति देना चाहते हैं?"</string> + <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को इस डिवाइस की \'जगह की जानकारी\' एक्सेस करने की अनुमति देना चाहते हैं?"</string> <!-- no translation found for permgrouprequestdetail_location (1347189607421252902) --> <skip /> <!-- no translation found for permgroupbackgroundrequest_location (5039063878675613235) --> @@ -366,12 +366,12 @@ <string name="permdesc_getTasks" msgid="7454215995847658102">"ऐप को माजूदा समय में और हाल ही में चल रही कार्रवाइयों के बारे में जानकारी निकालने देता है. इससे ऐप डिवाइस पर इस्तेमाल किए गए ऐप के बारे में जानकारी खोज सकता है."</string> <string name="permlab_manageProfileAndDeviceOwners" msgid="7918181259098220004">"प्रोफ़ाइल और डिवाइस स्वामियों को प्रबंधित करें"</string> <string name="permdesc_manageProfileAndDeviceOwners" msgid="106894851498657169">"ऐप्स को प्रोफ़ाइल स्वामी और डिवाइस स्वामी सेट करने दें."</string> - <string name="permlab_reorderTasks" msgid="2018575526934422779">"चल रहे ऐप्स पुन: क्रमित करें"</string> + <string name="permlab_reorderTasks" msgid="2018575526934422779">"चल रहे ऐप्स फिर से क्रमित करें"</string> <string name="permdesc_reorderTasks" msgid="7734217754877439351">"ऐप्स को कार्यों को अग्रभूमि और पृष्ठभूमि पर ले जाने देता है. ऐप्स आपके इनपुट के बिना यह कर सकता है."</string> <string name="permlab_enableCarMode" msgid="5684504058192921098">"कार मोड चालू करें"</string> <string name="permdesc_enableCarMode" msgid="4853187425751419467">"ऐप्स को कार मोड सक्षम करने देता है."</string> <string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"अन्य ऐप्स बंद करें"</string> - <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"ऐप्स को अन्य ऐप्स की पृष्ठभूमि प्रक्रियाओं को समाप्त करने देता है. यह अन्य ऐप्स का चलना रोक सकता है."</string> + <string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"ऐप्स को अन्य ऐप्स की पृष्ठभूमि प्रक्रियाओं को खत्म करने देता है. यह अन्य ऐप्स का चलना रोक सकता है."</string> <string name="permlab_systemAlertWindow" msgid="7238805243128138690">"यह ऐप्लिकेशन दूसरे ऐप्लिकेशन के ऊपर दिखाई दे सकता है"</string> <string name="permdesc_systemAlertWindow" msgid="2393776099672266188">"यह ऐप्लिकेशन, दूसरे ऐप्लिकेशन के ऊपर या स्क्रीन के अन्य भागों पर दिखाई दे सकता है. इससे ऐप्लिकेशन के सामान्य उपयोग में बाधा आ सकती है और दूसरे ऐप्लिकेशन के दिखाई देने के तरीकों में बदलाव हो सकता है."</string> <string name="permlab_runInBackground" msgid="7365290743781858803">"बैकग्राउंड में चलता है"</string> @@ -389,13 +389,13 @@ <string name="permlab_writeSettings" msgid="2226195290955224730">"सिस्टम सेटिंग बदलें"</string> <string name="permdesc_writeSettings" msgid="7775723441558907181">"ऐप्स को सिस्टम सेटिंग डेटा संशोधित करने देता है. दुर्भावनापूर्ण ऐप्स आपके सिस्टम के कॉन्फ़िगरेशन को दूषित कर सकते हैं."</string> <string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"प्रारंभ होने पर चलाएं"</string> - <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"ऐप्स को सिस्टम द्वारा बूटिंग पूर्ण करते ही स्वतः आरंभ करने देता है. इससे टैबलेट को आरंभ होने में अधिक समय लग सकता है और ऐप्स को निरंतर चलाकर संपूर्ण टैबलेट को धीमा करने देता है."</string> + <string name="permdesc_receiveBootCompleted" product="tablet" msgid="7390304664116880704">"ऐप्स को सिस्टम द्वारा बूटिंग पूर्ण करते ही अपने आप आरंभ करने देता है. इससे टैबलेट को आरंभ होने में अधिक समय लग सकता है और ऐप्स को निरंतर चलाकर संपूर्ण टैबलेट को धीमा करने देता है."</string> <string name="permdesc_receiveBootCompleted" product="tv" msgid="4525890122209673621">"सिस्टम के चालू होते ही ऐप को अपने आप शुरू होने देती है. इससे टीवी को चालू होने में ज़्यादा समय लग सकता है और ऐप के लगातार चलते रहने से पूरा टैबलेट धीमा हो सकता है."</string> <string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"सिस्टम के चालू होते ही ऐप को अपने आप शुरू होने देती है. इससे फ़ोन को चालू होने में ज़्यादा समय लग सकता है और ऐप के लगातार चलते रहने से पूरा फ़ोन धीमा हो सकता है."</string> <string name="permlab_broadcastSticky" msgid="7919126372606881614">"स्टिकी प्रसारण भेजें"</string> - <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"ऐप्स को स्टिकी प्रसारण भेजने देता है, जो प्रसारण समाप्त होने के बाद भी बने रहते हैं. अत्यधिक उपयोग, टैबलेट की बहुत अधिक मेमोरी का उपयोग करके उसे धीमा या अस्थिर कर सकता है."</string> + <string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"ऐप्स को स्टिकी प्रसारण भेजने देता है, जो प्रसारण खत्म होने के बाद भी बने रहते हैं. अत्यधिक उपयोग, टैबलेट की बहुत अधिक मेमोरी का उपयोग करके उसे धीमा या अस्थिर कर सकता है."</string> <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"ऐप को स्िटकी प्रसारण भेजने देती है, जो प्रसारण बंद होने के बाद भी बने रहते हैं. अत्यधिक उपयोग से टीवी धीमा या अस्थिर हो सकता है जिससे वह बहुत सारी मेमोरी का उपयोग कर सकता है."</string> - <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"ऐप्स को स्टिकी प्रसारण भेजने देता है, जो प्रसारण समाप्त होने के बाद भी बने रहते हैं. अत्यधिक उपयोग, फ़ोन की बहुत अधिक मेमोरी का उपयोग करके उसे धीमा या अस्थिर कर सकता है."</string> + <string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"ऐप्स को स्टिकी प्रसारण भेजने देता है, जो प्रसारण खत्म होने के बाद भी बने रहते हैं. अत्यधिक उपयोग, फ़ोन की बहुत अधिक मेमोरी का उपयोग करके उसे धीमा या अस्थिर कर सकता है."</string> <string name="permlab_readContacts" msgid="8348481131899886131">"अपने संपर्क पढ़ें"</string> <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"ऐप को आपके टैबलेट पर मौजूद आपके संपर्कों का डेटा पढ़ने देती है, जिसमें ये भी शामिल है कि आपने कुछ ख़ास लोगों से कितनी बार कॉल, ईमेल, या कुछ और तरीकों से बातचीत की. यह अनुमति ऐप को आपका संपर्क डेटा सेव करने देती है और धोखा देने वाले ऐप संपर्क डेटा को आपकी जानकारी के बिना शेयर कर सकते हैं."</string> <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"ऐप को आपके टीवी पर मौजूद आपके संपर्कों का डेटा पढ़ने देती है, जिसमें ये भी शामिल है कि आपने कुछ ख़ास लोगों से कितनी बार कॉल, ईमेल, या कुछ और तरीकों से बातचीत की. यह अनुमति ऐप को आपका संपर्क डेटा सेव करने देती है और धोखा देने वाले ऐप संपर्क डेटा को आपकी जानकारी के बिना शेयर कर सकते हैं."</string> @@ -511,7 +511,7 @@ <string name="permlab_nfc" msgid="4423351274757876953">"नियर फ़ील्ड कम्यूनिकेशन नियंत्रित करें"</string> <string name="permdesc_nfc" msgid="7120611819401789907">"ऐप्स को नियर फ़ील्ड कम्यूनिकेशन (NFC) टैग, कार्ड, और रीडर के साथ संचार करने देता है."</string> <string name="permlab_disableKeyguard" msgid="3598496301486439258">"अपना स्क्रीन लॉक अक्षम करें"</string> - <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ऐप्स को कीलॉक और कोई भी संबद्ध पासवर्ड सुरक्षा अक्षम करने देता है. उदाहरण के लिए, इनकमिंग फ़ोन कॉल प्राप्त करते समय फ़ोन, कीलॉक को अक्षम कर देता है, फिर कॉल समाप्त होने पर कीलॉक को पुन: सक्षम कर देता है."</string> + <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"ऐप्स को कीलॉक और कोई भी संबद्ध पासवर्ड सुरक्षा अक्षम करने देता है. उदाहरण के लिए, इनकमिंग फ़ोन कॉल प्राप्त करते समय फ़ोन, कीलॉक को अक्षम कर देता है, फिर कॉल खत्म होने पर कीलॉक को फिर से सक्षम कर देता है."</string> <!-- no translation found for permlab_requestPasswordComplexity (202650535669249674) --> <skip /> <!-- no translation found for permdesc_requestPasswordComplexity (4730994229754212347) --> @@ -530,15 +530,14 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"इससे ऐप्लिकेशन को आपके फ़ोटो संग्रह में बदलाव करने की मंज़ूरी दी जाती है."</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"अपने मीडिया संग्रह से जगह की जानकारी एक्सेस करने की अनुमति दें"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"इससे ऐप्लिकेशन को आपके मीडिया संग्रह से जगह की जानकारी एक्सेस करने की अनुमति दी जाती है."</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"अपनी पहचान की पुष्टि करें"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"बायोमेट्रिक हार्डवेयर उपलब्ध नहीं है"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"प्रमाणीकरण रद्द किया गया"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"पहचान नहीं हो पाई"</string> <string name="biometric_error_canceled" msgid="349665227864885880">"प्रमाणीकरण रद्द किया गया"</string> <string name="biometric_error_device_not_secured" msgid="6583143098363528349">"पिन, पैटर्न या पासवर्ड सेट नहीं है"</string> <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फ़िंगरप्रिंट की पहचान की गई. कृपया पुनः प्रयास करें."</string> - <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फ़िंगरप्रिंट संसाधित नहीं हो सका. कृपया पुन: प्रयास करें."</string> + <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फ़िंगरप्रिंट संसाधित नहीं हो सका. कृपया फिर से प्रयास करें."</string> <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"फ़िंगरप्रिंट सेंसर गंदा है. कृपया साफ़ करें और फिर कोशिश करें."</string> <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"उंगली बहुत तेज़ी से चलाई गई है. कृपया फिर से कोशिश करें."</string> <string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"उंगली बहुत धीरे चलाई गई. कृपया फिर से कोशिश करें."</string> @@ -549,12 +548,12 @@ <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string> <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"फ़िंगरप्रिंट हार्डवेयर उपलब्ध नहीं है."</string> <string name="fingerprint_error_no_space" msgid="1055819001126053318">"फ़िंगरप्रिंट को संग्रहित नहीं किया जा सका. कृपया कोई मौजूदा फ़िंगरप्रिंट निकालें."</string> - <string name="fingerprint_error_timeout" msgid="3927186043737732875">"फ़िंगरप्रिंट का समय समाप्त हो गया. पुनः प्रयास करें."</string> + <string name="fingerprint_error_timeout" msgid="3927186043737732875">"फ़िंगरप्रिंट का समय खत्म हो गया. पुनः प्रयास करें."</string> <string name="fingerprint_error_canceled" msgid="4402024612660774395">"फ़िंगरप्रिंट क्रियान्वयन रोक दिया गया."</string> <string name="fingerprint_error_user_canceled" msgid="7999639584615291494">"उपयोगकर्ता ने फिंगरप्रिंट की पुष्टि की कार्रवाई रद्द कर दी है."</string> - <string name="fingerprint_error_lockout" msgid="5536934748136933450">"बहुत अधिक प्रयास कर लिए गए हैं. बाद में पुन: प्रयास करें."</string> + <string name="fingerprint_error_lockout" msgid="5536934748136933450">"बहुत अधिक प्रयास कर लिए गए हैं. बाद में फिर से प्रयास करें."</string> <string name="fingerprint_error_lockout_permanent" msgid="5033251797919508137">"बहुत अधिक कोशिशें. फ़िंगरप्रिंट सेंसर अक्षम है."</string> - <string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"पुन: प्रयास करें."</string> + <string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"फिर से प्रयास करें."</string> <string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"कोई फ़िंगरप्रिंट रजिस्टर नहीं किया गया है."</string> <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है."</string> <string name="fingerprint_name_template" msgid="5870957565512716938">"फ़िंगरप्रिंट <xliff:g id="FINGERID">%d</xliff:g>"</string> @@ -604,25 +603,18 @@ <skip /> <string-array name="face_acquired_vendor"> </string-array> - <!-- no translation found for face_error_hw_not_available (396883585636963908) --> - <skip /> + <string name="face_error_hw_not_available" msgid="396883585636963908">"चेहरा नहीं पहचान पा रहे. हार्डवेयर उपलब्ध नहीं है."</string> <!-- no translation found for face_error_timeout (2605673935810019129) --> <skip /> - <!-- no translation found for face_error_no_space (2712120617457553825) --> - <skip /> - <!-- no translation found for face_error_canceled (2768146728600802422) --> - <skip /> - <!-- no translation found for face_error_user_canceled (9003022830076496163) --> - <skip /> + <string name="face_error_no_space" msgid="2712120617457553825">"चेहरे का नया डेटा सेव नहीं हो सकता. कोई पुराना डेटा मिटाएं."</string> + <string name="face_error_canceled" msgid="2768146728600802422">"चेहरा पहचानने की कार्रवाई रद्द की गई"</string> + <string name="face_error_user_canceled" msgid="9003022830076496163">"उपयोगकर्ता ने \'चेहरे की पहचान\' रद्द कर दी."</string> <string name="face_error_lockout" msgid="3407426963155388504">"कई बार कोशिश की गई. बाद में कोशिश करें."</string> - <!-- no translation found for face_error_lockout_permanent (3485837851962070925) --> - <skip /> + <string name="face_error_lockout_permanent" msgid="3485837851962070925">"कई बार कोशिश की जा चुकी है. \'चेहरे की पहचान\' बंद कर दी गई."</string> <!-- no translation found for face_error_unable_to_process (4940944939691171539) --> <skip /> - <!-- no translation found for face_error_not_enrolled (2600952202843125796) --> - <skip /> - <!-- no translation found for face_error_hw_not_present (1317845121210260372) --> - <skip /> + <string name="face_error_not_enrolled" msgid="2600952202843125796">"आपने डिवाइस पर \'चेहरे की पहचान\' सेट नहीं की है."</string> + <string name="face_error_hw_not_present" msgid="1317845121210260372">"इस डिवाइस पर \'चेहरे की पहचान\' सुविधा काम नहीं करती."</string> <string name="face_name_template" msgid="7004562145809595384">"चेहरा <xliff:g id="FACEID">%d</xliff:g>"</string> <string-array name="face_error_vendor"> </string-array> @@ -687,7 +679,7 @@ <string name="policydesc_limitPassword" msgid="2502021457917874968">"स्क्रीन लॉक पासवर्ड और पिन की लंबाई और उनमें स्वीकृत वर्णों को नियंत्रित करना."</string> <string name="policylab_watchLogin" msgid="5091404125971980158">"स्क्रीन अनलॉक करने के की कोशिशों पर नज़र रखना"</string> <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"स्क्रीन को अनलॉक करते समय गलत लिखे गए पासवर्ड की संख्या पर निगरानी करें, और बहुत अधिक बार गलत पासवर्ड लिखे जाने पर टैबलेट लॉक करें या टैबलेट का संपूर्ण डेटा मिटाएं."</string> - <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"स्क्रीन को अनलॉक करते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें और यदि बहुत अधिक गलत पासवर्ड लिखे जाते हैं तो टीवी को लॉक करें या टीवी का सभी डेटा मिटा दें."</string> + <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"स्क्रीन को अनलॉक करते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें और अगर बहुत अधिक गलत पासवर्ड लिखे जाते हैं तो टीवी को लॉक करें या टीवी का सभी डेटा मिटा दें."</string> <string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"स्क्रीन को अनलॉक करते समय जितनी बार गलत पासवर्ड लिखा गया है, उसकी संख्या पर नज़र रखना और अगर बहुत बार गलत पासवर्ड डाले गए हैं, तो फ़ोन को लॉक कर देना या फ़ोन का सारा डेटा मिटा देना."</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"स्क्रीन का लॉक खोलते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें, और अगर बार-बार अधिक पासवर्ड लिखे जाते हैं तो टैबलेट को लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटा दें."</string> <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"स्क्रीन का लॉक खोलते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें, और अगर बार-बार गलत पासवर्ड लिखा जाता है तो टीवी को लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटा दें."</string> @@ -765,7 +757,7 @@ <string name="phoneTypeFaxHome" msgid="2067265972322971467">"घर का फ़ैक्स"</string> <string name="phoneTypePager" msgid="7582359955394921732">"पेजर"</string> <string name="phoneTypeOther" msgid="1544425847868765990">"अन्य"</string> - <string name="phoneTypeCallback" msgid="2712175203065678206">"पुन: कॉल करें"</string> + <string name="phoneTypeCallback" msgid="2712175203065678206">"फिर से कॉल करें"</string> <string name="phoneTypeCar" msgid="8738360689616716982">"कार"</string> <string name="phoneTypeCompanyMain" msgid="540434356461478916">"कंपनी का मुख्य"</string> <string name="phoneTypeIsdn" msgid="8022453193171370337">"ISDN"</string> @@ -871,19 +863,19 @@ <string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"कृपया उपयोग के लिए गाइड देखें या ग्राहक सहायता से संपर्क करें."</string> <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"सिम कार्ड लॉक किया गया है."</string> <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"सिम कार्ड अनलॉक कर रहा है…"</string> - <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"आपने अपना अनलॉक आकार <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_password_attempts_dialog_message" msgid="2725973286239344555">"आपने अपना पासवर्ड <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_attempts_dialog_message" msgid="6481623830344107222">"आपने अपना अनलॉक आकार <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_password_attempts_dialog_message" msgid="2725973286239344555">"आपने अपना पासवर्ड <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="6216672706545696955">"आपने अपना पिन <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="9191611984625460820">"आपने अपना अनलॉक आकार <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="5316664559603394684">"आपने अपना अनलॉक पैटन <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="default" msgid="2590227559763762751">"आपने अपना अनलॉक आकार <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="tablet" msgid="9191611984625460820">"आपने अपना अनलॉक आकार <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="5316664559603394684">"आपने अपना अनलॉक पैटन <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="default" msgid="2590227559763762751">"आपने अपना अनलॉक आकार <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="6128106399745755604">"आप टैबलेट का लॉक खोलने के लिए <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="950408382418270260">"आपने टीवी का लॉक खोलने के लिए <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="default" msgid="8603565142156826565">"आप फ़ोन का लॉक खोलने के लिए <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="280873516493934365">"आप टैबलेट को गलत तरीके से <xliff:g id="NUMBER">%d</xliff:g> बार अनलॉक करने का प्रयास कर चुके हैं. टैबलेट अब फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा."</string> <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"आपने टीवी को अनलॉक करने के लिए <xliff:g id="NUMBER">%d</xliff:g> बार गलत तरीके से प्रयास किया है. अब टीवी को फ़ैक्टरी डिफ़ॉल्ट पर रीसेट कर दिया जाएगा."</string> <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"आप फ़ोन को गलत तरीके से <xliff:g id="NUMBER">%d</xliff:g> बार अनलॉक करने का प्रयास कर चुके हैं. फ़ोन अब फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा."</string> - <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string> + <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> सेकंड में फिर से प्रयास करें."</string> <string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"आकार भूल गए?"</string> <string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"खाता अनलॉक"</string> <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"बहुत अधिक आकार प्रयास"</string> @@ -914,7 +906,7 @@ <string name="keyguard_accessibility_camera" msgid="8904231194181114603">"कैमरा"</string> <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"मीडिया नियंत्रण"</string> <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"विजेट फिर से क्रमित करना प्रारंभ."</string> - <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"विजेट फिर से क्रमित करना समाप्त."</string> + <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"विजेट फिर से क्रमित करना खत्म."</string> <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"विजेट <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> को हटा दिया गया."</string> <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"अनलॉक क्षेत्र का विस्तार करें."</string> <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"स्लाइड अनलॉक."</string> @@ -1193,7 +1185,7 @@ <string name="aerr_restart" msgid="7581308074153624475">"ऐप्लिकेशन फिर से खोलें"</string> <string name="aerr_report" msgid="5371800241488400617">"फ़ीडबैक भेजें"</string> <string name="aerr_close" msgid="2991640326563991340">"बंद करें"</string> - <string name="aerr_mute" msgid="1974781923723235953">"डिवाइस पुन: प्रारंभ होने तक म्यूट करें"</string> + <string name="aerr_mute" msgid="1974781923723235953">"डिवाइस फिर से प्रारंभ होने तक म्यूट करें"</string> <string name="aerr_wait" msgid="3199956902437040261">"प्रतीक्षा करें"</string> <string name="aerr_close_app" msgid="3269334853724920302">"ऐप बंद करें"</string> <string name="anr_title" msgid="4351948481459135709"></string> @@ -1246,10 +1238,8 @@ <string name="dump_heap_title" msgid="5864292264307651673">"हीप डंप शेयर करें?"</string> <!-- no translation found for dump_heap_text (8546022920319781701) --> <skip /> - <!-- no translation found for dump_heap_system_text (3236094872980706024) --> - <skip /> - <!-- no translation found for dump_heap_ready_text (1778041771455343067) --> - <skip /> + <string name="dump_heap_system_text" msgid="3236094872980706024">"<xliff:g id="PROC">%1$s</xliff:g> प्रक्रिया अपनी <xliff:g id="SIZE">%2$s</xliff:g> की मेमोरी सीमा पार कर चुकी है. एक हीप डंप शेयर किए जाने के लिए तैयार है. सावधान रहें: इस हीप डंप में कोई ऐसी संवेदनशील निजी जानकारी भी शामिल हो सकती है जिसका एक्सेस प्रोसेस के पास हो. इसमें आपके टाइप किए गए शब्दों का डेटा भी शामिल है."</string> + <string name="dump_heap_ready_text" msgid="1778041771455343067">"<xliff:g id="PROC">%1$s</xliff:g> प्रक्रिया का हीप डंप शेयर किए जाने के लिए तैयार है. सावधान रहें: इस हीप डंप में कोई ऐसी संवेदनशील निजी जानकारी शामिल हो सकती है जिसका एक्सेस प्रोसेस के पास हो. इसमें आपके टाइप किए गए शब्दों का डेटा भी शामिल है."</string> <string name="sendText" msgid="5209874571959469142">"मैसेज करने के लिए कोई कार्रवाई चुनें"</string> <string name="volume_ringtone" msgid="6885421406845734650">"रिंगर वॉल्यूम"</string> <string name="volume_music" msgid="5421651157138628171">"मीडिया वॉल्यूम"</string> @@ -1361,7 +1351,7 @@ <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"हमेशा अनुमति दें"</string> <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"कभी भी अनुमति न दें"</string> <string name="sim_removed_title" msgid="6227712319223226185">"सिमकार्ड निकाला गया"</string> - <string name="sim_removed_message" msgid="2333164559970958645">"मान्य सिम कार्ड डालकर पुन: प्रारंभ करने तक मोबाइल नेटवर्क अनुपलब्ध रहेगा."</string> + <string name="sim_removed_message" msgid="2333164559970958645">"मान्य सिम कार्ड डालकर फिर से प्रारंभ करने तक मोबाइल नेटवर्क अनुपलब्ध रहेगा."</string> <string name="sim_done_button" msgid="827949989369963775">"हो गया"</string> <string name="sim_added_title" msgid="3719670512889674693">"सिम कार्ड जोड़ा गया"</string> <string name="sim_added_message" msgid="6599945301141050216">"मोबाइल नेटवर्क की पहुंच पाने लिए अपना डिवाइस फिर से चालू करें."</string> @@ -1411,7 +1401,7 @@ <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"अस्वीकार करें"</string> <string name="select_input_method" msgid="4653387336791222978">"इनपुट पद्धति चुनें"</string> <string name="show_ime" msgid="2506087537466597099">"सामान्य कीबोर्ड के सक्रिय होने के दौरान इसे स्क्रीन पर बनाए रखें"</string> - <string name="hardware" msgid="194658061510127999">"वर्चूअल कीबोर्ड दिखाएं"</string> + <string name="hardware" msgid="194658061510127999">"वर्चुअल कीबोर्ड दिखाएं"</string> <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"सामान्य कीबोर्ड कॉन्फ़िगर करें"</string> <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"भाषा और लेआउट चुनने के लिए टैप करें"</string> <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> @@ -1655,10 +1645,8 @@ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"ओवरले #<xliff:g id="ID">%1$d</xliff:g>"</string> <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string> <string name="display_manager_overlay_display_secure_suffix" msgid="6022119702628572080">", सुरक्षित"</string> - <!-- no translation found for activity_starter_block_bg_activity_starts_permissive (6995473033438879646) --> - <skip /> - <!-- no translation found for activity_starter_block_bg_activity_starts_enforcing (3317816771072146229) --> - <skip /> + <string name="activity_starter_block_bg_activity_starts_permissive" msgid="6995473033438879646">"आने वाले Q बिल्ड में <xliff:g id="PACKAGENAME">%1$s</xliff:g> के बैकग्राउंड में गतिविधि शुरू करने पर रोक लगा दी जाएगी. इस बारे में जानने के लिए g.co/dev/bgblock पर जाएं."</string> + <string name="activity_starter_block_bg_activity_starts_enforcing" msgid="3317816771072146229">"<xliff:g id="PACKAGENAME">%1$s</xliff:g> के बैकग्राउंड में गतिविधि शुरू करने पर रोक लगा दी गई है. इस बारे में जानने के लिए g.co/dev/bgblock पर जाएं."</string> <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"आकार भूल गए"</string> <string name="kg_wrong_pattern" msgid="1850806070801358830">"गलत पैटर्न डाला गया है"</string> <string name="kg_wrong_password" msgid="2333281762128113157">"गलत पासवर्ड"</string> @@ -1678,7 +1666,7 @@ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"गलत PIN कोड."</string> <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"ऐसा PIN लिखें, जो 4 से 8 अंकों का हो."</string> <string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"PUK कोड 8 अंकों का होना चाहिए."</string> - <string name="kg_invalid_puk" msgid="3638289409676051243">"सही PUK कोड पुन: डालें. बार-बार प्रयास करने से सिम स्थायी रूप से अक्षम हो जाएगी."</string> + <string name="kg_invalid_puk" msgid="3638289409676051243">"सही PUK कोड फिर से डालें. बार-बार प्रयास करने से सिम स्थायी रूप से अक्षम हो जाएगी."</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"पिन कोड का मिलान नहीं होता"</string> <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"बहुत अधिक आकार प्रयास"</string> <string name="kg_login_instructions" msgid="1100551261265506448">"अनलॉक करने के लिए, अपने Google खाते से साइन इन करें."</string> @@ -1688,18 +1676,18 @@ <string name="kg_login_invalid_input" msgid="5754664119319872197">"उपयोगकर्ता नाम या पासवर्ड गलत है"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"अपना उपयोगकर्ता नाम या पासवर्ड भूल गए?\n "<b>"google.com/accounts/recovery"</b>" पर जाएं."</string> <string name="kg_login_checking_password" msgid="1052685197710252395">"खाते की जाँच की जा रही है…"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"आप अपना PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में पुन: प्रयास करें."</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"आप अपना पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में पुन: प्रयास करें."</string> - <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"आपने अपना अनलॉक आकार <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से आरेखित किया है. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में पुन: प्रयास करें."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"आप अपना PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में फिर से प्रयास करें."</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"आप अपना पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में फिर से प्रयास करें."</string> + <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"आपने अपना अनलॉक आकार <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से आरेखित किया है. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में फिर से प्रयास करें."</string> <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"आप टैबलेट का लॉक खोलने के लिए <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से कोशिश कर चुके हैं. <xliff:g id="NUMBER_1">%2$d</xliff:g> बार और गलत कोशिश करने पर, टैबलेट फ़ैक्ट्री डिफ़ॉल्ट पर रीसेट हो जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string> <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"आप टीवी का लॉक खोलने के लिए <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से कोशिश कर चुके हैं. <xliff:g id="NUMBER_1">%2$d</xliff:g> बार और गलत कोशिश करने पर टीवी को फ़ैक्ट्री डिफ़ॉल्ट पर रीसेट कर दिया जाएगा और सभी उपयोगकर्ता डेटा खो जाएगा."</string> <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"आप फ़ोन का लॉक खोलने के लिए <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="2072996269148483637">"आप टैबलेट को अनलॉक करने के लिए <xliff:g id="NUMBER">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. टैबलेट अब फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा."</string> <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"आपने टीवी को अनलॉक करने के लिए <xliff:g id="NUMBER">%d</xliff:g> बार गलत तरीके से प्रयास किया है. अब टीवी को फ़ैक्टरी डिफ़ॉल्ट पर रीसेट कर दिया जाएगा."</string> <string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"आप फ़ोन को अनलॉक करने के लिए <xliff:g id="NUMBER">%d</xliff:g> बार गलत तरीके से प्रयास कर चुके हैं. फ़ोन अब फ़ैक्टरी डिफ़ॉल्ट पर रीसेट हो जाएगा."</string> - <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"आपने अपने अनलॉक आकार को <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="tablet" msgid="3253575572118914370">"आपने अपने अनलॉक आकार को <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="4224651132862313471">"आपने अपने लॉक खोलने के पैटर्न को <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="default" msgid="1437638152015574839">"आपने अपने अनलॉक आकार को <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="default" msgid="1437638152015574839">"आपने अपने अनलॉक आकार को <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="4160700433287233771">" — "</string> <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"निकालें"</string> <string name="safe_media_volume_warning" product="default" msgid="2276318909314492312">"वॉल्यूम को सुझाए गए स्तर से ऊपर बढ़ाएं?\n\nअत्यधिक वॉल्यूम पर अधिक समय तक सुनने से आपकी सुनने की क्षमता को नुकसान हो सकता है."</string> @@ -1822,8 +1810,8 @@ <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"PIN मेल नहीं खाते हैं. फिर से कोशिश करें."</string> <string name="restr_pin_error_too_short" msgid="8173982756265777792">"PIN बहुत छोटा है. कम से कम 4 अंकों का होना चाहिए."</string> <plurals name="restr_pin_countdown" formatted="false" msgid="9061246974881224688"> - <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> सेकंड में पुन: प्रयास करें</item> - <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> सेकंड में पुन: प्रयास करें</item> + <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> सेकंड में फिर से प्रयास करें</item> + <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> सेकंड में फिर से प्रयास करें</item> </plurals> <string name="restr_pin_try_later" msgid="973144472490532377">"बाद में फिर से प्रयास करें"</string> <string name="immersive_cling_title" msgid="8394201622932303336">"आप पूरे स्क्रीन पर देख रहे हैं"</string> @@ -1958,7 +1946,7 @@ <string name="app_info" msgid="6856026610594615344">"ऐप्लिकेशन की जानकारी"</string> <string name="negative_duration" msgid="5688706061127375131">"−<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="5268556852031489931">"डेमो प्रारंभ हो रहा है…"</string> - <string name="demo_restarting_message" msgid="952118052531642451">"डिवाइस पुन: रीसेट कर रहा है…"</string> + <string name="demo_restarting_message" msgid="952118052531642451">"डिवाइस फिर से रीसेट कर रहा है…"</string> <string name="suspended_widget_accessibility" msgid="6712143096475264190">"अक्षम <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="conference_call" msgid="3751093130790472426">"कॉन्फ़्रेंस कॉल"</string> <string name="tooltip_popup_title" msgid="5253721848739260181">"टूलटिप"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 4edf36e08973..296278577a3c 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -140,7 +140,7 @@ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string> <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Chiamate Wi-Fi"</string> <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWifi"</string> - <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Non attiva"</string> + <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Off"</string> <string name="wfc_mode_wifi_preferred_summary" msgid="7335489823608689868">"Chiamata tramite Wi-Fi"</string> <string name="wfc_mode_cellular_preferred_summary" msgid="7081742743152286290">"Chiamata su rete mobile"</string> <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Solo Wi-Fi"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index c0fa639c30f7..1f1df8f453ad 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -307,7 +307,7 @@ <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"האם לאפשר לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> גישה לפעילות הגופנית שלך?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"מצלמה"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"צילום תמונות והקלטת וידאו"</string> - <string name="permgrouprequest_camera" msgid="1299833592069671756">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאה לצלם תמונות וסרטונים?"</string> + <string name="permgrouprequest_camera" msgid="1299833592069671756">"לאשר לאפליקציה של <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> לצלם תמונות וסרטונים?"</string> <string name="permgrouplab_calllog" msgid="8798646184930388160">"יומני שיחות"</string> <string name="permgroupdesc_calllog" msgid="3006237336748283775">"קריאה וכתיבה של יומן השיחות של הטלפון"</string> <string name="permgrouprequest_calllog" msgid="8487355309583773267">"לתת לאפליקציה <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> הרשאת גישה ליומני השיחות של הטלפון?"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 17169e19fcd1..50bbca4fdd4b 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -301,7 +301,7 @@ <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"運動データへのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"カメラ"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"写真と動画の撮影"</string> - <string name="permgrouprequest_camera" msgid="1299833592069671756">"写真と動画の撮影を <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> + <string name="permgrouprequest_camera" msgid="1299833592069671756">"写真と動画の撮影を「<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>」に許可しますか?"</string> <string name="permgrouplab_calllog" msgid="8798646184930388160">"通話履歴"</string> <string name="permgroupdesc_calllog" msgid="3006237336748283775">"通話履歴の読み取りと書き込み"</string> <string name="permgrouprequest_calllog" msgid="8487355309583773267">"通話履歴へのアクセスを <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> に許可しますか?"</string> @@ -545,7 +545,7 @@ <string name="fingerprint_error_unable_to_process" msgid="6107816084103552441">"もう一度お試しください。"</string> <string name="fingerprint_error_no_fingerprints" msgid="7654382120628334248">"指紋が登録されていません。"</string> <string name="fingerprint_error_hw_not_present" msgid="409523969613176352">"このデバイスには指紋認証センサーがありません。"</string> - <string name="fingerprint_name_template" msgid="5870957565512716938">"指紋<xliff:g id="FINGERID">%d</xliff:g>"</string> + <string name="fingerprint_name_template" msgid="5870957565512716938">"指紋 <xliff:g id="FINGERID">%d</xliff:g>"</string> <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指紋アイコン"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index ac8a6dfddf69..d465a52792f0 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -1843,7 +1843,7 @@ <string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"ავარიული პაუზა"</string> <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"სამუშაო კვირის ღამე"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"შაბათ-კვირა"</string> - <string name="zen_mode_default_events_name" msgid="8158334939013085363">"მოვლენა"</string> + <string name="zen_mode_default_events_name" msgid="8158334939013085363">"მოვლენისას"</string> <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"ძილისას"</string> <string name="muted_by" msgid="5942954724562097128">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> ზოგიერთ ხმას ადუმებს"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"ფიქსირდება თქვენი მ ოწყობილობის შიდა პრობლემა და შეიძლება არასტაბილური იყოს, სანამ ქარხნულ მონაცემების არ განაახლებთ."</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 74488b5fbfc9..aa3c1a880c8c 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -283,7 +283,7 @@ <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасына құрылғының орналасқан жері туралы мәліметтерді пайдалануға рұқсат берілсін бе?"</string> <string name="permgrouprequestdetail_location" msgid="1347189607421252902">"Қолданбаны пайдалану кезінде ғана оған геодеректеріңізді көруге рұқсат етіледі."</string> <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасына құрылғының геодеректері <b>үнемі</b> көрсетіліп тұрсын ба?"</string> - <string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"Қолданба геодеректерді тек жұмыс кезінде ғана пайдалана алады"</string> + <string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"Қолданба геодеректерді тек жұмыс кезінде ғана пайдалана алады."</string> <string name="permgrouplab_calendar" msgid="5863508437783683902">"Күнтізбе"</string> <string name="permgroupdesc_calendar" msgid="3889615280211184106">"күнтізбеге кіру"</string> <string name="permgrouprequest_calendar" msgid="289900767793189421">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> қолданбасына күнтізбеге кіруге рұқсат берілсін бе?"</string> @@ -553,7 +553,7 @@ <string name="permdesc_manageFace" msgid="8919637120670185330">"Қолданбаға пайдаланатын бет үлгілерін енгізу және жою әдістерін шақыруға мүмкіндік береді."</string> <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"бетті тану жабдығын пайдалану"</string> <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"Қолданбаға бетті тану жабдығын қолдануға рұқсат етеді"</string> - <string name="face_acquired_insufficient" msgid="2767330364802375742">"Дәл бет деректері алынбады. Әрекетті қайталаңыз."</string> + <string name="face_acquired_insufficient" msgid="2767330364802375742">"Бет деректері дұрыс алынбады. Әрекетті қайталаңыз."</string> <string name="face_acquired_too_bright" msgid="5005650874582450967">"Тым ашық. Күңгірттеу жарық керек."</string> <string name="face_acquired_too_dark" msgid="1966194696381394616">"Тым қараңғы. Молырақ жарық керек."</string> <string name="face_acquired_too_close" msgid="1401011882624272753">"Телефонды алшақ ұстаңыз."</string> @@ -576,7 +576,7 @@ <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="396883585636963908">"Бетті тану мүмкін емес. Жабдық қолжетімді емес."</string> - <string name="face_error_timeout" msgid="2605673935810019129">"Бет тануды күту уақыты бітті. Әрекетті қайталаңыз."</string> + <string name="face_error_timeout" msgid="2605673935810019129">"Бетті тану уақыты бітті. Әрекетті қайталаңыз."</string> <string name="face_error_no_space" msgid="2712120617457553825">"Жаңа бетті сақтау мүмкін емес. Алдымен ескісін жойыңыз."</string> <string name="face_error_canceled" msgid="2768146728600802422">"Бетті танудан бас тартылды."</string> <string name="face_error_user_canceled" msgid="9003022830076496163">"Пайдаланушы бетті тану әрекетінен бас тартты."</string> @@ -1210,7 +1210,7 @@ <string name="dump_heap_ready_text" msgid="1778041771455343067">"<xliff:g id="PROC">%1$s</xliff:g> процесінің дамп файлы бөлісуге дайын. Бұл дамп файлында процесс кезінде пайдаланылған кез келген құпия жеке ақпарат (соның ішінде сіз енгізген деректер) болуы мүмкін екенін ескеріңіз."</string> <string name="sendText" msgid="5209874571959469142">"Мәтін үшін әрекет таңдау"</string> <string name="volume_ringtone" msgid="6885421406845734650">"Қоңырау шырылының қаттылығы"</string> - <string name="volume_music" msgid="5421651157138628171">"Meдиа дыбысының қаттылығы"</string> + <string name="volume_music" msgid="5421651157138628171">"Mультимeдиа дыбыс деңгейі"</string> <string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Bluetooth арқылы ойнату"</string> <string name="volume_music_hint_silent_ringtone_selected" msgid="8310739960973156272">"Үнсіз қоңырау әуенін орнату"</string> <string name="volume_call" msgid="3941680041282788711">"Келетін қоңырау дыбысының қаттылығы"</string> @@ -1220,8 +1220,8 @@ <string name="volume_unknown" msgid="1400219669770445902">"Дыбыс қаттылығы"</string> <string name="volume_icon_description_bluetooth" msgid="6538894177255964340">"Bluetooth дыбысының қаттылығы"</string> <string name="volume_icon_description_ringer" msgid="3326003847006162496">"Қоңырау әуенінің дыбыс қаттылығы"</string> - <string name="volume_icon_description_incall" msgid="8890073218154543397">"Қоңырау дыбысының қаттылығы"</string> - <string name="volume_icon_description_media" msgid="4217311719665194215">"Meдиа дыбысының қаттылығы"</string> + <string name="volume_icon_description_incall" msgid="8890073218154543397">"Қоңыраудағы дыбыс деңгейі"</string> + <string name="volume_icon_description_media" msgid="4217311719665194215">"Mультимeдиа дыбыс деңгейі"</string> <string name="volume_icon_description_notification" msgid="7044986546477282274">"Хабар дыбысының қаттылығы"</string> <string name="ringtone_default" msgid="3789758980357696936">"Әдепкі рингтон"</string> <string name="ringtone_default_with_actual" msgid="1767304850491060581">"Әдепкі (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 9e2f718fe4d6..8c4981148045 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -578,7 +578,7 @@ <string name="face_error_hw_not_available" msgid="396883585636963908">"មិនអាចផ្ទៀងផ្ទាត់មុខបានទេ។ មិនមានហាតវែរទេ។"</string> <string name="face_error_timeout" msgid="2605673935810019129">"ការសម្គាល់មុខបានអស់ម៉ោង។ សូមព្យាយាមម្ដងទៀត។"</string> <string name="face_error_no_space" msgid="2712120617457553825">"មិនអាចផ្ទុកទិន្នន័យទម្រង់មុខថ្មីបានទេ។ សូមលុបទិន្នន័យទម្រង់មុខចាស់ជាមុនសិន។"</string> - <string name="face_error_canceled" msgid="2768146728600802422">"បានបោះបង់ប្រតិបត្តិការចាប់ទម្រង់មុខ។"</string> + <string name="face_error_canceled" msgid="2768146728600802422">"បានបោះបង់ប្រតិបត្តិការចាប់ទម្រង់មុខ"</string> <string name="face_error_user_canceled" msgid="9003022830076496163">"ការផ្ទៀងផ្ទាត់មុខត្រូវបានបោះបង់ដោយអ្នកប្រើប្រាស់"</string> <string name="face_error_lockout" msgid="3407426963155388504">"ព្យាយាមចូលច្រើនពេកហើយ។ សូមព្យាយាមម្តងទៀតពេលក្រោយ។"</string> <string name="face_error_lockout_permanent" msgid="3485837851962070925">"ព្យាយាមចូលច្រើនពេក។ បានបិទការផ្ទៀងផ្ទាត់មុខ។"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 52e8bc700bd1..c0f0f44c415b 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -519,8 +519,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"ನಿಮ್ಮ ಫೋಟೋ ಸಂಗ್ರಹಣೆಯನ್ನು ಮಾರ್ಪಡಿಸಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"ನಿಮ್ಮ ಮೀಡಿಯಾ ಸಂಗ್ರಹಣೆಯಿಂದ ಸ್ಥಳಗಳನ್ನು ಓದಿ"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"ನಿಮ್ಮ ಮೀಡಿಯಾ ಸಂಗ್ರಹಣೆಯಿಂದ ಸ್ಥಳಗಳನ್ನು ಓದಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"ಇದು ನೀವೇ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್ವೇರ್ ಲಭ್ಯವಿಲ್ಲ"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"ಪ್ರಮಾಣೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string> @@ -528,7 +527,7 @@ <string name="biometric_error_device_not_secured" msgid="6583143098363528349">"ಪಿನ್, ಪ್ಯಾಟರ್ನ್ ಅಥವಾ ಪಾಸ್ವರ್ಡ್ ಸೆಟ್ ಮಾಡಿಲ್ಲ"</string> <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ಭಾಗಶಃ ಬೆರಳಚ್ಚು ಪತ್ತೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string> <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ಬೆರಳಚ್ಚು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string> - <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ಬೆರಳಚ್ಚು ಸೆನ್ಸಾರ್ ಕೊಳೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಅದನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string> + <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ಬೆರಳಚ್ಚು ಸೆನ್ಸರ್ ಮಲಿನಗೊಂಡಿದೆ. ದಯವಿಟ್ಟು ಅದನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string> <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"ಬೆರಳನ್ನು ಅತಿ ವೇಗವಾಗಿ ಸರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string> <string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ಬೆರಳನ್ನು ತುಂಬಾ ನಿಧಾನವಾಗಿ ಸರಿಸಲಾಗಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string> <string-array name="fingerprint_acquired_vendor"> @@ -1206,7 +1205,7 @@ <string name="dump_heap_ready_notification" msgid="1162196579925048701">"<xliff:g id="PROC">%1$s</xliff:g> ಹೀಪ್ ಡಂಪ್ ಸಿದ್ಧವಾಗಿದೆ"</string> <string name="dump_heap_notification_detail" msgid="3993078784053054141">"ಹೀಪ್ ಡಂಪ್ ಅನ್ನು ಸಂಗ್ರಹಿಸಲಾಗಿದೆ; ಹಂಚಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="dump_heap_title" msgid="5864292264307651673">"ಹೀಪ್ ಡಂಪ್ ಹಂಚಿಕೊಳ್ಳುವುದೇ?"</string> - <string name="dump_heap_text" msgid="8546022920319781701">"<xliff:g id="PROC">%1$s</xliff:g> ಪ್ರಕ್ರಿಯೆಯು ತನ್ನ <xliff:g id="SIZE">%2$s</xliff:g> ಮೆಮೊರಿ ಮಿತಿಯನ್ನು ಮೀರಿದೆ. ಅದರ ಡೆವಲಪರ್ ಜೊತೆಗೆ ಹಂಚಿಕೊಳ್ಳಲು ಹಂಚಿಕೊಳ್ಳಲು ನಿಮಗಾಗಿ ಹೀಪ್ ಡಂಪ್ ಲಭ್ಯವಿದೆ. ಎಚ್ಚರಿಕೆ: ಈ ಹೀಪ್ ಡಂಪ್, ಪ್ರಕ್ರಿಯೆಯು ಪ್ರವೇಶ ಹೊಂದಿರುವ ಯಾವುದೇ ನಿಮ್ಮ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಿರಬಹುದು."</string> + <string name="dump_heap_text" msgid="8546022920319781701">"<xliff:g id="PROC">%1$s</xliff:g> ಪ್ರಕ್ರಿಯೆಯು ತನ್ನ <xliff:g id="SIZE">%2$s</xliff:g> ಮೆಮೊರಿ ಮಿತಿಯನ್ನು ಮೀರಿದೆ. ಅದರ ಡೆವಲಪರ್ ಜೊತೆಗೆ ಹಂಚಿಕೊಳ್ಳಲು ನಿಮಗಾಗಿ ಹೀಪ್ ಡಂಪ್ ಲಭ್ಯವಿದೆ. ಎಚ್ಚರಿಕೆ: ಈ ಹೀಪ್ ಡಂಪ್, ಅಪ್ಲಿಕೇಶನ್ ಪ್ರವೇಶ ಹೊಂದಿರುವ ನಿಮ್ಮ ಯಾವುದೇ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಿರಬಹುದು."</string> <string name="dump_heap_system_text" msgid="3236094872980706024">"<xliff:g id="PROC">%1$s</xliff:g> ಪ್ರಕ್ರಿಯೆಯು ತನ್ನ <xliff:g id="SIZE">%2$s</xliff:g> ಮೆಮೊರಿ ಮಿತಿಯನ್ನು ಮೀರಿದೆ. ಹಂಚಿಕೊಳ್ಳಲು ನಿಮಗಾಗಿ ಹೀಪ್ ಡಂಪ್ ಲಭ್ಯವಿದೆ. ಎಚ್ಚರಿಕೆ: ಈ ಹೀಪ್ ಡಂಪ್, ಪ್ರಕ್ರಿಯೆಯು ಯಾವುದೇ ಸೂಕ್ಷ್ಮ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಗೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರಬಹುದು, ಇದು ನೀವು ಟೈಪ್ ಮಾಡಿದ ವಿಷಯಗಳನ್ನು ಸಹ ಒಳಗೊಂಡಿರಬಹುದು."</string> <string name="dump_heap_ready_text" msgid="1778041771455343067">"<xliff:g id="PROC">%1$s</xliff:g> ನ ಪ್ರಕ್ರಿಯೆಯ ಹೀಪ್ ಡಂಪ್ ನಿಮಗಾಗಿ ಹಂಚಿಕೊಳ್ಳಲು ಲಭ್ಯವಿದೆ. ಎಚ್ಚರಿಕೆ: ಈ ಹೀಪ್ ಡಂಪ್, ಪ್ರಕ್ರಿಯೆಯು ಯಾವುದೇ ಸೂಕ್ಷ್ಮ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಗೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರಬಹುದು, ಇದು ನೀವು ಟೈಪ್ ಮಾಡಿದ ವಿಷಯಗಳನ್ನು ಸಹ ಒಳಗೊಂಡಿರಬಹುದು."</string> <string name="sendText" msgid="5209874571959469142">"ಪಠ್ಯಕ್ಕೆ ಕ್ರಿಯೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> @@ -1350,8 +1349,8 @@ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB ಡೀಬಗಿಂಗ್ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string> <string name="adb_active_notification_message" msgid="7463062450474107752">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ಆಫ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಆಯ್ಕೆ ಮಾಡಿ."</string> - <string name="test_harness_mode_notification_title" msgid="2216359742631914387">"ಸ್ವಯಂ ಪರೀಕ್ಷೆಯಾಗುವುದು ಮೋಡ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> - <string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ಸ್ವಯಂ ಪರೀಕ್ಷೆಯಾಗುವುದು ಮೋಡ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಫ್ಯಾಕ್ಟರಿ ರಿಸೆಟ್ ಮಾಡಬೇಕು."</string> + <string name="test_harness_mode_notification_title" msgid="2216359742631914387">"ಸ್ವಯಂ ಪರೀಕ್ಷೆಯಾಗುವಿಕೆ ಮೋಡ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> + <string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ಸ್ವಯಂ ಪರೀಕ್ಷೆಯಾಗುವಿಕೆ ಮೋಡ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಫ್ಯಾಕ್ಟರಿ ರಿಸೆಟ್ ಮಾಡಬೇಕು."</string> <string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB ಪೋರ್ಟ್ನಲ್ಲಿ ದ್ರವ ಅಥವಾ ಧೂಳಿನ ಕಣಗಳಿವೆ"</string> <string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ಪೋರ್ಟ್ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ. ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="usb_contaminant_not_detected_title" msgid="4202417484434906086">"USB ಪೋರ್ಟ್ ಬಳಸಲು ಸುರಕ್ಷಿತವಾಗಿದೆ"</string> @@ -1996,7 +1995,7 @@ <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"ಬ್ಯಾಟರಿ ಅವಧಿ ಹೆಚ್ಚಿಸಲು ಬ್ಯಾಟರಿ ಸೇವರ್ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="battery_saver_notification_channel_name" msgid="2083316159716201806">"ಬ್ಯಾಟರಿ ಸೇವರ್"</string> <string name="battery_saver_sticky_disabled_notification_title" msgid="6376147579378764641">"ಇನ್ನೊಮ್ಮೆ ಬ್ಯಾಟರಿ ಕಡಿಮೆಯಾಗುವವರೆಗೂ ಬ್ಯಾಟರಿ ಸೇವರ್ ಮರುಸಕ್ರಿಯವಾಗುವುದಿಲ್ಲ"</string> - <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"ಸಾಕಾಗುವಷ್ಟು ಬ್ಯಾಟರಿಯನ್ನು ಚಾರ್ಜ್ ಮಾಡಲಾಗಿದೆ. ಇನ್ನೊಮ್ಮೆ ಬ್ಯಾಟರಿ ಕಡಿಮೆಯಾಗುವವರೆಗೂ ಬ್ಯಾಟರಿ ಸೇವರ್ ಮರುಸಕ್ರಿಯವಾಗುವುದಿಲ್ಲ."</string> + <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"ಬ್ಯಾಟರಿಯನ್ನು ಬೇಕಾಗಿರುವಷ್ಟು ಮಟ್ಟಕ್ಕೆ ಚಾರ್ಜ್ ಮಾಡಲಾಗಿದೆ. ಇನ್ನೊಮ್ಮೆ ಬ್ಯಾಟರಿ ಕಡಿಮೆಯಾಗುವವರೆಗೂ ಬ್ಯಾಟರಿ ಸೇವರ್ ಮರುಸಕ್ರಿಯವಾಗುವುದಿಲ್ಲ."</string> <string name="battery_saver_charged_notification_title" product="default" msgid="2960978289873161288">"ಫೋನ್ <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> ಚಾರ್ಜ್ ಆಗಿದೆ"</string> <string name="battery_saver_charged_notification_title" product="tablet" msgid="7555713825806482451">"ಟ್ಯಾಬ್ಲೆಟ್ <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> ಚಾರ್ಜ್ ಆಗಿದೆ"</string> <string name="battery_saver_charged_notification_title" product="device" msgid="5954873381559605660">"ಸಾಧನ <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> ಚಾರ್ಜ್ ಆಗಿದೆ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 5422aedea5b4..bc278e2f965c 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1843,7 +1843,7 @@ <string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"다운타임"</string> <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"평일 밤"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"주말"</string> - <string name="zen_mode_default_events_name" msgid="8158334939013085363">"일정"</string> + <string name="zen_mode_default_events_name" msgid="8158334939013085363">"캘린더 일정"</string> <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"수면 중"</string> <string name="muted_by" msgid="5942954724562097128">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g>(이)가 일부 소리를 음소거함"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"사용 중인 기기 내부에 문제가 발생했습니다. 초기화할 때까지 불안정할 수 있습니다."</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 60031270ffcd..8810aeb1b7e7 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -655,7 +655,7 @@ <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Экрандын кулпусун ачуу учурунда туура эмес терилген сырсөздөрдү тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, сыналгыны кулпулап же бул колдонуучунун бардык дайындарын тазалап салуу."</string> <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Экрандын кулпусун ачуу учурунда туура эмес терилген сырсөздөрдү тескөө жана сырсөз өтө көп жолу туура эмес терилген болсо, телефонду кулпулап же бул колдонуучунун бардык дайындарын тазалап салуу."</string> <string name="policylab_resetPassword" msgid="4934707632423915395">"Экран кулпусун өзгөртүү"</string> - <string name="policydesc_resetPassword" msgid="1278323891710619128">"Экран кулпусун өзгөртүү."</string> + <string name="policydesc_resetPassword" msgid="1278323891710619128">"Экран кулпусун өзгөртөт."</string> <string name="policylab_forceLock" msgid="2274085384704248431">"Экранды кулпулоо"</string> <string name="policydesc_forceLock" msgid="1141797588403827138">"Экран качан жана кантип кулпулана турганын башкарат."</string> <string name="policylab_wipeData" msgid="3910545446758639713">"Бардык маалыматты өчүрүү"</string> @@ -675,7 +675,7 @@ <string name="policylab_disableCamera" msgid="6395301023152297826">"Камераларды өчүрүү"</string> <string name="policydesc_disableCamera" msgid="2306349042834754597">"Түзмөктүн бардык камераларын колдонууга тыюу салуу."</string> <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Функцияларды өчүрүү"</string> - <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Экранды кулпулоо функцияларынын айрымдарын колдонууга тыюу салуу"</string> + <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Экранды кулпулаган функциялардын айрымдарын колдонууга тыюу салат."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Үй"</item> <item msgid="869923650527136615">"Мобилдик"</item> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index a529258abe65..7f347c8d0dd0 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -528,7 +528,7 @@ <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Откриен е делумен отпечаток. Обидете се повторно."</string> <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Отпечатокот не можеше да се обработи. Обидете се повторно."</string> <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензорот за отпечатоци е валкан. Исчистете го и обидете се повторно."</string> - <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Прстот се дрижеше пребрзо. Обидете се повторно."</string> + <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"Прстот се движеше пребрзо. Обидете се повторно."</string> <string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Прстот се движеше премногу бавно. Обидете се повторно."</string> <string-array name="fingerprint_acquired_vendor"> </string-array> @@ -1362,7 +1362,7 @@ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"СПОДЕЛИ"</string> <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"ОДБИЈ"</string> <string name="select_input_method" msgid="4653387336791222978">"Одбери метод на внес"</string> - <string name="show_ime" msgid="2506087537466597099">"Прикажувај го на екранот додека е активна физичката тастатура"</string> + <string name="show_ime" msgid="2506087537466597099">"Прикажувај ја на екранот додека е активна физичката тастатура"</string> <string name="hardware" msgid="194658061510127999">"Прикажи виртуелна тастатура"</string> <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Конфигурирајте физичка тастатура"</string> <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Допрете за избирање јазик и распоред"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index af69c6535097..4962885a9c67 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -141,10 +141,8 @@ <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"വൈഫൈ കോളിംഗ്"</string> <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"Voവൈഫൈ"</string> <string name="wifi_calling_off_summary" msgid="8720659586041656098">"ഓഫ്"</string> - <!-- no translation found for wfc_mode_wifi_preferred_summary (7335489823608689868) --> - <skip /> - <!-- no translation found for wfc_mode_cellular_preferred_summary (7081742743152286290) --> - <skip /> + <string name="wfc_mode_wifi_preferred_summary" msgid="7335489823608689868">"വൈഫൈ മുഖേനയുള്ള കോൾ"</string> + <string name="wfc_mode_cellular_preferred_summary" msgid="7081742743152286290">"മൊബൈൽ നെറ്റ്വർക്ക് മുഖേനയുള്ള കോൾ"</string> <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"വൈഫൈ മാത്രം"</string> <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: കൈമാറിയില്ല"</string> <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string> @@ -230,8 +228,7 @@ <string name="global_action_bug_report" msgid="7934010578922304799">"ബഗ് റിപ്പോർട്ട്"</string> <string name="global_action_logout" msgid="935179188218826050">"സെഷൻ അവസാനിപ്പിക്കുക"</string> <string name="global_action_screenshot" msgid="8329831278085426283">"സ്ക്രീൻഷോട്ട്"</string> - <!-- no translation found for bugreport_title (5981047024855257269) --> - <skip /> + <string name="bugreport_title" msgid="5981047024855257269">"ബഗ് റിപ്പോർട്ട്"</string> <string name="bugreport_message" msgid="398447048750350456">"ഒരു ഇമെയിൽ സന്ദേശമായി അയയ്ക്കുന്നതിന്, ഇത് നിങ്ങളുടെ നിലവിലെ ഉപകരണ നിലയെക്കുറിച്ചുള്ള വിവരങ്ങൾ ശേഖരിക്കും. ബഗ് റിപ്പോർട്ട് ആരംഭിക്കുന്നതിൽ നിന്ന് ഇത് അയയ്ക്കാനായി തയ്യാറാകുന്നതുവരെ അൽപ്പസമയമെടുക്കും; ക്ഷമയോടെ കാത്തിരിക്കുക."</string> <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ഇന്റരാക്റ്റീവ് റിപ്പോർട്ട്"</string> <string name="bugreport_option_interactive_summary" msgid="229299488536107968">"മിക്ക സാഹചര്യങ്ങളിലും ഇത് ഉപയോഗിക്കുക. റിപ്പോർട്ടിന്റെ പുരോഗതി കാണാനും പ്രശ്നത്തെ കുറിച്ചുള്ള കൂടുതൽ വിശദാംശങ്ങൾ നൽകാനും സ്ക്രീൻഷോട്ടുകൾ എടുക്കാനും ഇത് അനുവദിക്കുന്നു. റിപ്പോർട്ടുചെയ്യാൻ നീണ്ട സമയം എടുക്കുന്നതും നിങ്ങൾ കുറച്ച് ഉപയോഗിക്കുന്നതുമായ ചില വിഭാഗങ്ങളെ ഇത് വിട്ടുകളഞ്ഞേക്കാം."</string> @@ -284,12 +281,9 @@ <string name="permgrouplab_location" msgid="7275582855722310164">"ലൊക്കേഷൻ"</string> <string name="permgroupdesc_location" msgid="1346617465127855033">"ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാൻ"</string> <string name="permgrouprequest_location" msgid="3788275734953323491">"ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാൻ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ആപ്പിനെ അനുവദിക്കണോ?"</string> - <!-- no translation found for permgrouprequestdetail_location (1347189607421252902) --> - <skip /> - <!-- no translation found for permgroupbackgroundrequest_location (5039063878675613235) --> - <skip /> - <!-- no translation found for permgroupbackgroundrequestdetail_location (4597006851453417387) --> - <skip /> + <string name="permgrouprequestdetail_location" msgid="1347189607421252902">"നിങ്ങൾ ആപ്പ് ഉപയോഗിക്കുമ്പോൾ മാത്രമേ അതിന് ലൊക്കേഷൻ ആക്സസ് ലഭിക്കൂ."</string> + <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനെ ഈ ഉപകരണത്തിന്റെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാൻ എപ്പോഴും അനുവദിക്കണോ?"</string> + <string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"നിലവിൽ, ഉപയോഗിക്കുമ്പോൾ മാത്രം ആപ്പിന് ലൊക്കേഷൻ ആക്സസ് ചെയ്യാം"</string> <string name="permgrouplab_calendar" msgid="5863508437783683902">"കലണ്ടർ"</string> <string name="permgroupdesc_calendar" msgid="3889615280211184106">"നിങ്ങളുടെ കലണ്ടർ ആക്സസ്സ് ചെയ്യുക"</string> <string name="permgrouprequest_calendar" msgid="289900767793189421">"നിങ്ങളുടെ കലണ്ടർ ആക്സസ് ചെയ്യാൻ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ആപ്പിനെ അനുവദിക്കണോ?"</string> @@ -509,10 +503,8 @@ <string name="permdesc_nfc" msgid="7120611819401789907">"നിയർ ഫീൽഡ് കമ്മ്യൂണിക്കേഷൻ (NFC) ടാഗുകളുമായും കാർഡുകളുമായും റീഡറുകളുമായുള്ള ആശയവിനിമയത്തിന് അപ്ലിക്കേഷനുകളെ അനുവദിക്കുന്നു."</string> <string name="permlab_disableKeyguard" msgid="3598496301486439258">"നിങ്ങളുടെ സ്ക്രീൻ ലോക്ക് പ്രവർത്തനരഹിതമാക്കുക"</string> <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"കീലോക്കും ഏതെങ്കിലും അനുബന്ധ പാസ്വേഡ് സുരക്ഷയും പ്രവർത്തനരഹിതമാക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഉദാഹരണത്തിന്, ഒരു ഇൻകമിംഗ് കോൾ സ്വീകരിക്കുമ്പോൾ ഫോൺ കീലോക്ക് പ്രവർത്തനരഹിതമാക്കുന്നു, കോൾ അവസാനിക്കുമ്പോൾ കീലോക്ക് വീണ്ടും പ്രവർത്തനക്ഷമമാകുന്നു."</string> - <!-- no translation found for permlab_requestPasswordComplexity (202650535669249674) --> - <skip /> - <!-- no translation found for permdesc_requestPasswordComplexity (4730994229754212347) --> - <skip /> + <string name="permlab_requestPasswordComplexity" msgid="202650535669249674">"സ്ക്രീൻ ലോക്ക് സങ്കീർണ്ണത അഭ്യർത്ഥിക്കുക"</string> + <string name="permdesc_requestPasswordComplexity" msgid="4730994229754212347">"സ്ക്രീൻ ലോക്കിന്റെ സാധ്യമായ നീളവും തരവും സൂചിപ്പിക്കുന്ന, അതിന്റെ സങ്കീർണ്ണത നില (ഉയർന്നത്, ഇടത്തരം, കുറഞ്ഞത് അല്ലെങ്കിൽ ഒന്നുമില്ല) മനസ്സിലാക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. സ്ക്രീൻ ലോക്ക് ഒരു പ്രത്യേക തലത്തിലേക്ക് അപ്ഡേറ്റ് ചെയ്യാൻ ഉപയോക്താക്കളെ നിർദ്ദേശിക്കാനും ആപ്പിനാവും, പക്ഷെ ഉപയോക്താക്കൾക്ക് എളുപ്പത്തിൽ അവഗണിക്കാനും മറ്റൊന്നിലേക്ക് നാവിഗേറ്റ് ചെയ്യാനുമാവും. പ്ലെയിൻടെക്സ്റ്റിൽ സ്ക്രീൻ ലോക്ക് സംഭരിക്കപ്പെട്ടിട്ടില്ലെന്ന കാര്യം ശ്രദ്ധിക്കുക, അതിനാൽ ആപ്പിന് കൃത്യമായ പാസ്വേഡ് അറിയില്ല."</string> <string name="permlab_useBiometric" msgid="8837753668509919318">"ബയോമെട്രിക് ഹാർഡ്വെയർ ഉപയോഗിക്കുക"</string> <string name="permdesc_useBiometric" msgid="8389855232721612926">"പരിശോധിച്ചുറപ്പിക്കുന്നതിനായി, ബയോമെട്രിക് ഹാർഡ്വെയർ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുക"</string> <string name="permlab_manageFingerprint" msgid="5640858826254575638">"ഫിംഗർപ്രിന്റ് ഹാർഡ്വെയർ നിയന്ത്രിക്കുക"</string> @@ -527,8 +519,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"നിങ്ങളുടെ ഫോട്ടോ ശേഖരം പരിഷ്ക്കരിക്കുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"നിങ്ങളുടെ മീഡിയ ശേഖരത്തിൽ നിന്നും ലൊക്കേഷനുകൾ റീഡ് ചെയ്യുക"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"നിങ്ങളുടെ മീഡിയ ശേഖരത്തിൽ നിന്നും ലൊക്കേഷനുകൾ റീഡ് ചെയ്യുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"ഇത് നിങ്ങളാണെന്ന് പരിശോധിച്ചുറപ്പിക്കുക"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ബയോമെട്രിക് ഹാർഡ്വെയർ ലഭ്യമല്ല"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"പരിശോധിച്ചുറപ്പിക്കൽ റദ്ദാക്കി"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"തിരിച്ചറിഞ്ഞില്ല"</string> @@ -536,7 +527,7 @@ <string name="biometric_error_device_not_secured" msgid="6583143098363528349">"പിന്നോ പാറ്റേണോ പാസ്വേഡോ സജ്ജീകരിച്ചിട്ടില്ല"</string> <string name="fingerprint_acquired_partial" msgid="735082772341716043">"വിരലടയാളം ഭാഗികമായി തിരിച്ചറിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string> <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"വിരലടയാളം പ്രോസസ്സ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string> - <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"വിരലടയാള സെൻസറിന് വൃത്തിയില്ല. അത് ശുചിയാക്കി വീണ്ടും ശ്രമിക്കുക."</string> + <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"വിരലടയാള സെൻസറിൽ ചെളിയുണ്ട്. അത് വൃത്തിയാക്കി വീണ്ടും ശ്രമിക്കുക."</string> <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"വിരൽ വളരെ വേഗത്തിൽ നീക്കി. വീണ്ടും ശ്രമിക്കുക."</string> <string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"വിരൽ വളരെ പതുക്കെ നീക്കി. വീണ്ടും ശ്രമിക്കുക."</string> <string-array name="fingerprint_acquired_vendor"> @@ -562,55 +553,36 @@ <string name="permdesc_manageFace" msgid="8919637120670185330">"ഉപയോഗിക്കാനായി, മുഖത്തിന്റെ ടെംപ്ലേറ്റുകൾ ചേർക്കാനും ഇല്ലാതാക്കാനുമുള്ള രീതികൾ അഭ്യർത്ഥിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string> <string name="permlab_useFaceAuthentication" msgid="8996134460546804535">"മുഖം തിരിച്ചറിയൽ ഹാർഡ്വെയർ ഉപയോഗിക്കുക"</string> <string name="permdesc_useFaceAuthentication" msgid="5011118722951833089">"പരിശോധിച്ചുറപ്പിക്കലിനായി മുഖം തിരിച്ചറിയൽ ഹാർഡ്വെയർ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു"</string> - <!-- no translation found for face_acquired_insufficient (2767330364802375742) --> - <skip /> - <!-- no translation found for face_acquired_too_bright (5005650874582450967) --> - <skip /> - <!-- no translation found for face_acquired_too_dark (1966194696381394616) --> - <skip /> - <!-- no translation found for face_acquired_too_close (1401011882624272753) --> - <skip /> - <!-- no translation found for face_acquired_too_far (1210969240069012510) --> - <skip /> - <!-- no translation found for face_acquired_too_high (3362395713403348013) --> - <skip /> - <!-- no translation found for face_acquired_too_low (488983581737550912) --> - <skip /> - <!-- no translation found for face_acquired_too_right (941726879175375970) --> - <skip /> - <!-- no translation found for face_acquired_too_left (5873592047381190672) --> - <skip /> - <!-- no translation found for face_acquired_poor_gaze (8471716624377228327) --> - <skip /> - <!-- no translation found for face_acquired_not_detected (4885504661626728809) --> - <skip /> - <!-- no translation found for face_acquired_too_much_motion (3149332171102108851) --> - <skip /> + <string name="face_acquired_insufficient" msgid="2767330364802375742">"കൃത്യ മുഖ ഡാറ്റ എടുക്കാനായില്ല. വീണ്ടും ശ്രമിക്കൂ."</string> + <string name="face_acquired_too_bright" msgid="5005650874582450967">"വളരെയധികം തെളിച്ചം. സൗമ്യതയേറിയ പ്രകാശം ശ്രമിക്കൂ."</string> + <string name="face_acquired_too_dark" msgid="1966194696381394616">"വളരെ ഇരുണ്ടത്. തിളക്കമേറിയ ലൈറ്റിംഗ് പരീക്ഷിക്കുക."</string> + <string name="face_acquired_too_close" msgid="1401011882624272753">"ഫോൺ കൂടുതൽ അകലേയ്ക്ക് നീക്കുക."</string> + <string name="face_acquired_too_far" msgid="1210969240069012510">"ഫോൺ അടുത്തേക്ക് നീക്കുക."</string> + <string name="face_acquired_too_high" msgid="3362395713403348013">"ഫോൺ കൂടുതൽ ഉയരത്തിലേക്ക് നീക്കുക."</string> + <string name="face_acquired_too_low" msgid="488983581737550912">"ഫോൺ കൂടുതൽ താഴേക്ക് നീക്കുക."</string> + <string name="face_acquired_too_right" msgid="941726879175375970">"ഫോൺ വലത്തോട്ട് നീക്കുക."</string> + <string name="face_acquired_too_left" msgid="5873592047381190672">"ഫോൺ ഇടത്തോട്ട് നീക്കുക."</string> + <string name="face_acquired_poor_gaze" msgid="8471716624377228327">"തുറന്ന കണ്ണുകളുമായി സ്ക്രീനിലേക്ക് നോക്കുക."</string> + <string name="face_acquired_not_detected" msgid="4885504661626728809">"നിങ്ങളുടെ മുഖം കാണാനാവുന്നില്ല. ഫോണിലേക്ക് നോക്കൂ."</string> + <string name="face_acquired_too_much_motion" msgid="3149332171102108851">"വളരെയധികം ചലനം. ഫോൺ അനക്കാതെ നേരെ പിടിക്കുക."</string> <string name="face_acquired_recalibrate" msgid="8077949502893707539">"നിങ്ങളുടെ മുഖം വീണ്ടും എൻറോൾ ചെയ്യുക."</string> - <!-- no translation found for face_acquired_too_different (7663983770123789694) --> - <skip /> + <string name="face_acquired_too_different" msgid="7663983770123789694">"ഇനി മുഖം തിരിച്ചറിയാനാവില്ല. വീണ്ടും ശ്രമിക്കൂ."</string> <string name="face_acquired_too_similar" msgid="1508776858407646460">"വളരെയധികം സമാനത, നിങ്ങളുടെ പോസ് മാറ്റുക."</string> - <!-- no translation found for face_acquired_pan_too_extreme (1852495480382773759) --> - <skip /> - <!-- no translation found for face_acquired_tilt_too_extreme (1290820400317982049) --> - <skip /> + <string name="face_acquired_pan_too_extreme" msgid="1852495480382773759">"അൽപ്പം കൂടി സ്ക്രീനിന് നേരെ നോക്കുക."</string> + <string name="face_acquired_tilt_too_extreme" msgid="1290820400317982049">"അൽപ്പം കൂടി സ്ക്രീനിന് നേരെ നോക്കുക."</string> <string name="face_acquired_roll_too_extreme" msgid="1444829237745898619">"നിങ്ങളുടെ തല ലംബമായി നേരെയാക്കുക"</string> - <!-- no translation found for face_acquired_obscured (5747521031647744553) --> - <skip /> - <!-- no translation found for face_acquired_sensor_dirty (364493868630891300) --> - <skip /> + <string name="face_acquired_obscured" msgid="5747521031647744553">"തലയ്ക്കും ഫോണിനുമിടയിലുള്ള തടസ്സം നീക്കുക."</string> + <string name="face_acquired_sensor_dirty" msgid="364493868630891300">"ക്യാമറ വൃത്തിയാക്കുക."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="396883585636963908">"മുഖം പരിശോധിക്കാൻ കഴിയില്ല. ഹാർഡ്വെയർ ലഭ്യമല്ല."</string> - <!-- no translation found for face_error_timeout (2605673935810019129) --> - <skip /> + <string name="face_error_timeout" msgid="2605673935810019129">"മുഖത്തിന്റെ സമയപരിധി കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string> <string name="face_error_no_space" msgid="2712120617457553825">"പുതിയ മുഖ ഡാറ്റ സംഭരിക്കാനാകില്ല. ആദ്യം പഴയത് ഇല്ലാതാക്കുക."</string> <string name="face_error_canceled" msgid="2768146728600802422">"മുഖം തിരിച്ചറിയൽ പ്രവർത്തനം റദ്ദാക്കി"</string> <string name="face_error_user_canceled" msgid="9003022830076496163">"മുഖം പരിശോധിച്ചുറപ്പിക്കൽ ഉപയോക്താവ് റദ്ദാക്കി"</string> <string name="face_error_lockout" msgid="3407426963155388504">"നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string> <string name="face_error_lockout_permanent" msgid="3485837851962070925">"വളരെയധികം ശ്രമങ്ങൾ. മുഖം തിരിച്ചറിയൽ പ്രവർത്തനരഹിതമാക്കി."</string> - <!-- no translation found for face_error_unable_to_process (4940944939691171539) --> - <skip /> + <string name="face_error_unable_to_process" msgid="4940944939691171539">"മുഖം പരിശോധിക്കാൻ കഴിയില്ല. വീണ്ടും ശ്രമിക്കൂ."</string> <string name="face_error_not_enrolled" msgid="2600952202843125796">"നിങ്ങൾ മുഖം പരിശോധിച്ചുറപ്പിക്കൽ സജ്ജീകരിച്ചില്ല"</string> <string name="face_error_hw_not_present" msgid="1317845121210260372">"ഈ ഉപകരണം മുഖം പരിശോധിച്ചുറപ്പിക്കൽ പിന്തുണയ്ക്കുന്നില്ല"</string> <string name="face_name_template" msgid="7004562145809595384">"മുഖം <xliff:g id="FACEID">%d</xliff:g>"</string> @@ -1230,13 +1202,11 @@ <string name="new_app_action" msgid="6694851182870774403">"<xliff:g id="NEW_APP">%1$s</xliff:g> തുറക്കുക"</string> <string name="new_app_description" msgid="5894852887817332322">"<xliff:g id="OLD_APP">%1$s</xliff:g> സംരക്ഷിക്കാതെ അവസാനിപ്പിക്കും"</string> <string name="dump_heap_notification" msgid="2618183274836056542">"<xliff:g id="PROC">%1$s</xliff:g> മെമ്മറി പരിധി കവിഞ്ഞു"</string> - <!-- no translation found for dump_heap_ready_notification (1162196579925048701) --> - <skip /> + <string name="dump_heap_ready_notification" msgid="1162196579925048701">"<xliff:g id="PROC">%1$s</xliff:g> ഹീപ്പ് ഡംപ് തയ്യാറാണ്"</string> <string name="dump_heap_notification_detail" msgid="3993078784053054141">"ഹീപ്പ് ഡംപ് ശേഖരിച്ചു. പങ്കിടാൻ ടാപ്പ് ചെയ്യുക"</string> <string name="dump_heap_title" msgid="5864292264307651673">"ഹീപ്പ് ഡംപ് പങ്കിടണോ?"</string> - <!-- no translation found for dump_heap_text (8546022920319781701) --> - <skip /> - <string name="dump_heap_system_text" msgid="3236094872980706024">"<xliff:g id="PROC">%1$s</xliff:g> പ്രോസസ്സ് അതിൻ്റെ മെമ്മറി പരിധിയായ <xliff:g id="SIZE">%2$s</xliff:g> കവിഞ്ഞു. നിങ്ങൾക്ക് പങ്കിടാൻ ഒരു ഹീപ്പ് ഡംപ് ലഭ്യമാണ്. ശ്രദ്ധിക്കുക: പ്രോസസിന് ആക്സസ് ചെയ്യാനാകുന്ന, സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട ഏതെങ്കിലും വ്യക്തിഗത വിവരം ഈ ഹീപ്പ് ഡംപിൽ അടങ്ങിയിരിക്കാം, നിങ്ങൾ ടൈപ്പ് ചെയ്തിട്ടുള്ള കാര്യങ്ങൾ ഇതിൽ ഉൾപ്പെട്ടിരിക്കാം."</string> + <string name="dump_heap_text" msgid="8546022920319781701">"<xliff:g id="PROC">%1$s</xliff:g> പ്രോസസിന്, മെമ്മറി പരിധിയായ <xliff:g id="SIZE">%2$s</xliff:g> കവിഞ്ഞു. അതിന്റെ ഡവലപ്പറുമായി പങ്കിടാൻ ഒരു ഹീപ്പ് ഡംപ് നിങ്ങൾക്ക് ലഭ്യമാണ്. ശ്രദ്ധിക്കുക: ഈ ഹീപ്പ് ഡംപിൽ ആപ്പിന് ആക്സസുള്ള ഏതെങ്കിലും വ്യക്തിഗത വിവരം അടങ്ങിയിരിക്കാം."</string> + <string name="dump_heap_system_text" msgid="3236094872980706024">"<xliff:g id="PROC">%1$s</xliff:g> പ്രോസസ് അതിൻ്റെ മെമ്മറി പരിധിയായ <xliff:g id="SIZE">%2$s</xliff:g> കവിഞ്ഞു. നിങ്ങൾക്ക് പങ്കിടാൻ ഒരു ഹീപ്പ് ഡംപ് ലഭ്യമാണ്. ശ്രദ്ധിക്കുക: പ്രോസസിന് ആക്സസ് ചെയ്യാനാകുന്ന, സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട ഏതെങ്കിലും വ്യക്തിഗത വിവരം ഈ ഹീപ്പ് ഡംപിൽ അടങ്ങിയിരിക്കാം, നിങ്ങൾ ടൈപ്പ് ചെയ്തിട്ടുള്ള കാര്യങ്ങൾ ഇതിൽ ഉൾപ്പെട്ടിരിക്കാം."</string> <string name="dump_heap_ready_text" msgid="1778041771455343067">"നിങ്ങൾക്ക് പങ്കിടാൻ <xliff:g id="PROC">%1$s</xliff:g> എന്നതിൻ്റെ പ്രോസസിൻ്റെ ഒരു ഹീപ്പ് ഡംപ് ലഭ്യമാണ്. ശ്രദ്ധിക്കുക: പ്രോസസിന് ആക്സസ് ചെയ്യാനാകുന്ന, സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട ഏതെങ്കിലും വ്യക്തിഗത വിവരം ഈ ഹീപ്പ് ഡംപിൽ അടങ്ങിയിരിക്കാം, നിങ്ങൾ ടൈപ്പ് ചെയ്തിട്ടുള്ള കാര്യങ്ങൾ ഇതിൽ ഉൾപ്പെട്ടിരിക്കാം."</string> <string name="sendText" msgid="5209874571959469142">"വാചകസന്ദേശത്തിനായി ഒരു പ്രവർത്തനം തിരഞ്ഞെടുക്കുക"</string> <string name="volume_ringtone" msgid="6885421406845734650">"റിംഗർ വോളിയം"</string> @@ -1276,10 +1246,8 @@ <string name="wifi_available_content_failed_to_connect" msgid="3377406637062802645">"എല്ലാ നെറ്റ്വർക്കുകളും കാണാൻ ടാപ്പുചെയ്യുക"</string> <string name="wifi_available_action_connect" msgid="2635699628459488788">"കണക്റ്റുചെയ്യുക"</string> <string name="wifi_available_action_all_networks" msgid="4368435796357931006">"എല്ലാ നെറ്റ്വർക്കുകളും"</string> - <!-- no translation found for wifi_suggestion_title (9099832833531486167) --> - <skip /> - <!-- no translation found for wifi_suggestion_content (5883181205841582873) --> - <skip /> + <string name="wifi_suggestion_title" msgid="9099832833531486167">"വൈഫൈ നെറ്റ്വർക്കുകളിലേക്ക് കണക്റ്റ് ചെയ്യണോ?"</string> + <string name="wifi_suggestion_content" msgid="5883181205841582873">"<xliff:g id="NAME">%s</xliff:g> നിർദ്ദേശിച്ചത്"</string> <string name="wifi_suggestion_action_allow_app" msgid="3689946344485394085">"ഉവ്വ്"</string> <string name="wifi_suggestion_action_disallow_app" msgid="7977918905605931385">"ഇല്ല"</string> <string name="wifi_wakeup_onboarding_title" msgid="228772560195634292">"വൈഫൈ സ്വമേധയാ ഓണാകും"</string> @@ -1291,14 +1259,11 @@ <string name="network_available_sign_in" msgid="1848877297365446605">"നെറ്റ്വർക്കിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string> <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) --> <skip /> - <!-- no translation found for wifi_no_internet (5198100389964214865) --> - <skip /> + <string name="wifi_no_internet" msgid="5198100389964214865">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string> <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക"</string> <string name="captive_portal_logged_in_detailed" msgid="8489345381637456021">"കണക്റ്റ് ചെയ്തു"</string> - <!-- no translation found for network_partial_connectivity (7774883385494762741) --> - <skip /> - <!-- no translation found for network_partial_connectivity_detailed (1959697814165325217) --> - <skip /> + <string name="network_partial_connectivity" msgid="7774883385494762741">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> എന്നതിന് പരിമിതമായ കണക്റ്റിവിറ്റി ഉണ്ട്"</string> + <string name="network_partial_connectivity_detailed" msgid="1959697814165325217">"ഏതുവിധേനയും കണക്റ്റ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string> <string name="wifi_softap_config_change" msgid="8475911871165857607">"നിങ്ങളുടെ ഹോട്ട്സ്പോട്ട് ക്രമീകരണത്തിൽ വരുത്തിയ മാറ്റങ്ങൾ"</string> <string name="wifi_softap_config_change_summary" msgid="7601233252456548891">"നിങ്ങളുടെ ഹോട്ട്സ്പോട്ട് ബാൻഡ് മാറി."</string> <string name="wifi_softap_config_change_detailed" msgid="8022936822860678033">"നിങ്ങളുടെ മുൻഗണനയനുസരിച്ചുള്ള, 5GHz മാത്രം എന്നത് ഈ ഉപകരണം പിന്തുണയ്ക്കുന്നില്ല. പകരം, 5GHz ബാൻഡ് ലഭ്യമാകുമ്പോൾ അത് ഉപയോഗിക്കും."</string> @@ -1384,10 +1349,8 @@ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB ഡീബഗ്ഗിംഗ് കണക്റ്റ് ചെയ്തു"</string> <string name="adb_active_notification_message" msgid="7463062450474107752">"USB ഡീബഗ്ഗിംഗ് ഓഫാക്കാൻ ടാപ്പ് ചെയ്യുക"</string> <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ഡീബഗ്ഗുചെയ്യൽ പ്രവർത്തനരഹിതമാക്കാൻ തിരഞ്ഞെടുക്കുക."</string> - <!-- no translation found for test_harness_mode_notification_title (2216359742631914387) --> - <skip /> - <!-- no translation found for test_harness_mode_notification_message (1343197173054407119) --> - <skip /> + <string name="test_harness_mode_notification_title" msgid="2216359742631914387">"പരിശോധനാ സംവിധാനങ്ങൾ മോഡ് പ്രവർത്തനക്ഷമമാക്കി"</string> + <string name="test_harness_mode_notification_message" msgid="1343197173054407119">"പരിശോധനാ സംവിധാന മോഡ് പ്രവർത്തനരഹിതമാക്കാൻ ഫാക്ടറി പുനഃക്രമീകരണം നിർവഹിക്കുക."</string> <string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB പോർട്ടിൽ ദ്രാവകമോ പൊടിയോ കണ്ടെത്തി"</string> <string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB പോർട്ടർ സ്വയമേവ പ്രവർത്തനരഹിതമായി. കൂടുതലറിയാൻ ടാപ്പ് ചെയ്യുക."</string> <string name="usb_contaminant_not_detected_title" msgid="4202417484434906086">"USB പോർട്ട് ഇപ്പോൾ സുരക്ഷിതമായി ഉപയോഗിക്കാം"</string> @@ -1834,10 +1797,8 @@ <string name="package_updated_device_owner" msgid="1847154566357862089">"നിങ്ങളുടെ അഡ്മിൻ അപ്ഡേറ്റ് ചെയ്യുന്നത്"</string> <string name="package_deleted_device_owner" msgid="2307122077550236438">"നിങ്ങളുടെ അഡ്മിൻ ഇല്ലാതാക്കുന്നത്"</string> <string name="confirm_battery_saver" msgid="639106420541753635">"ശരി"</string> - <!-- no translation found for battery_saver_description_with_learn_more (2108984221113106294) --> - <skip /> - <!-- no translation found for battery_saver_description (6413346684861241431) --> - <skip /> + <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"ബാറ്ററി ലൈഫ് വികസിപ്പിക്കാൻ പശ്ചാത്തല ആക്റ്റിവിറ്റി, ചില വിഷ്വൽ ഇഫക്റ്റുകൾ, മറ്റ് ഹൈ പവർ ഫീച്ചറുകൾ എന്നിവയെ ബാറ്ററി ലാഭിക്കൽ ഓഫാക്കുകയോ നിയന്ത്രിക്കുകയോ ചെയ്യും. "<annotation id="url">"കൂടുതലറിയുക"</annotation></string> + <string name="battery_saver_description" msgid="6413346684861241431">"ബാറ്ററി ലൈഫ് വികസിപ്പിക്കാൻ പശ്ചാത്തല ആക്റ്റിവിറ്റി, ചില വിഷ്വൽ ഇഫക്റ്റുകൾ, മറ്റ് ഹൈ പവർ ഫീച്ചറുകൾ എന്നിവയെ ബാറ്ററി ലാഭിക്കൽ ഓഫാക്കുകയോ നിയന്ത്രിക്കുകയോ ചെയ്യും."</string> <string name="data_saver_description" msgid="6015391409098303235">"ഡാറ്റാ ഉപയോഗം കുറയ്ക്കാൻ സഹായിക്കുന്നതിന്, പശ്ചാത്തലത്തിൽ ഡാറ്റ അയയ്ക്കുകയോ സ്വീകരിക്കുകയോ ചെയ്യുന്നതിൽ നിന്ന് ചില ആപ്സിനെ ഡാറ്റ സേവർ തടയുന്നു. നിങ്ങൾ നിലവിൽ ഉപയോഗിക്കുന്ന ഒരു ആപ്പിന് ഡാറ്റ ആക്സസ്സ് ചെയ്യാൻ കഴിയും, എന്നാൽ കുറഞ്ഞ ആവൃത്തിയിലാണിത് നടക്കുക. ഇതിനർത്ഥം, ഉദാഹരണമായി നിങ്ങൾ ടാപ്പ് ചെയ്യുന്നത് വരെ ചിത്രങ്ങൾ പ്രദർശിപ്പിക്കുകയില്ല എന്നാണ്."</string> <string name="data_saver_enable_title" msgid="4674073932722787417">"ഡാറ്റ സേവർ ഓണാക്കണോ?"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"ഓണാക്കുക"</string> @@ -2032,22 +1993,14 @@ <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"ദിനചര്യ മോഡ് വിവരത്തെ കുറിച്ചുള്ള അറിയിപ്പ്"</string> <string name="dynamic_mode_notification_title" msgid="508815255807182035">"സാധാരണയുള്ളതിലും നേരത്തെ ബാറ്ററിയുടെ ചാർജ് തീർന്നേക്കാം"</string> <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"ബാറ്ററി ലൈഫ് വര്ദ്ധിപ്പിക്കാൻ, ബാറ്ററി ലാഭിക്കൽ സജീവമാക്കി"</string> - <!-- no translation found for battery_saver_notification_channel_name (2083316159716201806) --> - <skip /> - <!-- no translation found for battery_saver_sticky_disabled_notification_title (6376147579378764641) --> - <skip /> - <!-- no translation found for battery_saver_sticky_disabled_notification_summary (8090192609249817945) --> - <skip /> - <!-- no translation found for battery_saver_charged_notification_title (2960978289873161288) --> - <skip /> - <!-- no translation found for battery_saver_charged_notification_title (7555713825806482451) --> - <skip /> - <!-- no translation found for battery_saver_charged_notification_title (5954873381559605660) --> - <skip /> - <!-- no translation found for battery_saver_off_notification_summary (1374222493681267143) --> - <skip /> - <!-- no translation found for battery_saver_off_alternative_notification_summary (4340727818546508436) --> - <skip /> + <string name="battery_saver_notification_channel_name" msgid="2083316159716201806">"ബാറ്ററി ലാഭിക്കൽ"</string> + <string name="battery_saver_sticky_disabled_notification_title" msgid="6376147579378764641">"ബാറ്ററി ചാർജ് വീണ്ടും കുറയുന്നത് വരെ ബാറ്ററി ലാഭിക്കൽ പിന്നെയും സജീവമാവുകയില്ല"</string> + <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"മതിയായ നില വരെ ബാറ്ററി ചാർജായി. വീണ്ടും ബാറ്ററി ചാർജ് കുറയുന്നത് വരെ ബാറ്ററി ലാഭിക്കൽ പിന്നെയും സജീവമാവുകയില്ല."</string> + <string name="battery_saver_charged_notification_title" product="default" msgid="2960978289873161288">"ഫോൺ <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> ചാർജായി"</string> + <string name="battery_saver_charged_notification_title" product="tablet" msgid="7555713825806482451">"ടാബ്ലെറ്റ് <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> ചാർജായി"</string> + <string name="battery_saver_charged_notification_title" product="device" msgid="5954873381559605660">"ഉപകരണം <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> ചാർജായി"</string> + <string name="battery_saver_off_notification_summary" msgid="1374222493681267143">"ബാറ്ററി ലാഭിക്കൽ ഓഫാണ്. ഫീച്ചറുകൾക്ക് ഇനിമുതൽ നിയന്ത്രണമില്ല."</string> + <string name="battery_saver_off_alternative_notification_summary" msgid="4340727818546508436">"ബാറ്ററി ലാഭിക്കൽ ഓഫാക്കി. ഫീച്ചറുകൾക്ക് ഇനിമുതൽ നിയന്ത്രണമില്ല."</string> <string name="mime_type_folder" msgid="7111951698626315204">"ഫോള്ഡര്"</string> <string name="mime_type_apk" msgid="5518003630972506900">"Android ആപ്പ്"</string> <string name="mime_type_generic" msgid="6833871596845900027">"ഫയൽ"</string> @@ -2071,6 +2024,5 @@ <item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> ഫയലുകൾ</item> <item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> + <xliff:g id="COUNT_1">%d</xliff:g> ഫയൽ</item> </plurals> - <!-- no translation found for chooser_no_direct_share_targets (997970693708458895) --> - <skip /> + <string name="chooser_no_direct_share_targets" msgid="997970693708458895">"നേരിട്ടുള്ള പങ്കിടൽ ലഭ്യമല്ല"</string> </resources> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index c761d97776a7..dfbf57916809 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -519,8 +519,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"अॅपला तुमच्या फोटो संग्रहामध्ये सुधारणा करण्याची अनुमती देते."</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"तुमच्या मीडिया संग्रहातून स्थाने वाचा"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"अॅपला तुमच्या मीडिया संग्रहामध्येील स्थाने वाचण्यासाठी अनुमती देते."</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"हे तुम्हीच आहात याची पडताळणी करा"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"बायोमेट्रिक हार्डवेअर उपलब्ध नाही"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"ऑथेंटिकेशन रद्द केले"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"ओळखले नाही"</string> @@ -675,7 +674,7 @@ <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"स्टोअर केलेला अॅप डेटा एंक्रिप्ट केला जाणे आवश्यक आहे."</string> <string name="policylab_disableCamera" msgid="6395301023152297826">"कॅमेरे अक्षम करा"</string> <string name="policydesc_disableCamera" msgid="2306349042834754597">"सर्व डिव्हाइस कॅमेर्यांचा वापर प्रतिबंधित करा."</string> - <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"काही स्क्रीन लॉक वैशिष्ट्ये अक्षम करा"</string> + <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"काही स्क्रीन लॉक वैशिष्ट्ये बंद करा"</string> <string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"काही स्क्रीन लॉक वैशिष्ट्यांचा वापर प्रतिबंधित करा."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"घर"</item> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 67f85a7283f1..77620395f79d 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -519,8 +519,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"यसले अनुप्रयोगलाई तपाईंको तस्बिरको सङ्ग्रह परिमार्जन गर्न दिन्छ।"</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"आफ्नो मिडियाको सङ्ग्रहका स्थानहरू पढ्नुहोस्"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"यसले अनुप्रयोगलाई तपाईंको मिडिया सङ्ग्रहका स्थानहरू पढ्न दिन्छ।"</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"यो तपाईं नै हो भन्ने पुष्टि गर्नुहोस्"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"बायोमेट्रिक हार्डवेयर उपलब्ध छैन"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"प्रमाणीकरण रद्द गरियो"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"पहिचान भएन"</string> diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 6bbd2587d78d..37e452d72c9a 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -28,4 +28,6 @@ <!-- The background color of a notification card. --> <color name="notification_material_background_color">@color/black</color> -</resources>
\ No newline at end of file + + <color name="chooser_row_divider">@color/list_divider_color_dark</color> +</resources> diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml index e35b750272da..98f209d3f401 100644 --- a/core/res/res/values-night/themes_device_defaults.xml +++ b/core/res/res/values-night/themes_device_defaults.xml @@ -71,4 +71,8 @@ easier. <style name="ThemeOverlay.DeviceDefault.Accent.DayNight" parent="@style/ThemeOverlay.DeviceDefault.Accent" /> + <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon"> + <item name="windowLightNavigationBar">false</item> + </style> + </resources> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index b13fb8f56324..4a357b55439c 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1098,7 +1098,7 @@ <string name="view_calendar" msgid="979609872939597838">"Weergeven"</string> <string name="view_calendar_desc" msgid="5828320291870344584">"Geselecteerde tijd weergeven op de kalender"</string> <string name="add_calendar_event" msgid="1953664627192056206">"Plannen"</string> - <string name="add_calendar_event_desc" msgid="4326891793260687388">"Evenement plannen voor geselecteerde tijd"</string> + <string name="add_calendar_event_desc" msgid="4326891793260687388">"Afspraak plannen voor geselecteerde tijd"</string> <string name="view_flight" msgid="7691640491425680214">"Volgen"</string> <string name="view_flight_desc" msgid="3876322502674253506">"Geselecteerde vlucht volgen"</string> <string name="translate" msgid="9218619809342576858">"Vertalen"</string> @@ -1209,7 +1209,7 @@ <string name="dump_heap_system_text" msgid="3236094872980706024">"Het proces <xliff:g id="PROC">%1$s</xliff:g> overschrijdt de geheugenlimiet van <xliff:g id="SIZE">%2$s</xliff:g>. Er is een heap dump beschikbaar die je kunt delen. Let op: Deze heap dump kan gevoelige persoonlijke informatie bevatten waartoe het proces toegang heeft. Dit omvat mogelijk wat je hebt getypt."</string> <string name="dump_heap_ready_text" msgid="1778041771455343067">"Er is een heap dump beschikbaar van het proces van <xliff:g id="PROC">%1$s</xliff:g>. Deze kun je delen. Let op: Deze heap dump bevat mogelijk gevoelige persoonlijke informatie waartoe het proces toegang heeft. Dit omvat mogelijk wat je hebt getypt."</string> <string name="sendText" msgid="5209874571959469142">"Een actie voor tekst selecteren"</string> - <string name="volume_ringtone" msgid="6885421406845734650">"Belvolume"</string> + <string name="volume_ringtone" msgid="6885421406845734650">"Beltoonvolume"</string> <string name="volume_music" msgid="5421651157138628171">"Mediavolume"</string> <string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Afspelen via Bluetooth"</string> <string name="volume_music_hint_silent_ringtone_selected" msgid="8310739960973156272">"Stille beltoon ingesteld"</string> @@ -1220,7 +1220,7 @@ <string name="volume_unknown" msgid="1400219669770445902">"Volume"</string> <string name="volume_icon_description_bluetooth" msgid="6538894177255964340">"Bluetooth-volume"</string> <string name="volume_icon_description_ringer" msgid="3326003847006162496">"Beltoonvolume"</string> - <string name="volume_icon_description_incall" msgid="8890073218154543397">"Belvolume"</string> + <string name="volume_icon_description_incall" msgid="8890073218154543397">"Gespreksvolume"</string> <string name="volume_icon_description_media" msgid="4217311719665194215">"Mediavolume"</string> <string name="volume_icon_description_notification" msgid="7044986546477282274">"Meldingsvolume"</string> <string name="ringtone_default" msgid="3789758980357696936">"Standaardbeltoon"</string> @@ -1843,7 +1843,7 @@ <string name="zen_mode_downtime_feature_name" msgid="2626974636779860146">"Downtime"</string> <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Doordeweekse avond"</string> <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Weekend"</string> - <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evenement"</string> + <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Afspraken"</string> <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"Slapen"</string> <string name="muted_by" msgid="5942954724562097128">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dempt sommige geluiden"</string> <string name="system_error_wipe_data" msgid="6608165524785354962">"Er is een intern probleem met je apparaat. Het apparaat kan instabiel zijn totdat u het apparaat terugzet naar de fabrieksinstellingen."</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 4611d02f0f3f..bac464bf6ad1 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -519,8 +519,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਟੋ ਸੰਗ੍ਰਹਿ ਨੂੰ ਸੋਧਣ ਦਿੰਦੀ ਹੈ।"</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"ਤੁਹਾਡੇ ਮੀਡੀਆ ਸੰਗ੍ਰਹਿ ਦੇ ਟਿਕਾਣਿਆਂ ਨੂੰ ਪੜ੍ਹਨਾ"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਮੀਡੀਆ ਸੰਗ੍ਰਹਿ ਦੇ ਟਿਕਾਣਿਆਂ ਨੂੰ ਪੜ੍ਹਨ ਦਿੰਦੀ ਹੈ।"</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"ਪ੍ਰਮਾਣਿਤ ਕਰੋ ਕਿ ਇਹ ਤੁਸੀਂ ਹੀ ਹੋ"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"ਪ੍ਰਮਾਣੀਕਰਨ ਰੱਦ ਕੀਤਾ ਗਿਆ"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string> @@ -1206,7 +1205,7 @@ <string name="dump_heap_ready_notification" msgid="1162196579925048701">"<xliff:g id="PROC">%1$s</xliff:g> ਹੀਪ ਡੰਪ ਤਿਆਰ ਹੈ"</string> <string name="dump_heap_notification_detail" msgid="3993078784053054141">"ਹੀਪ ਡੰਪ ਨੂੰ ਇਕੱਤਰ ਕੀਤਾ ਗਿਆ। ਸਾਂਝਾ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="dump_heap_title" msgid="5864292264307651673">"ਕੀ ਹੀਪ ਡੰਪ ਸ਼ੇਅਰ ਕਰਨਾ ਹੈ?"</string> - <string name="dump_heap_text" msgid="8546022920319781701">"<xliff:g id="PROC">%1$s</xliff:g> ਪ੍ਰਕਿਰਿਆ ਇਸਦੀ ਮੈਮਰੀ ਸੀਮਾ <xliff:g id="SIZE">%2$s</xliff:g> ਤੋਂ ਵਧ ਗਈ ਹੈ। ਇਸਦੇ ਵਿਕਾਸਕਾਰ ਨਾਲ ਸਾਂਝਾ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਲਈ ਇੱਕ ਹੀਪ ਡੰਪ ਉਪਲਬਧ ਹੈ। ਸਾਵਧਾਨ ਰਹੋ: ਇਸ ਹੀਪ ਡੰਪ ਵਿੱਚ ਤੁਹਾਡੀ ਕੋਈ ਵੀ ਨਿੱਜੀ ਜਾਣਕਾਰੀ ਹੋ ਸਕਦੀ ਹੈ, ਜਿਸ \'ਤੇ ਐਪਲੀਕੇਸ਼ਨ ਦੀ ਪਹੁੰਚ ਹੈ।"</string> + <string name="dump_heap_text" msgid="8546022920319781701">"<xliff:g id="PROC">%1$s</xliff:g> ਪ੍ਰਕਿਰਿਆ ਇਸਦੀ ਮੈਮੋਰੀ ਸੀਮਾ <xliff:g id="SIZE">%2$s</xliff:g> ਤੋਂ ਵਧ ਗਈ ਹੈ। ਇਸਦੇ ਵਿਕਾਸਕਾਰ ਨਾਲ ਸਾਂਝਾ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਵਾਸਤੇ ਇੱਕ ਹੀਪ ਡੰਪ ਉਪਲਬਧ ਹੈ। ਸਾਵਧਾਨ ਰਹੋ: ਇਸ ਹੀਪ ਡੰਪ ਵਿੱਚ ਤੁਹਾਡੀ ਕੋਈ ਵੀ ਨਿੱਜੀ ਜਾਣਕਾਰੀ ਹੋ ਸਕਦੀ ਹੈ, ਜਿਸ \'ਤੇ ਐਪਲੀਕੇਸ਼ਨ ਦੀ ਪਹੁੰਚ ਹੈ।"</string> <string name="dump_heap_system_text" msgid="3236094872980706024">"<xliff:g id="PROC">%1$s</xliff:g> ਪ੍ਰਕਿਰਿਆ, ਇਸ ਦੀ ਮੈਮੋਰੀ ਸੀਮਾ <xliff:g id="SIZE">%2$s</xliff:g> ਤੋਂ ਵੱਧ ਗਈ ਹੈ। ਸਾਂਝਾ ਕਰਨ ਲਈ ਹੀਪ ਡੰਪ ਤੁਹਾਡੇ ਲਈ ਉਪਲਬਧ ਹੈ। ਸਾਵਧਾਨ ਰਹੋ: ਇਸ ਹੀਪ ਡੰਪ ਵਿੱਚ ਕੋਈ ਵੀ ਸੰਵੇਦਨਸ਼ੀਲ ਨਿੱਜੀ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੋ ਸਕਦੀ ਹੈ, ਜਿਸ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਦੀ ਪਹੁੰਚ ਹੈ, ਜਿਸ ਵਿੱਚ ਸ਼ਾਇਦ ਤੁਹਾਡੇ ਵੱਲੋਂ ਟਾਈਪ ਕੀਤੀਆਂ ਚੀਜ਼ਾਂ ਸ਼ਾਮਲ ਹੋਣ।"</string> <string name="dump_heap_ready_text" msgid="1778041771455343067">"ਸਾਂਝਾ ਕਰਨ ਲਈ <xliff:g id="PROC">%1$s</xliff:g> ਦੀ ਪ੍ਰਕਿਰਿਆ ਦਾ ਹੀਪ ਡੰਪ ਤੁਹਾਡੇ ਲਈ ਉਪਲਬਧ ਹੈ। ਸਾਵਧਾਨ ਰਹੋ: ਸ਼ਾਇਦ ਇਸ ਹੀਪ ਡੰਪ ਵਿੱਚ ਕੋਈ ਵੀ ਸੰਵੇਦਨਸ਼ੀਲ ਨਿੱਜੀ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੋਵੇ, ਜਿਸ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਦੀ ਪਹੁੰਚ ਹੈ, ਜਿਸ ਵਿੱਚ ਸ਼ਾਇਦ ਤੁਹਾਡੇ ਵੱਲੋਂ ਟਾਈਪ ਕੀਤੀਆਂ ਚੀਜ਼ਾਂ ਸ਼ਾਮਲ ਹੋਣ।"</string> <string name="sendText" msgid="5209874571959469142">"ਲਿਖਤ ਲਈ ਕੋਈ ਕਾਰਵਾਈ ਚੁਣੋ"</string> @@ -1350,7 +1349,7 @@ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB ਡੀਬਗਿੰਗ ਕਨੈਕਟ ਕੀਤੀ"</string> <string name="adb_active_notification_message" msgid="7463062450474107752">"USB ਡੀਬੱਗਿੰਗ ਬੰਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string> <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ਡੀਬੱਗਿੰਗ ਅਯੋਗ ਬਣਾਉਣ ਲਈ ਚੁਣੋ।"</string> - <string name="test_harness_mode_notification_title" msgid="2216359742631914387">"ਟੈਸਟ ਹਾਰਨੈੱਸ ਮੋਡ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string> + <string name="test_harness_mode_notification_title" msgid="2216359742631914387">"ਟੈਸਟ ਹਾਰਨੈੱਸ ਮੋਡ ਚਾਲੂ ਹੈ"</string> <string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ਟੈਸਟ ਹਾਰਨੈੱਸ ਮੋਡ ਬੰਦ ਕਰਨ ਲਈ ਫੈਕਟਰੀ ਰੀਸੈੱਟ ਕਰੋ।"</string> <string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB ਪੋਰਟ ਵਿੱਚ ਪਾਣੀ ਜਾਂ ਧੂੜ-ਮਿੱਟੀ"</string> <string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ਪੋਰਟ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਬੰਦ ਕੀਤਾ ਗਿਆ। ਹੋਰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 5bf6017842f9..f090ea6d61a7 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -301,7 +301,7 @@ <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"Permitir que o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse sua atividade física?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Câmera"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"tire fotos e grave vídeos"</string> - <string name="permgrouprequest_camera" msgid="1299833592069671756">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tire fotos e grave vídeos?"</string> + <string name="permgrouprequest_camera" msgid="1299833592069671756">"Permitir que o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tire fotos e grave vídeos?"</string> <string name="permgrouplab_calllog" msgid="8798646184930388160">"Registro de chamadas"</string> <string name="permgroupdesc_calllog" msgid="3006237336748283775">"ler e gravar o registro de chamadas telefônicas"</string> <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Permitir que o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse seu registro de chamadas telefônicas?"</string> @@ -1213,14 +1213,14 @@ <string name="volume_music" msgid="5421651157138628171">"Volume da mídia"</string> <string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Reproduzindo por meio de Bluetooth"</string> <string name="volume_music_hint_silent_ringtone_selected" msgid="8310739960973156272">"Toque silencioso definido"</string> - <string name="volume_call" msgid="3941680041282788711">"Volume na chamada"</string> - <string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume de chamada Bluetooth"</string> + <string name="volume_call" msgid="3941680041282788711">"Volume das chamadas"</string> + <string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume das chamadas por Bluetooth"</string> <string name="volume_alarm" msgid="1985191616042689100">"Volume do alarme"</string> <string name="volume_notification" msgid="2422265656744276715">"Volume da notificação"</string> <string name="volume_unknown" msgid="1400219669770445902">"Volume"</string> <string name="volume_icon_description_bluetooth" msgid="6538894177255964340">"Volume de Bluetooth"</string> <string name="volume_icon_description_ringer" msgid="3326003847006162496">"Volume do toque"</string> - <string name="volume_icon_description_incall" msgid="8890073218154543397">"Volume de chamadas"</string> + <string name="volume_icon_description_incall" msgid="8890073218154543397">"Volume das chamadas"</string> <string name="volume_icon_description_media" msgid="4217311719665194215">"Volume da mídia"</string> <string name="volume_icon_description_notification" msgid="7044986546477282274">"Volume da notificação"</string> <string name="ringtone_default" msgid="3789758980357696936">"Toque padrão"</string> @@ -1361,7 +1361,7 @@ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTILHAR"</string> <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECUSAR"</string> <string name="select_input_method" msgid="4653387336791222978">"Selecione o método de entrada"</string> - <string name="show_ime" msgid="2506087537466597099">"Manter na tela enquanto o teclado físico estiver ativo"</string> + <string name="show_ime" msgid="2506087537466597099">"Mantém o teclado virtual na tela enquanto o teclado físico está ativo"</string> <string name="hardware" msgid="194658061510127999">"Mostrar teclado virtual"</string> <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configurar teclado físico"</string> <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Toque para selecionar o idioma e o layout"</string> @@ -1796,8 +1796,8 @@ <string name="package_updated_device_owner" msgid="1847154566357862089">"Atualizado pelo seu administrador"</string> <string name="package_deleted_device_owner" msgid="2307122077550236438">"Excluído pelo seu administrador"</string> <string name="confirm_battery_saver" msgid="639106420541753635">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"A \"Economia de bateria\" desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos que consomem muita energia, a fim de prolongar a duração da bateria. "<annotation id="url">"Saiba mais"</annotation></string> - <string name="battery_saver_description" msgid="6413346684861241431">"A \"Economia de bateria\" desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos que consomem muita energia, a fim de prolongar a duração da bateria."</string> + <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"A Economia de bateria desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos que consomem muita energia, a fim de prolongar a duração da bateria. "<annotation id="url">"Saiba mais"</annotation></string> + <string name="battery_saver_description" msgid="6413346684861241431">"A Economia de bateria desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos que consomem muita energia, a fim de prolongar a duração da bateria."</string> <string name="data_saver_description" msgid="6015391409098303235">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você esteja usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não sejam exibidas até que você toque nelas."</string> <string name="data_saver_enable_title" msgid="4674073932722787417">"Ativar Economia de dados?"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"Ativar"</string> @@ -1991,15 +1991,15 @@ <string name="notification_appops_overlay_active" msgid="633813008357934729">"exibindo sobre outros apps na sua tela"</string> <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notificação de informação do modo rotina"</string> <string name="dynamic_mode_notification_title" msgid="508815255807182035">"A bateria pode acabar antes da recarga normal"</string> - <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"A \"Economia de bateria\" foi ativada para aumentar a duração da carga"</string> + <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"A Economia de bateria foi ativada para aumentar a duração da carga"</string> <string name="battery_saver_notification_channel_name" msgid="2083316159716201806">"Economia de bateria"</string> - <string name="battery_saver_sticky_disabled_notification_title" msgid="6376147579378764641">"A \"Economia de bateria\" só será reativada quando a bateria estiver acabando novamente"</string> - <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"A bateria foi carregada até o nível suficiente. A \"Economia de bateria\" só será reativada quando a bateria estiver acabando novamente."</string> + <string name="battery_saver_sticky_disabled_notification_title" msgid="6376147579378764641">"A Economia de bateria só será reativada quando a bateria estiver acabando novamente"</string> + <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"A bateria foi carregada até o nível suficiente. A Economia de bateria só será reativada quando a bateria estiver acabando novamente."</string> <string name="battery_saver_charged_notification_title" product="default" msgid="2960978289873161288">"Smartphone <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> carregado"</string> <string name="battery_saver_charged_notification_title" product="tablet" msgid="7555713825806482451">"Tablet <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> carregado"</string> <string name="battery_saver_charged_notification_title" product="device" msgid="5954873381559605660">"Dispositivo <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> carregado"</string> - <string name="battery_saver_off_notification_summary" msgid="1374222493681267143">"\"Economia de bateria\" desativada. Os recursos não estão mais restritos."</string> - <string name="battery_saver_off_alternative_notification_summary" msgid="4340727818546508436">"\"Economia de bateria\" desativada. Os recursos não estão mais restritos."</string> + <string name="battery_saver_off_notification_summary" msgid="1374222493681267143">"Economia de bateria desativada. Os recursos não estão mais restritos."</string> + <string name="battery_saver_off_alternative_notification_summary" msgid="4340727818546508436">"Economia de bateria desativada. Os recursos não estão mais restritos."</string> <string name="mime_type_folder" msgid="7111951698626315204">"Pasta"</string> <string name="mime_type_apk" msgid="5518003630972506900">"Aplicativo Android"</string> <string name="mime_type_generic" msgid="6833871596845900027">"Arquivo"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 5bf6017842f9..f090ea6d61a7 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -301,7 +301,7 @@ <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"Permitir que o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse sua atividade física?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Câmera"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"tire fotos e grave vídeos"</string> - <string name="permgrouprequest_camera" msgid="1299833592069671756">"Permitir que <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tire fotos e grave vídeos?"</string> + <string name="permgrouprequest_camera" msgid="1299833592069671756">"Permitir que o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> tire fotos e grave vídeos?"</string> <string name="permgrouplab_calllog" msgid="8798646184930388160">"Registro de chamadas"</string> <string name="permgroupdesc_calllog" msgid="3006237336748283775">"ler e gravar o registro de chamadas telefônicas"</string> <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Permitir que o app <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> acesse seu registro de chamadas telefônicas?"</string> @@ -1213,14 +1213,14 @@ <string name="volume_music" msgid="5421651157138628171">"Volume da mídia"</string> <string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Reproduzindo por meio de Bluetooth"</string> <string name="volume_music_hint_silent_ringtone_selected" msgid="8310739960973156272">"Toque silencioso definido"</string> - <string name="volume_call" msgid="3941680041282788711">"Volume na chamada"</string> - <string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume de chamada Bluetooth"</string> + <string name="volume_call" msgid="3941680041282788711">"Volume das chamadas"</string> + <string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume das chamadas por Bluetooth"</string> <string name="volume_alarm" msgid="1985191616042689100">"Volume do alarme"</string> <string name="volume_notification" msgid="2422265656744276715">"Volume da notificação"</string> <string name="volume_unknown" msgid="1400219669770445902">"Volume"</string> <string name="volume_icon_description_bluetooth" msgid="6538894177255964340">"Volume de Bluetooth"</string> <string name="volume_icon_description_ringer" msgid="3326003847006162496">"Volume do toque"</string> - <string name="volume_icon_description_incall" msgid="8890073218154543397">"Volume de chamadas"</string> + <string name="volume_icon_description_incall" msgid="8890073218154543397">"Volume das chamadas"</string> <string name="volume_icon_description_media" msgid="4217311719665194215">"Volume da mídia"</string> <string name="volume_icon_description_notification" msgid="7044986546477282274">"Volume da notificação"</string> <string name="ringtone_default" msgid="3789758980357696936">"Toque padrão"</string> @@ -1361,7 +1361,7 @@ <string name="share_remote_bugreport_action" msgid="6249476773913384948">"COMPARTILHAR"</string> <string name="decline_remote_bugreport_action" msgid="6230987241608770062">"RECUSAR"</string> <string name="select_input_method" msgid="4653387336791222978">"Selecione o método de entrada"</string> - <string name="show_ime" msgid="2506087537466597099">"Manter na tela enquanto o teclado físico estiver ativo"</string> + <string name="show_ime" msgid="2506087537466597099">"Mantém o teclado virtual na tela enquanto o teclado físico está ativo"</string> <string name="hardware" msgid="194658061510127999">"Mostrar teclado virtual"</string> <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Configurar teclado físico"</string> <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Toque para selecionar o idioma e o layout"</string> @@ -1796,8 +1796,8 @@ <string name="package_updated_device_owner" msgid="1847154566357862089">"Atualizado pelo seu administrador"</string> <string name="package_deleted_device_owner" msgid="2307122077550236438">"Excluído pelo seu administrador"</string> <string name="confirm_battery_saver" msgid="639106420541753635">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"A \"Economia de bateria\" desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos que consomem muita energia, a fim de prolongar a duração da bateria. "<annotation id="url">"Saiba mais"</annotation></string> - <string name="battery_saver_description" msgid="6413346684861241431">"A \"Economia de bateria\" desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos que consomem muita energia, a fim de prolongar a duração da bateria."</string> + <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"A Economia de bateria desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos que consomem muita energia, a fim de prolongar a duração da bateria. "<annotation id="url">"Saiba mais"</annotation></string> + <string name="battery_saver_description" msgid="6413346684861241431">"A Economia de bateria desativa ou restringe atividades em segundo plano, alguns efeitos visuais e outros recursos que consomem muita energia, a fim de prolongar a duração da bateria."</string> <string name="data_saver_description" msgid="6015391409098303235">"Para ajudar a reduzir o uso de dados, a Economia de dados impede que alguns apps enviem ou recebam dados em segundo plano. Um app que você esteja usando no momento pode acessar dados, mas com menos frequência. Isso pode fazer com que imagens não sejam exibidas até que você toque nelas."</string> <string name="data_saver_enable_title" msgid="4674073932722787417">"Ativar Economia de dados?"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"Ativar"</string> @@ -1991,15 +1991,15 @@ <string name="notification_appops_overlay_active" msgid="633813008357934729">"exibindo sobre outros apps na sua tela"</string> <string name="dynamic_mode_notification_channel_name" msgid="2348803891571320452">"Notificação de informação do modo rotina"</string> <string name="dynamic_mode_notification_title" msgid="508815255807182035">"A bateria pode acabar antes da recarga normal"</string> - <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"A \"Economia de bateria\" foi ativada para aumentar a duração da carga"</string> + <string name="dynamic_mode_notification_summary" msgid="2541166298550402690">"A Economia de bateria foi ativada para aumentar a duração da carga"</string> <string name="battery_saver_notification_channel_name" msgid="2083316159716201806">"Economia de bateria"</string> - <string name="battery_saver_sticky_disabled_notification_title" msgid="6376147579378764641">"A \"Economia de bateria\" só será reativada quando a bateria estiver acabando novamente"</string> - <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"A bateria foi carregada até o nível suficiente. A \"Economia de bateria\" só será reativada quando a bateria estiver acabando novamente."</string> + <string name="battery_saver_sticky_disabled_notification_title" msgid="6376147579378764641">"A Economia de bateria só será reativada quando a bateria estiver acabando novamente"</string> + <string name="battery_saver_sticky_disabled_notification_summary" msgid="8090192609249817945">"A bateria foi carregada até o nível suficiente. A Economia de bateria só será reativada quando a bateria estiver acabando novamente."</string> <string name="battery_saver_charged_notification_title" product="default" msgid="2960978289873161288">"Smartphone <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> carregado"</string> <string name="battery_saver_charged_notification_title" product="tablet" msgid="7555713825806482451">"Tablet <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> carregado"</string> <string name="battery_saver_charged_notification_title" product="device" msgid="5954873381559605660">"Dispositivo <xliff:g id="CHARGE_LEVEL">%1$s</xliff:g> carregado"</string> - <string name="battery_saver_off_notification_summary" msgid="1374222493681267143">"\"Economia de bateria\" desativada. Os recursos não estão mais restritos."</string> - <string name="battery_saver_off_alternative_notification_summary" msgid="4340727818546508436">"\"Economia de bateria\" desativada. Os recursos não estão mais restritos."</string> + <string name="battery_saver_off_notification_summary" msgid="1374222493681267143">"Economia de bateria desativada. Os recursos não estão mais restritos."</string> + <string name="battery_saver_off_alternative_notification_summary" msgid="4340727818546508436">"Economia de bateria desativada. Os recursos não estão mais restritos."</string> <string name="mime_type_folder" msgid="7111951698626315204">"Pasta"</string> <string name="mime_type_apk" msgid="5518003630972506900">"Aplicativo Android"</string> <string name="mime_type_generic" msgid="6833871596845900027">"Arquivo"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 2683109bd960..3a942b6d18f8 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -307,7 +307,7 @@ <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"Открыть приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к данным о физической активности?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"снимать фото и видео"</string> - <string name="permgrouprequest_camera" msgid="1299833592069671756">"Разрешить приложению <b>\"<xliff:g id="APP_NAME">%1$s</xliff:g>\"</b> снимать фото и видео?"</string> + <string name="permgrouprequest_camera" msgid="1299833592069671756">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> снимать фото и видео?"</string> <string name="permgrouplab_calllog" msgid="8798646184930388160">"Список вызовов"</string> <string name="permgroupdesc_calllog" msgid="3006237336748283775">"чтение и запись телефонных звонков"</string> <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Разрешить приложению <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> доступ к списку вызовов?"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 8a91f2409452..08a9fbac5005 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -407,7 +407,7 @@ <string name="permdesc_readCalendar" product="tablet" msgid="4993979255403945892">"Ky aplikacion mund të lexojë të gjitha ngjarjet e kalendarit të ruajtura në tabletin tënd dhe të ndajë ose të ruajë të dhënat e kalendarit."</string> <string name="permdesc_readCalendar" product="tv" msgid="8837931557573064315">"Ky aplikacion mund të lexojë të gjitha ngjarjet e kalendarit të ruajtura në televizorin tënd dhe të ndajë ose të ruajë të dhënat e kalendarit."</string> <string name="permdesc_readCalendar" product="default" msgid="4373978642145196715">"Ky aplikacion mund të lexojë të gjitha ngjarjet e kalendarit të ruajtura në telefonin tënd dhe të ndajë ose të ruajë të dhënat e kalendarit."</string> - <string name="permlab_writeCalendar" msgid="8438874755193825647">"shto ose modifiko ngjarjet e kalendarit dhe dërgoju mail të ftuarve, pa dijeninë e zotëruesve"</string> + <string name="permlab_writeCalendar" msgid="8438874755193825647">"shto ose modifiko ngjarjet e kalendarit dhe dërgoju email të ftuarve pa dijeninë e zotëruesve"</string> <string name="permdesc_writeCalendar" product="tablet" msgid="1675270619903625982">"Ky aplikacion mund të shtojë, të heqë ose të ndryshojë ngjarjet e kalendarit në tabletin tënd. Ky aplikacion mund të dërgojë mesazhe që mund të duket se vijnë nga zotëruesit e kalendarit ose të ndryshojë ngjarjet pa i njoftuar zotëruesit e tyre."</string> <string name="permdesc_writeCalendar" product="tv" msgid="9017809326268135866">"Ky aplikacion mund të shtojë, të heqë ose të ndryshojë ngjarjet e kalendarit në televizorin tënd. Ky aplikacion mund të dërgojë mesazhe që mund të duket se vijnë nga zotëruesit e kalendarit ose të ndryshojë ngjarjet pa i njoftuar zotëruesit e tyre."</string> <string name="permdesc_writeCalendar" product="default" msgid="7592791790516943173">"Ky aplikacion mund të shtojë, të heqë ose të ndryshojë ngjarjet e kalendarit në telefonin tënd. Ky aplikacion mund të dërgojë mesazhe që mund të duket se vijnë nga zotëruesit e kalendarit ose të ndryshojë ngjarjet pa i njoftuar zotëruesit e tyre."</string> @@ -1153,7 +1153,7 @@ <string name="aerr_application_repeated" msgid="3146328699537439573">"<xliff:g id="APPLICATION">%1$s</xliff:g> vazhdon të ndalojë"</string> <string name="aerr_process_repeated" msgid="6235302956890402259">"<xliff:g id="PROCESS">%1$s</xliff:g> vazhdon të ndalojë"</string> <string name="aerr_restart" msgid="7581308074153624475">"Hap përsëri aplikacionin"</string> - <string name="aerr_report" msgid="5371800241488400617">"Dërgo komentin"</string> + <string name="aerr_report" msgid="5371800241488400617">"Dërgo koment"</string> <string name="aerr_close" msgid="2991640326563991340">"Mbyll"</string> <string name="aerr_mute" msgid="1974781923723235953">"Vendose në heshtje deri kur të riniset pajisja"</string> <string name="aerr_wait" msgid="3199956902437040261">"Prit!"</string> @@ -1924,7 +1924,7 @@ <string name="time_picker_minute_label" msgid="5168864173796598399">"minutë"</string> <string name="time_picker_header_text" msgid="143536825321922567">"Vendos orën"</string> <string name="time_picker_input_error" msgid="7574999942502513765">"Fut një kohë të vlefshme"</string> - <string name="time_picker_prompt_label" msgid="7588093983899966783">"Shkruaj kohën"</string> + <string name="time_picker_prompt_label" msgid="7588093983899966783">"Shkruaj orën"</string> <string name="time_picker_text_input_mode_description" msgid="4148166758173708199">"Kalo te modaliteti i hyrjes së tekstit për hyrjen e kohës."</string> <string name="time_picker_radial_mode_description" msgid="4953403779779557198">"Kalo te modaliteti i orës për hyrjen e kohës."</string> <string name="autofill_picker_accessibility_title" msgid="8469043291648711535">"Opsionet e plotësimit automatik"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 8ab8b8ff6b10..006d6ad44bba 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -302,12 +302,9 @@ <string name="permgrouplab_microphone" msgid="171539900250043464">"மைக்ரோஃபோன்"</string> <string name="permgroupdesc_microphone" msgid="4988812113943554584">"ஒலிப் பதிவு செய்யலாம்"</string> <string name="permgrouprequest_microphone" msgid="9167492350681916038">"ஆடியோவைப் பதிவு செய்ய <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> பயன்பாட்டை அனுமதிக்கவா?"</string> - <!-- no translation found for permgrouplab_activityRecognition (1565108047054378642) --> - <skip /> - <!-- no translation found for permgroupdesc_activityRecognition (6949472038320473478) --> - <skip /> - <!-- no translation found for permgrouprequest_activityRecognition (7626438016904799383) --> - <skip /> + <string name="permgrouplab_activityRecognition" msgid="1565108047054378642">"உடல் செயல்பாடுகள்"</string> + <string name="permgroupdesc_activityRecognition" msgid="6949472038320473478">"உடல் செயல்பாட்டைக் கண்காணிக்கும்"</string> + <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"உடல் செயல்பாட்டைக் கண்காணிக்க <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸை அனுமதிக்கவா?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"கேமரா"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"படங்கள் மற்றும் வீடியோக்கள் எடுக்கலாம்"</string> <string name="permgrouprequest_camera" msgid="1299833592069671756">"படங்கள் எடுக்கவும், வீடியோ பதிவு செய்யவும் <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ஆப்ஸை அனுமதிக்கவா?"</string> @@ -530,8 +527,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"உங்களின் படத் தொகுப்பை மாற்ற ஆப்ஸை அனுமதிக்கும்."</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"மீடியா தொகுப்பிலிருந்து இடங்களை அறிதல்"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"உங்களின் மீடியா தொகுப்பிலிருந்து இடங்களை அறிந்துகொள்ள ஆப்ஸை அனுமதிக்கும்."</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"நீங்கள்தான் என உறுதிசெய்க"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"பயோமெட்ரிக் வன்பொருள் இல்லை"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"அங்கீகரிப்பு ரத்தானது"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"அடையாளங்காணபடவில்லை"</string> @@ -604,25 +600,18 @@ <skip /> <string-array name="face_acquired_vendor"> </string-array> - <!-- no translation found for face_error_hw_not_available (396883585636963908) --> - <skip /> + <string name="face_error_hw_not_available" msgid="396883585636963908">"முகத்தைச் சரிபார்க்க இயலவில்லை. வன்பொருள் இல்லை."</string> <!-- no translation found for face_error_timeout (2605673935810019129) --> <skip /> - <!-- no translation found for face_error_no_space (2712120617457553825) --> - <skip /> - <!-- no translation found for face_error_canceled (2768146728600802422) --> - <skip /> - <!-- no translation found for face_error_user_canceled (9003022830076496163) --> - <skip /> + <string name="face_error_no_space" msgid="2712120617457553825">"புதிய முகங்களைச் சேர்க்க இயலவில்லை. பழையது ஒன்றை நீக்கவும்."</string> + <string name="face_error_canceled" msgid="2768146728600802422">"முக அங்கீகாரச் செயல்பாடு ரத்துசெய்யப்பட்டது"</string> + <string name="face_error_user_canceled" msgid="9003022830076496163">"முக அங்கீகாரம் பயனரால் ரத்துசெய்யப்பட்டது"</string> <string name="face_error_lockout" msgid="3407426963155388504">"பலமுறை முயன்றுவிட்டீர்கள். பிறகு முயலவும்."</string> - <!-- no translation found for face_error_lockout_permanent (3485837851962070925) --> - <skip /> + <string name="face_error_lockout_permanent" msgid="3485837851962070925">"பலமுறை முயன்றுவிட்டீர்கள். முக அங்கீகாரம் முடக்கப்பட்டது."</string> <!-- no translation found for face_error_unable_to_process (4940944939691171539) --> <skip /> - <!-- no translation found for face_error_not_enrolled (2600952202843125796) --> - <skip /> - <!-- no translation found for face_error_hw_not_present (1317845121210260372) --> - <skip /> + <string name="face_error_not_enrolled" msgid="2600952202843125796">"முக அங்கீகாரத்தை இன்னும் நீங்கள் அமைக்கவில்லை"</string> + <string name="face_error_hw_not_present" msgid="1317845121210260372">"இந்தச் சாதனத்தில் முக அங்கீகாரம் ஆதரிக்கப்படவில்லை"</string> <string name="face_name_template" msgid="7004562145809595384">"முகம் <xliff:g id="FACEID">%d</xliff:g>"</string> <string-array name="face_error_vendor"> </string-array> @@ -1246,10 +1235,8 @@ <string name="dump_heap_title" msgid="5864292264307651673">"ஹீப் டம்பைப் பகிரவா?"</string> <!-- no translation found for dump_heap_text (8546022920319781701) --> <skip /> - <!-- no translation found for dump_heap_system_text (3236094872980706024) --> - <skip /> - <!-- no translation found for dump_heap_ready_text (1778041771455343067) --> - <skip /> + <string name="dump_heap_system_text" msgid="3236094872980706024">"<xliff:g id="SIZE">%2$s</xliff:g> அளவான தனது நினைவக வரம்பை <xliff:g id="PROC">%1$s</xliff:g> செயலாக்கம் மீறியுள்ளது. உங்களுக்கான ஹீப் டம்ப் பகிர்வதற்குக் கிடைக்கிறது. கவனத்திற்கு: நீங்கள் உள்ளிட்டவை உட்பட செயலாக்கத்திற்கு அணுகலுள்ள தனிப்பட்ட தகவல்கள் இந்த ஹீப் டம்பில் இருக்கக்கூடும்"</string> + <string name="dump_heap_ready_text" msgid="1778041771455343067">"<xliff:g id="PROC">%1$s</xliff:g> செயலாக்கத்திற்கான ஹீப் டம்ப் நீங்கள் பகிர்வதற்குக் கிடைக்கிறது. கவனத்திற்கு: நீங்கள் உள்ளிட்டவை உட்பட செயலாக்கத்திற்கு அணுகலுள்ள தனிப்பட்ட தகவல்கள் இந்த ஹீப் டம்பில் இருக்கக்கூடும்."</string> <string name="sendText" msgid="5209874571959469142">"உரைக்கான செயலைத் தேர்வுசெய்யவும்"</string> <string name="volume_ringtone" msgid="6885421406845734650">"ரிங்கரின் ஒலியளவு"</string> <string name="volume_music" msgid="5421651157138628171">"மீடியாவின் ஒலியளவு"</string> @@ -1656,10 +1643,8 @@ <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"மேலோட்ட #<xliff:g id="ID">%1$d</xliff:g>"</string> <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string> <string name="display_manager_overlay_display_secure_suffix" msgid="6022119702628572080">", பாதுகாப்பானது"</string> - <!-- no translation found for activity_starter_block_bg_activity_starts_permissive (6995473033438879646) --> - <skip /> - <!-- no translation found for activity_starter_block_bg_activity_starts_enforcing (3317816771072146229) --> - <skip /> + <string name="activity_starter_block_bg_activity_starts_permissive" msgid="6995473033438879646">"<xliff:g id="PACKAGENAME">%1$s</xliff:g>மின் \'பின்னணிச் செயல்பாடுத் தொடக்கம்\' இனிவரும் Q பதிப்புகளில் தடுக்கப்படும். g.co/dev/bgblock என்ற இணைப்பைப் பார்க்கவும்."</string> + <string name="activity_starter_block_bg_activity_starts_enforcing" msgid="3317816771072146229">"<xliff:g id="PACKAGENAME">%1$s</xliff:g>மில் \'பின்னணிச் செயல்பாட்டுத் தொடக்கம்\' தடுக்கப்பட்டுள்ளது. g.co/dev/bgblock என்ற இணைப்பைப் பார்க்கவும்."</string> <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"வடிவத்தை மறந்துவிட்டீர்களா"</string> <string name="kg_wrong_pattern" msgid="1850806070801358830">"தவறான வடிவம்"</string> <string name="kg_wrong_password" msgid="2333281762128113157">"தவறான கடவுச்சொல்"</string> @@ -1965,7 +1950,7 @@ <string name="conference_call" msgid="3751093130790472426">"குழு அழைப்பு"</string> <string name="tooltip_popup_title" msgid="5253721848739260181">"உதவிக்குறிப்பு"</string> <string name="app_category_game" msgid="5431836943981492993">"கேம்ஸ்"</string> - <string name="app_category_audio" msgid="1659853108734301647">"இசையும் ஆடியோவும்"</string> + <string name="app_category_audio" msgid="1659853108734301647">"இசை & ஆடியோ"</string> <string name="app_category_video" msgid="2728726078629384196">"திரைப்படங்களும் வீடியோவும்"</string> <string name="app_category_image" msgid="4867854544519846048">"புகைப்படங்களும் படங்களும்"</string> <string name="app_category_social" msgid="5842783057834965912">"சமூகமும் தகவல்தொடர்பும்"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 9904cfa1f586..1662be72e28b 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -519,8 +519,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"మీ ఫోటో సేకరణను సవరించడానికి యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవండి"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవడానికి యాప్ను అనుమతిస్తుంది."</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"ఇది మీరేనని ధృవీకరించండి"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"బయోమెట్రిక్ హార్డ్వేర్ అందుబాటులో లేదు"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"ప్రమాణీకరణ రద్దు చేయబడింది"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"గుర్తించలేదు"</string> @@ -572,7 +571,7 @@ <string name="face_acquired_pan_too_extreme" msgid="1852495480382773759">"దయచేసి స్క్రీన్ వైపు మరింత సూటిగా చూడండి."</string> <string name="face_acquired_tilt_too_extreme" msgid="1290820400317982049">"దయచేసి స్క్రీన్ వైపు మరింత సూటిగా చూడండి."</string> <string name="face_acquired_roll_too_extreme" msgid="1444829237745898619">"దయచేసి మీ తలను నిలువుగా, నిటారుగా ఉంచండి."</string> - <string name="face_acquired_obscured" msgid="5747521031647744553">"మీ తలకు, ఫోన్కు మధ్యన ఖాళీ తగ్గించండి."</string> + <string name="face_acquired_obscured" msgid="5747521031647744553">"మీ తలకు, ఫోన్కు మధ్యన ఏదీ లేదని నిర్దారించుకోండి."</string> <string name="face_acquired_sensor_dirty" msgid="364493868630891300">"దయచేసి కెమెరాను శుభ్రం చేయండి."</string> <string-array name="face_acquired_vendor"> </string-array> @@ -908,7 +907,7 @@ <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nమీరు ఖచ్చితంగా ఈ పేజీ నుండి వెలుపలకు నావిగేట్ చేయాలనుకుంటున్నారా?"</string> <string name="save_password_label" msgid="6860261758665825069">"నిర్ధారించండి"</string> <string name="double_tap_toast" msgid="4595046515400268881">"చిట్కా: దగ్గరకు మరియు దూరానికి జూమ్ చేయడానికి రెండు సార్లు నొక్కండి."</string> - <string name="autofill_this_form" msgid="4616758841157816676">"స్వీయ పూరింపు"</string> + <string name="autofill_this_form" msgid="4616758841157816676">"ఆటోఫిల్"</string> <string name="setup_autofill" msgid="7103495070180590814">"స్వీయ పూరణను సెటప్ చేయండి"</string> <string name="autofill_window_title" msgid="4107745526909284887">"<xliff:g id="SERVICENAME">%1$s</xliff:g> ద్వారా స్వీయ పూరింపు చేయండి"</string> <string name="autofill_address_name_separator" msgid="6350145154779706772">" "</string> @@ -1078,7 +1077,7 @@ <string name="selectTextMode" msgid="1018691815143165326">"వచనాన్ని ఎంచుకోండి"</string> <string name="undo" msgid="7905788502491742328">"చర్య రద్దు చేయి"</string> <string name="redo" msgid="7759464876566803888">"చర్యను పునరావృతం చేయి"</string> - <string name="autofill" msgid="3035779615680565188">"స్వీయ పూరింపు"</string> + <string name="autofill" msgid="3035779615680565188">"ఆటోఫిల్"</string> <string name="textSelectionCABTitle" msgid="5236850394370820357">"వచన ఎంపిక"</string> <string name="addToDictionary" msgid="4352161534510057874">"నిఘంటువుకు జోడించు"</string> <string name="deleteText" msgid="6979668428458199034">"తొలగించు"</string> @@ -1118,7 +1117,7 @@ <string name="dialog_alert_title" msgid="2049658708609043103">"గమనిక"</string> <string name="loading" msgid="7933681260296021180">"లోడ్ చేస్తోంది…"</string> <string name="capital_on" msgid="1544682755514494298">"ఆన్లో ఉంది"</string> - <string name="capital_off" msgid="6815870386972805832">"ఆఫ్లో ఉంది"</string> + <string name="capital_off" msgid="6815870386972805832">"ఆఫ్"</string> <string name="whichApplication" msgid="4533185947064773386">"దీన్ని ఉపయోగించి చర్యను పూర్తి చేయండి"</string> <string name="whichApplicationNamed" msgid="8260158865936942783">"%1$sను ఉపయోగించి చర్యను పూర్తి చేయి"</string> <string name="whichApplicationLabel" msgid="7425855495383818784">"చర్యను పూర్తి చేయి"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index a8926324e543..f47c39271b96 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -526,7 +526,7 @@ <string name="biometric_error_canceled" msgid="349665227864885880">"ยกเลิกการตรวจสอบสิทธิ์แล้ว"</string> <string name="biometric_error_device_not_secured" msgid="6583143098363528349">"ไม่ได้ตั้ง PIN, รูปแบบ หรือรหัสผ่าน"</string> <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ตรวจพบลายนิ้วมือเพียงบางส่วน โปรดลองอีกครั้ง"</string> - <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ไม่สามารถประมวลผลลายนิ้วมือได้ โปรดลองอีกครั้ง"</string> + <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ประมวลผลลายนิ้วมือไม่ได้ โปรดลองอีกครั้ง"</string> <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"เซ็นเซอร์ลายนิ้วมือไม่สะอาด โปรดทำความสะอาดและลองอีกครั้ง"</string> <string name="fingerprint_acquired_too_fast" msgid="6470642383109155969">"เคลื่อนนิ้วเร็วเกินไป โปรดลองอีกครั้ง"</string> <string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"นิ้วเคลื่อนที่ช้าเกินไป โปรดลองอีกครั้ง"</string> @@ -661,7 +661,7 @@ <string name="policylab_wipeData" msgid="3910545446758639713">"ลบข้อมูลทั้งหมด"</string> <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"ลบข้อมูลของแท็บเล็ตโดยไม่มีการเตือน ด้วยการดำเนินการรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string> <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"ลบข้อมูลของทีวีโดยไม่ต้องมีคำเตือนโดยการรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string> - <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"ลบข้อมูลของโทรศัพท์โดยไม่มีการเตือน ด้วยการดำเนินการรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string> + <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"ลบข้อมูลโทรศัพท์โดยไม่มีการเตือน ด้วยการรีเซ็ตข้อมูลเป็นค่าเริ่มต้น"</string> <string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"ลบข้อมูลผู้ใช้"</string> <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"ลบข้อมูลของผู้ใช้นี้ในแท็บเล็ตเครื่องนี้โดยไม่มีการเตือน"</string> <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"ลบข้อมูลของผู้ใช้นี้ในทีวีเครื่องนี้โดยไม่มีการเตือน"</string> @@ -1616,7 +1616,7 @@ <item quantity="other">ลองอีกครั้งใน <xliff:g id="NUMBER">%d</xliff:g> วินาที</item> <item quantity="one">ลองอีกครั้งใน 1 วินาที</item> </plurals> - <string name="kg_pattern_instructions" msgid="398978611683075868">"วาดรูปแบบของคุณ"</string> + <string name="kg_pattern_instructions" msgid="398978611683075868">"ลากรูปแบบของคุณ"</string> <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"ป้อน PIN ของซิม"</string> <string name="kg_pin_instructions" msgid="2377242233495111557">"ป้อน PIN"</string> <string name="kg_password_instructions" msgid="5753646556186936819">"ป้อนรหัสผ่าน"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 3c56b01ef7a9..fa5c47961141 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -301,7 +301,7 @@ <string name="permgrouprequest_activityRecognition" msgid="7626438016904799383">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasına fiziksel aktivitenize erişme izni verilsin mi?"</string> <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string> <string name="permgroupdesc_camera" msgid="3250611594678347720">"fotoğraf çekme ve video kaydetme"</string> - <string name="permgrouprequest_camera" msgid="1299833592069671756">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasının resim çekmesine ve video kaydı yapmasına izin verilsin mi?"</string> + <string name="permgrouprequest_camera" msgid="1299833592069671756">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasının fotoğraf çekmesine ve video kaydı yapmasına izin verilsin mi?"</string> <string name="permgrouplab_calllog" msgid="8798646184930388160">"Arama kayıtları"</string> <string name="permgroupdesc_calllog" msgid="3006237336748283775">"telefon arama kaydını okuma ve yazma"</string> <string name="permgrouprequest_calllog" msgid="8487355309583773267">"Telefon arama kayıtlarınıza erişmek için <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uygulamasına izin verilsin mi?"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index e99eebb1d768..4f42e73d7277 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -527,8 +527,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"ایپ کو آپ کی تصویر کے مجموعے میں ترمیم کی اجازت دیتا ہے۔"</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"اپنی میڈيا کے مجموعے سے مقامات پڑھیں"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"ایپ کو آپ کی میڈيا کے مجموعے سے مقامات پڑھنے کی اجازت دیتا ہے۔"</string> - <!-- no translation found for biometric_dialog_default_title (881952973720613213) --> - <skip /> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"توثیق کریں کہ یہ آپ ہیں"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"بایومیٹرک ہارڈ ویئر دستیاب نہیں ہے"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"تصدیق کا عمل منسوخ ہو گیا"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"تسلیم شدہ نہیں ہے"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 123552d7ac71..322582dd3e7d 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -140,7 +140,7 @@ <string name="wfcSpnFormat_wifi" msgid="1892673884655959773">"Wi-Fi"</string> <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="1336669776254502831">"Wi-Fi chaqiruv"</string> <string name="wfcSpnFormat_vowifi" msgid="1765176406171272629">"VoWi-Fi"</string> - <string name="wifi_calling_off_summary" msgid="8720659586041656098">"O‘chiq"</string> + <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Yoqilmagan"</string> <string name="wfc_mode_wifi_preferred_summary" msgid="7335489823608689868">"Wi-Fi orqali chaqiruv"</string> <string name="wfc_mode_cellular_preferred_summary" msgid="7081742743152286290">"Mobil tarmoq orqali chaqiruv"</string> <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Faqat Wi-Fi"</string> @@ -280,9 +280,9 @@ <string name="permgrouprequest_contacts" msgid="6032805601881764300">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun kontaktlaringizga ruxsat berilsinmi?"</string> <string name="permgrouplab_location" msgid="7275582855722310164">"Joylashuv"</string> <string name="permgroupdesc_location" msgid="1346617465127855033">"shu qurilmaning joylashuvi haqidagi axborotga kirish"</string> - <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun bu qurilmaning joylashuvi haqidagi axborotdan foydalanishiga ruxsat berilsinmi?"</string> + <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun bu qurilmaning joylashuvi haqidagi axborotdan foydalanishga ruxsat berilsinmi?"</string> <string name="permgrouprequestdetail_location" msgid="1347189607421252902">"Bu ilovadan foydalanilayotdangina u joylashuv axborotidan foydalana oladi"</string> - <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ilovasiga bu qurilmaning joylashuv axboroti uchun <b>doimiy</b> ruxsat berilsinmi?"</string> + <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun bu qurilmaning joylashuvi haqidagi axborotdan <b>doim</b> foydalanish ruxsati berilsinmi?"</string> <string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"Ilova hozirda joylashuv axborotidan faqat ilova ishlatilayotgandagina foydala oladi"</string> <string name="permgrouplab_calendar" msgid="5863508437783683902">"Taqvim"</string> <string name="permgroupdesc_calendar" msgid="3889615280211184106">"taqvimingizga kirish"</string> @@ -981,8 +981,8 @@ <string name="days" msgid="4774547661021344602">"kun"</string> <string name="hour" msgid="2126771916426189481">"soat"</string> <string name="hours" msgid="894424005266852993">"soat"</string> - <string name="minute" msgid="9148878657703769868">"daq."</string> - <string name="minutes" msgid="5646001005827034509">"daq."</string> + <string name="minute" msgid="9148878657703769868">"daqiqa"</string> + <string name="minutes" msgid="5646001005827034509">"daqiqa"</string> <string name="second" msgid="3184235808021478">"sek"</string> <string name="seconds" msgid="3161515347216589235">"sek"</string> <string name="week" msgid="5617961537173061583">"hafta"</string> @@ -1836,8 +1836,8 @@ </plurals> <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha"</string> <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha (keyingi signal)"</string> - <string name="zen_mode_forever" msgid="931849471004038757">"O‘chirmaguningizcha"</string> - <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"“Bezovta qilinmasin” rejimi o‘chirilmaguncha"</string> + <string name="zen_mode_forever" msgid="931849471004038757">"Rejimdan chiqilgunicha"</string> + <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Bezovta qilinmasin rejimidan chiqilgunicha"</string> <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string> <string name="toolbar_collapse_description" msgid="2821479483960330739">"Yig‘ish"</string> <string name="zen_mode_feature_name" msgid="5254089399895895004">"Bezovta qilinmasin"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 44a7e0691b1a..9b7b464e49e2 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -280,7 +280,7 @@ <string name="permgrouprequest_contacts" msgid="6032805601881764300">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>访问您的通讯录吗?"</string> <string name="permgrouplab_location" msgid="7275582855722310164">"位置信息"</string> <string name="permgroupdesc_location" msgid="1346617465127855033">"获取此设备的位置信息"</string> - <string name="permgrouprequest_location" msgid="3788275734953323491">"要允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”获取此设备的位置信息吗?"</string> + <string name="permgrouprequest_location" msgid="3788275734953323491">"要允许<xliff:g id="APP_NAME">%1$s</xliff:g>获取此设备的位置信息吗?"</string> <string name="permgrouprequestdetail_location" msgid="1347189607421252902">"只有当您使用该应用时,该应用才有权访问位置信息"</string> <string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"要<b>一律允许</b>允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>访问此设备的位置信息吗?"</string> <string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"目前只有当您使用该应用时,该应用才能访问位置信息"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 1e15f186c91a..c633449e89b0 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1797,7 +1797,7 @@ <string name="package_deleted_device_owner" msgid="2307122077550236438">"已由您的管理員刪除"</string> <string name="confirm_battery_saver" msgid="639106420541753635">"好"</string> <string name="battery_saver_description_with_learn_more" msgid="2108984221113106294">"省電模式會關閉或限制背景活動、某些視覺效果以及其他高耗電功能,以延長電池壽命。"<annotation id="url">"瞭解詳情"</annotation></string> - <string name="battery_saver_description" msgid="6413346684861241431">"省電模式會關閉或限制背景活動、某些視覺效果以及其他高耗電功能,以延長電池壽命。"</string> + <string name="battery_saver_description" msgid="6413346684861241431">"「省電模式」會關閉或限制背景活動、某些視覺效果和其他耗電量高的功能,以延長電池壽命。"</string> <string name="data_saver_description" msgid="6015391409098303235">"「數據節省模式」可防止部分應用程式在背景收發資料,以節省數據用量。您正在使用的應用程式可存取資料,但次數可能會減少。例如,圖片可能需要輕按才會顯示。"</string> <string name="data_saver_enable_title" msgid="4674073932722787417">"要開啟「數據節省模式」嗎?"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"開啟"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 9d48fe384f3f..a5104242c71c 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2091,6 +2091,40 @@ Corresponds to {@link android.view.Window#setNavigationBarDividerColor(int)}. --> <attr name="navigationBarDividerColor" format="color" /> + <!-- Sets whether the system should ensure that the status bar has enough + contrast when a fully transparent background is requested. + + <p>If set to this value, the system will determine whether a scrim is necessary + to ensure that the status bar has enough contrast with the contents of + this app, and set an appropriate effective bar background color accordingly. + + <p>When the status bar color has a non-zero alpha value, the value of this + attribute has no effect. + + <p>If the app does not target at least {@link android.os.Build.VERSION_CODES#Q Q}, + this attribute is ignored. + + @see android.view.Window#setEnsureStatusBarContrastWhenTransparent + @hide pendingAPI --> + <attr name="ensureStatusBarContrastWhenTransparent" format="boolean" /> + + <!-- Sets whether the system should ensure that the navigation bar has enough + contrast when a fully transparent background is requested. + + <p>If set to this value, the system will determine whether a scrim is necessary + to ensure that the navigation bar has enough contrast with the contents of + this app, and set an appropriate effective bar background color accordingly. + + <p>When the navigation bar color has a non-zero alpha value, the value of this + attribute has no effect. + + <p>If the app does not target at least {@link android.os.Build.VERSION_CODES#Q Q}, + this attribute is ignored. + + @see android.view.Window#setEnsureNavigationBarContrastWhenTransparent + @hide pendingApi --> + <attr name="ensureNavigationBarContrastWhenTransparent" format="boolean" /> + <!-- The duration, in milliseconds, of the window background fade duration when transitioning into or away from an Activity when called with an Activity Transition. Corresponds to @@ -8980,6 +9014,10 @@ <!-- @hide From Theme.navigationBarColor, used for the TaskDescription navigation bar color. --> <attr name="navigationBarColor"/> + <!-- @hide From Window.ensureStatusBarContrastWhenTransparent --> + <attr name="ensureStatusBarContrastWhenTransparent"/> + <!-- @hide From Window.ensureNavigationBarContrastWhenTransparent --> + <attr name="ensureNavigationBarContrastWhenTransparent"/> </declare-styleable> <declare-styleable name="Shortcut"> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index e9b1bd3af0dc..ef26cd74cde0 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -215,6 +215,5 @@ <!-- Magnifier --> <color name="default_magnifier_color_overlay">#00FFFFFF</color> - <color name="chooser_row_divider">#1f000000</color> - + <color name="chooser_row_divider">@color/list_divider_color_light</color> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0163fc0e20ce..58a4c185d7e1 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -313,14 +313,15 @@ Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. --> <integer translatable="false" name="config_networkAvoidBadWifi">1</integer> - <!-- The URL returned by ConnectivityManager#getCaptivePortalServerUrl. The actual returned - value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL. This is the default value - used if that setting is unset. + <!-- Configuration hook for the URL returned by ConnectivityManager#getCaptivePortalServerUrl. + If empty, the returned value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL, + and if that value is empty, the framework will use a hard-coded default. This is *NOT* a URL that will always be used by the system network validation to detect captive portals: NetworkMonitor may use different strategies and will not necessarily use this URL. NetworkMonitor behaviour should be configured with NetworkStack resource overlays instead. --> - <string translatable="false" name="config_networkDefaultCaptivePortalServerUrl">http://connectivitycheck.gstatic.com/generate_204</string> + <!--suppress CheckTagEmptyBody --> + <string translatable="false" name="config_networkCaptivePortalServerUrl"></string> <!-- If the hardware supports specially marking packets that caused a wakeup of the main CPU, set this value to the mark used. --> @@ -1031,6 +1032,9 @@ <!-- Boolean indicating whether display white balance is supported. --> <bool name="config_displayWhiteBalanceAvailable">false</bool> + <!-- Boolean indicating whether display white balance should be enabled by default. --> + <bool name="config_displayWhiteBalanceEnabledDefault">false</bool> + <!-- Minimum color temperature, in Kelvin, supported by display white balance. --> <integer name="config_displayWhiteBalanceColorTemperatureMin">4000</integer> @@ -3251,6 +3255,10 @@ <!-- Controls the size of the back gesture inset. --> <dimen name="config_backGestureInset">0dp</dimen> + <!-- Controls whether the navbar needs a scrim with + {@link Window#setEnsureNavigationBarContrastWhenTransparent}. --> + <bool name="config_navBarNeedsScrim">true</bool> + <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. These values are in DPs and will be converted to pixel sizes internally. --> <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index c646fefad9d6..02cbc2e578cf 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -724,6 +724,7 @@ <dimen name="chooser_edge_margin_thin">16dp</dimen> <dimen name="chooser_edge_margin_normal">24dp</dimen> <dimen name="chooser_preview_image_font_size">20sp</dimen> + <dimen name="chooser_preview_image_border">1dp</dimen> <dimen name="chooser_preview_width">-1px</dimen> <dimen name="resolver_icon_size">42dp</dimen> <dimen name="resolver_badge_size">18dp</dimen> diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml index a43f7529c283..f22a91ff75c1 100644 --- a/core/res/res/values/dimens_car.xml +++ b/core/res/res/values/dimens_car.xml @@ -66,9 +66,10 @@ <dimen name="car_padding_0">4dp</dimen> <dimen name="car_padding_1">8dp</dimen> <dimen name="car_padding_2">16dp</dimen> - <dimen name="car_padding_3">28dp</dimen> + <dimen name="car_padding_3">24dp</dimen> <dimen name="car_padding_4">32dp</dimen> <dimen name="car_padding_5">64dp</dimen> + <dimen name="car_padding_6">96dp</dimen> <!-- Radius --> <dimen name="car_radius_1">4dp</dimen> @@ -121,6 +122,9 @@ <!-- Dialog button end margin --> <dimen name="button_end_margin">@*android:dimen/car_padding_4</dimen> + <!-- Dialog top padding when there is no title --> + <dimen name="dialog_no_title_padding_top">@*android:dimen/car_padding_4</dimen> + <!-- Dialog start margin for text view --> <dimen name="text_view_start_margin">@*android:dimen/car_keyline_1</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b87381364b1a..0304d8220152 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2011,7 +2011,7 @@ <java-symbol type="integer" name="config_networkNotifySwitchType" /> <java-symbol type="array" name="config_networkNotifySwitches" /> <java-symbol type="integer" name="config_networkAvoidBadWifi" /> - <java-symbol type="string" name="config_networkDefaultCaptivePortalServerUrl" /> + <java-symbol type="string" name="config_networkCaptivePortalServerUrl" /> <java-symbol type="integer" name="config_networkWakeupPacketMark" /> <java-symbol type="integer" name="config_networkWakeupPacketMask" /> <java-symbol type="bool" name="config_apfDrop802_3Frames" /> @@ -2777,6 +2777,7 @@ <java-symbol type="dimen" name="chooser_edge_margin_normal" /> <java-symbol type="dimen" name="chooser_preview_image_font_size"/> <java-symbol type="dimen" name="chooser_preview_width" /> + <java-symbol type="dimen" name="chooser_preview_image_border"/> <java-symbol type="dimen" name="chooser_max_collapsed_height" /> <java-symbol type="layout" name="chooser_grid" /> <java-symbol type="layout" name="chooser_grid_preview_text" /> @@ -2790,6 +2791,7 @@ <java-symbol type="drawable" name="scroll_indicator_material" /> <java-symbol type="layout" name="chooser_row" /> + <java-symbol type="color" name="chooser_row_divider" /> <java-symbol type="layout" name="chooser_row_direct_share" /> <java-symbol type="bool" name="config_supportDoubleTapWake" /> <java-symbol type="drawable" name="ic_perm_device_info" /> @@ -2846,6 +2848,7 @@ <java-symbol type="integer" name="config_navBarInteractionMode" /> <java-symbol type="bool" name="config_navBarCanMove" /> <java-symbol type="bool" name="config_navBarTapThrough" /> + <java-symbol type="bool" name="config_navBarNeedsScrim" /> <java-symbol type="dimen" name="config_backGestureInset" /> <java-symbol type="color" name="system_bar_background_semi_transparent" /> @@ -3148,7 +3151,6 @@ <java-symbol type="bool" name="config_setColorTransformAccelerated" /> <java-symbol type="bool" name="config_setColorTransformAcceleratedPerLayer" /> - <java-symbol type="bool" name="config_displayWhiteBalanceAvailable" /> <java-symbol type="bool" name="config_nightDisplayAvailable" /> <java-symbol type="bool" name="config_allowDisablingAssistDisclosure" /> <java-symbol type="integer" name="config_defaultNightDisplayAutoMode" /> @@ -3160,8 +3162,8 @@ <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficients" /> <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" /> <java-symbol type="array" name="config_availableColorModes" /> - <java-symbol type="bool" name="config_displayWhiteBalanceAvailable" /> + <java-symbol type="bool" name="config_displayWhiteBalanceEnabledDefault" /> <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMin" /> <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMax" /> <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureDefault" /> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 9f20ee6e19a0..03fb1fc72b45 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1658,7 +1658,7 @@ easier. <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault.Light" /> <!-- Theme used for the intent picker activity. --> - <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.DayNight"> + <style name="Theme.DeviceDefault.ResolverCommon" parent="Theme.DeviceDefault.DayNight"> <item name="windowEnterTransition">@empty</item> <item name="windowExitTransition">@empty</item> <item name="windowIsTranslucent">true</item> @@ -1670,6 +1670,12 @@ easier. <item name="colorControlActivated">?attr/colorControlHighlight</item> <item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item> <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item> + <item name="navigationBarColor">?attr/colorBackgroundFloating</item> + <item name="navigationBarDividerColor">@color/chooser_row_divider</item> + </style> + + <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon"> + <item name="windowLightNavigationBar">true</item> </style> <!-- @hide DeviceDefault themes for the autofill FillUi --> @@ -1719,5 +1725,7 @@ easier. </style> <!-- @hide DeviceDefault theme for the DocumentsUI app. --> - <style name="Theme.DeviceDefault.DocumentsUI" parent="Theme.DeviceDefault.DayNight" /> + <style name="Theme.DeviceDefault.DocumentsUI" parent="Theme.DeviceDefault.DayNight"> + <item name="actionModeCloseDrawable">@drawable/ic_clear_material</item> + </style> </resources> diff --git a/core/xsd/permission.xsd b/core/xsd/permission.xsd index 2ef2d04d3b34..9520db767350 100644 --- a/core/xsd/permission.xsd +++ b/core/xsd/permission.xsd @@ -20,33 +20,33 @@ xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="permissions"> <xs:complexType> - <xs:sequence> - <xs:element name="group" type="group" maxOccurs="unbounded"/> - <xs:element name="permission" type="permission" maxOccurs="unbounded"/> - <xs:element name="assign-permission" type="assign-permission" maxOccurs="unbounded"/> - <xs:element name="split-permission" type="split-permission" maxOccurs="unbounded"/> - <xs:element name="library" type="library" maxOccurs="unbounded"/> - <xs:element name="feature" type="feature" maxOccurs="unbounded"/> - <xs:element name="unavailable-feature" type="unavailable-feature" maxOccurs="unbounded"/> - <xs:element name="allow-in-power-save-except-idle" type="allow-in-power-save-except-idle" maxOccurs="unbounded"/> - <xs:element name="allow-in-power-save" type="allow-in-power-save" maxOccurs="unbounded"/> - <xs:element name="allow-in-data-usage-save" type="allow-in-data-usage-save" maxOccurs="unbounded"/> - <xs:element name="allow-unthrottled-location" type="allow-unthrottled-location" maxOccurs="unbounded"/> - <xs:element name="allow-ignore-location-settings" type="allow-ignore-location-settings" maxOccurs="unbounded"/> - <xs:element name="allow-implicit-broadcast" type="allow-implicit-broadcast" maxOccurs="unbounded"/> - <xs:element name="app-link" type="app-link" maxOccurs="unbounded"/> - <xs:element name="system-user-whitelisted-app" type="system-user-whitelisted-app" maxOccurs="unbounded"/> - <xs:element name="system-user-blacklisted-app" type="system-user-blacklisted-app" maxOccurs="unbounded"/> - <xs:element name="default-enabled-vr-app" type="default-enabled-vr-app" maxOccurs="unbounded"/> - <xs:element name="backup-transport-whitelisted-service" type="backup-transport-whitelisted-service" maxOccurs="unbounded"/> - <xs:element name="disabled-until-used-preinstalled-carrier-associated-app" type="disabled-until-used-preinstalled-carrier-associated-app" maxOccurs="unbounded"/> - <xs:element name="disabled-until-used-preinstalled-carrier-app" type="disabled-until-used-preinstalled-carrier-app" maxOccurs="unbounded"/> - <xs:element name="privapp-permissions" type="privapp-permissions" maxOccurs="unbounded"/> - <xs:element name="oem-permissions" type="oem-permissions" maxOccurs="unbounded"/> - <xs:element name="hidden-api-whitelisted-app" type="hidden-api-whitelisted-app" maxOccurs="unbounded"/> - <xs:element name="allow-association" type="allow-association" maxOccurs="unbounded"/> - <xs:element name="bugreport-whitelisted" type="bugreport-whitelisted" maxOccurs="unbounded"/> - </xs:sequence> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="group" type="group"/> + <xs:element name="permission" type="permission"/> + <xs:element name="assign-permission" type="assign-permission"/> + <xs:element name="split-permission" type="split-permission"/> + <xs:element name="library" type="library"/> + <xs:element name="feature" type="feature"/> + <xs:element name="unavailable-feature" type="unavailable-feature"/> + <xs:element name="allow-in-power-save-except-idle" type="allow-in-power-save-except-idle"/> + <xs:element name="allow-in-power-save" type="allow-in-power-save"/> + <xs:element name="allow-in-data-usage-save" type="allow-in-data-usage-save"/> + <xs:element name="allow-unthrottled-location" type="allow-unthrottled-location"/> + <xs:element name="allow-ignore-location-settings" type="allow-ignore-location-settings"/> + <xs:element name="allow-implicit-broadcast" type="allow-implicit-broadcast"/> + <xs:element name="app-link" type="app-link"/> + <xs:element name="system-user-whitelisted-app" type="system-user-whitelisted-app"/> + <xs:element name="system-user-blacklisted-app" type="system-user-blacklisted-app"/> + <xs:element name="default-enabled-vr-app" type="default-enabled-vr-app"/> + <xs:element name="backup-transport-whitelisted-service" type="backup-transport-whitelisted-service"/> + <xs:element name="disabled-until-used-preinstalled-carrier-associated-app" type="disabled-until-used-preinstalled-carrier-associated-app"/> + <xs:element name="disabled-until-used-preinstalled-carrier-app" type="disabled-until-used-preinstalled-carrier-app"/> + <xs:element name="privapp-permissions" type="privapp-permissions"/> + <xs:element name="oem-permissions" type="oem-permissions"/> + <xs:element name="hidden-api-whitelisted-app" type="hidden-api-whitelisted-app"/> + <xs:element name="allow-association" type="allow-association"/> + <xs:element name="bugreport-whitelisted" type="bugreport-whitelisted"/> + </xs:choice> </xs:complexType> </xs:element> <xs:complexType name="group"> diff --git a/core/xsd/schema/current.txt b/core/xsd/schema/current.txt index c25bc146045b..771c1dffb909 100644 --- a/core/xsd/schema/current.txt +++ b/core/xsd/schema/current.txt @@ -153,31 +153,31 @@ package com.android.xml.permission.configfile { public class Permissions { ctor public Permissions(); - method public java.util.List<com.android.xml.permission.configfile.AllowAssociation> getAllowAssociation(); - method public java.util.List<com.android.xml.permission.configfile.AllowIgnoreLocationSettings> getAllowIgnoreLocationSettings(); - method public java.util.List<com.android.xml.permission.configfile.AllowImplicitBroadcast> getAllowImplicitBroadcast(); - method public java.util.List<com.android.xml.permission.configfile.AllowInDataUsageSave> getAllowInDataUsageSave(); - method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSave> getAllowInPowerSave(); - method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSaveExceptIdle> getAllowInPowerSaveExceptIdle(); - method public java.util.List<com.android.xml.permission.configfile.AllowUnthrottledLocation> getAllowUnthrottledLocation(); - method public java.util.List<com.android.xml.permission.configfile.AppLink> getAppLink(); - method public java.util.List<com.android.xml.permission.configfile.AssignPermission> getAssignPermission(); - method public java.util.List<com.android.xml.permission.configfile.BackupTransportWhitelistedService> getBackupTransportWhitelistedService(); - method public java.util.List<com.android.xml.permission.configfile.BugreportWhitelisted> getBugreportWhitelisted(); - method public java.util.List<com.android.xml.permission.configfile.DefaultEnabledVrApp> getDefaultEnabledVrApp(); - method public java.util.List<com.android.xml.permission.configfile.DisabledUntilUsedPreinstalledCarrierApp> getDisabledUntilUsedPreinstalledCarrierApp(); - method public java.util.List<com.android.xml.permission.configfile.DisabledUntilUsedPreinstalledCarrierAssociatedApp> getDisabledUntilUsedPreinstalledCarrierAssociatedApp(); - method public java.util.List<com.android.xml.permission.configfile.Feature> getFeature(); - method public java.util.List<com.android.xml.permission.configfile.Group> getGroup(); - method public java.util.List<com.android.xml.permission.configfile.HiddenApiWhitelistedApp> getHiddenApiWhitelistedApp(); - method public java.util.List<com.android.xml.permission.configfile.Library> getLibrary(); - method public java.util.List<com.android.xml.permission.configfile.OemPermissions> getOemPermissions(); - method public java.util.List<com.android.xml.permission.configfile.Permission> getPermission(); - method public java.util.List<com.android.xml.permission.configfile.PrivappPermissions> getPrivappPermissions(); - method public java.util.List<com.android.xml.permission.configfile.SplitPermission> getSplitPermission(); - method public java.util.List<com.android.xml.permission.configfile.SystemUserBlacklistedApp> getSystemUserBlacklistedApp(); - method public java.util.List<com.android.xml.permission.configfile.SystemUserWhitelistedApp> getSystemUserWhitelistedApp(); - method public java.util.List<com.android.xml.permission.configfile.UnavailableFeature> getUnavailableFeature(); + method public java.util.List<com.android.xml.permission.configfile.AllowAssociation> getAllowAssociation_optional(); + method public java.util.List<com.android.xml.permission.configfile.AllowIgnoreLocationSettings> getAllowIgnoreLocationSettings_optional(); + method public java.util.List<com.android.xml.permission.configfile.AllowImplicitBroadcast> getAllowImplicitBroadcast_optional(); + method public java.util.List<com.android.xml.permission.configfile.AllowInDataUsageSave> getAllowInDataUsageSave_optional(); + method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSaveExceptIdle> getAllowInPowerSaveExceptIdle_optional(); + method public java.util.List<com.android.xml.permission.configfile.AllowInPowerSave> getAllowInPowerSave_optional(); + method public java.util.List<com.android.xml.permission.configfile.AllowUnthrottledLocation> getAllowUnthrottledLocation_optional(); + method public java.util.List<com.android.xml.permission.configfile.AppLink> getAppLink_optional(); + method public java.util.List<com.android.xml.permission.configfile.AssignPermission> getAssignPermission_optional(); + method public java.util.List<com.android.xml.permission.configfile.BackupTransportWhitelistedService> getBackupTransportWhitelistedService_optional(); + method public java.util.List<com.android.xml.permission.configfile.BugreportWhitelisted> getBugreportWhitelisted_optional(); + method public java.util.List<com.android.xml.permission.configfile.DefaultEnabledVrApp> getDefaultEnabledVrApp_optional(); + method public java.util.List<com.android.xml.permission.configfile.DisabledUntilUsedPreinstalledCarrierApp> getDisabledUntilUsedPreinstalledCarrierApp_optional(); + method public java.util.List<com.android.xml.permission.configfile.DisabledUntilUsedPreinstalledCarrierAssociatedApp> getDisabledUntilUsedPreinstalledCarrierAssociatedApp_optional(); + method public java.util.List<com.android.xml.permission.configfile.Feature> getFeature_optional(); + method public java.util.List<com.android.xml.permission.configfile.Group> getGroup_optional(); + method public java.util.List<com.android.xml.permission.configfile.HiddenApiWhitelistedApp> getHiddenApiWhitelistedApp_optional(); + method public java.util.List<com.android.xml.permission.configfile.Library> getLibrary_optional(); + method public java.util.List<com.android.xml.permission.configfile.OemPermissions> getOemPermissions_optional(); + method public java.util.List<com.android.xml.permission.configfile.Permission> getPermission_optional(); + method public java.util.List<com.android.xml.permission.configfile.PrivappPermissions> getPrivappPermissions_optional(); + method public java.util.List<com.android.xml.permission.configfile.SplitPermission> getSplitPermission_optional(); + method public java.util.List<com.android.xml.permission.configfile.SystemUserBlacklistedApp> getSystemUserBlacklistedApp_optional(); + method public java.util.List<com.android.xml.permission.configfile.SystemUserWhitelistedApp> getSystemUserWhitelistedApp_optional(); + method public java.util.List<com.android.xml.permission.configfile.UnavailableFeature> getUnavailableFeature_optional(); } public class PrivappPermissions { diff --git a/core/xsd/vts/Android.bp b/core/xsd/vts/Android.bp new file mode 100644 index 000000000000..9cf68c1ea703 --- /dev/null +++ b/core/xsd/vts/Android.bp @@ -0,0 +1,34 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_test { + name: "vts_permission_validate_test", + srcs: [ + "ValidatePermission.cpp" + ], + static_libs: [ + "android.hardware.audio.common.test.utility", + "libxml2", + ], + shared_libs: [ + "liblog", + "libbase", + ], + cflags: [ + "-Wall", + "-Werror", + ], +} diff --git a/core/xsd/vts/Android.mk b/core/xsd/vts/Android.mk new file mode 100644 index 000000000000..a5754a494d94 --- /dev/null +++ b/core/xsd/vts/Android.mk @@ -0,0 +1,22 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := VtsValidatePermission +include test/vts/tools/build/Android.host_config.mk diff --git a/core/xsd/vts/AndroidTest.xml b/core/xsd/vts/AndroidTest.xml new file mode 100644 index 000000000000..e5cc9a0f74ee --- /dev/null +++ b/core/xsd/vts/AndroidTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Config for VTS VtsValidatePermission."> + <option name="config-descriptor:metadata" key="plan" value="vts-treble" /> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher"> + <option name="abort-on-push-failure" value="false"/> + <option name="push-group" value="HostDrivenTest.push"/> + <option name="push" value="DATA/etc/permission.xsd->/data/local/tmp/permission.xsd"/> + </target_preparer> + <test class="com.android.tradefed.testtype.VtsMultiDeviceTest"> + <option name="test-module-name" value="VtsValidatePermission"/> + <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_permission_validate_test/vts_permission_validate_test" /> + <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_permission_validate_test/vts_permission_validate_test" /> + <option name="binary-test-type" value="gtest"/> + <option name="test-timeout" value="30s"/> + </test> +</configuration> diff --git a/core/xsd/vts/ValidatePermission.cpp b/core/xsd/vts/ValidatePermission.cpp new file mode 100644 index 000000000000..34996890000b --- /dev/null +++ b/core/xsd/vts/ValidatePermission.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <dirent.h> +#include <regex> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string> + +#include "android-base/logging.h" +#include "utility/ValidateXml.h" + +static void get_files_in_dirs(const char* dir_path, std::vector<std::string>& files) { + DIR* d; + struct dirent* de; + + d = opendir(dir_path); + if (d == nullptr) { + return; + } + + while ((de = readdir(d))) { + if (de->d_type != DT_REG) { + continue; + } + if (std::regex_match(de->d_name, std::regex("(.*)(.xml)"))) { + files.push_back(de->d_name); + } + } + closedir(d); +} + +TEST(CheckConfig, permission) { + RecordProperty("description", + "Verify that the permission file " + "is valid according to the schema"); + + const char* location = "/vendor/etc/permissions"; + + std::vector<std::string> files; + get_files_in_dirs(location, files); + + for (std::string file_name : files) { + EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(file_name.c_str(), {location}, + "/data/local/tmp/permission.xsd"); + } +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index ed198e60902b..e4a93e740c4c 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -309,6 +309,7 @@ applications that come with the platform <permission name="android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"/> <permission name="android.permission.SET_WALLPAPER" /> <permission name="android.permission.SET_WALLPAPER_COMPONENT" /> + <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/libs/hwui/tests/unit/CommonPoolTests.cpp b/libs/hwui/tests/unit/CommonPoolTests.cpp index c564ed632786..70a5f5acbb6e 100644 --- a/libs/hwui/tests/unit/CommonPoolTests.cpp +++ b/libs/hwui/tests/unit/CommonPoolTests.cpp @@ -135,4 +135,48 @@ TEST(CommonPool, fullQueue) { for (auto& f : futures) { f.get(); } +} + +class ObjectTracker { + static std::atomic_int sGlobalCount; + +public: + ObjectTracker() { + sGlobalCount++; + } + ObjectTracker(const ObjectTracker&) { + sGlobalCount++; + } + ObjectTracker(ObjectTracker&&) { + sGlobalCount++; + } + ~ObjectTracker() { + sGlobalCount--; + } + + static int count() { return sGlobalCount.load(); } +}; + +std::atomic_int ObjectTracker::sGlobalCount{0}; + +TEST(CommonPool, asyncLifecycleCheck) { + ASSERT_EQ(0, ObjectTracker::count()); + { + ObjectTracker obj; + ASSERT_EQ(1, ObjectTracker::count()); + EXPECT_LT(1, CommonPool::async([obj] { return ObjectTracker::count(); }).get()); + } + CommonPool::waitForIdle(); + ASSERT_EQ(0, ObjectTracker::count()); +} + +TEST(CommonPool, syncLifecycleCheck) { + ASSERT_EQ(0, ObjectTracker::count()); + { + ObjectTracker obj; + ASSERT_EQ(1, ObjectTracker::count()); + EXPECT_LT(1, CommonPool::runSync([obj] { return ObjectTracker::count(); })); + } + CommonPool::waitForIdle(); + ASSERT_EQ(0, ObjectTracker::count()); }
\ No newline at end of file diff --git a/libs/hwui/thread/CommonPool.cpp b/libs/hwui/thread/CommonPool.cpp index 7f94a152cf8d..d011bdfe945e 100644 --- a/libs/hwui/thread/CommonPool.cpp +++ b/libs/hwui/thread/CommonPool.cpp @@ -49,9 +49,13 @@ CommonPool::CommonPool() { } } -void CommonPool::post(Task&& task) { +CommonPool& CommonPool::instance() { static CommonPool pool; - pool.enqueue(std::move(task)); + return pool; +} + +void CommonPool::post(Task&& task) { + instance().enqueue(std::move(task)); } void CommonPool::enqueue(Task&& task) { @@ -86,5 +90,18 @@ void CommonPool::workerLoop() { } } +void CommonPool::waitForIdle() { + instance().doWaitForIdle(); +} + +void CommonPool::doWaitForIdle() { + std::unique_lock lock(mLock); + while (mWaitingThreads != THREAD_COUNT) { + lock.unlock(); + usleep(100); + lock.lock(); + } +} + } // namespace uirenderer } // namespace android
\ No newline at end of file diff --git a/libs/hwui/thread/CommonPool.h b/libs/hwui/thread/CommonPool.h index aef2990d6343..7603eeef4692 100644 --- a/libs/hwui/thread/CommonPool.h +++ b/libs/hwui/thread/CommonPool.h @@ -57,11 +57,13 @@ public: mHead = newHead; } - constexpr T&& pop() { + constexpr T pop() { LOG_ALWAYS_FATAL_IF(mTail == mHead, "empty"); int index = mTail; mTail = (mTail + 1) % SIZE; - return std::move(mBuffer[index]); + T ret = std::move(mBuffer[index]); + mBuffer[index] = nullptr; + return ret; } private: @@ -95,11 +97,17 @@ public: return task.get_future().get(); }; + // For testing purposes only, blocks until all worker threads are parked. + static void waitForIdle(); + private: + static CommonPool& instance(); + CommonPool(); ~CommonPool() {} void enqueue(Task&&); + void doWaitForIdle(); void workerLoop(); diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index d14116f7b555..39740bd46f9f 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -60,6 +60,9 @@ SkColorType PixelFormatToColorType(android::PixelFormat format) { } sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { + if (dataspace == HAL_DATASPACE_UNKNOWN) { + return SkColorSpace::MakeSRGB(); + } skcms_Matrix3x3 gamut; switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { diff --git a/media/Android.bp b/media/Android.bp index 34801813ee87..8746046c220d 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -24,6 +24,10 @@ java_library { "mediaplayer2-protos", ], + permitted_packages: [ + "android.media", + ], + installable: true, // Make sure that the implementaion only relies on SDK or system APIs. diff --git a/media/OWNERS b/media/OWNERS index 72c89529974b..a33a990f5ba4 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -1,3 +1,4 @@ +andrewlewis@google.com chz@google.com dwkang@google.com elaurent@google.com diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java index 87035da3d513..72c18f6148f0 100644 --- a/media/apex/java/android/media/MediaPlayer2.java +++ b/media/apex/java/android/media/MediaPlayer2.java @@ -31,6 +31,7 @@ import android.media.MediaDrm.KeyRequest; import android.media.MediaPlayer2.DrmInfo; import android.media.MediaPlayer2Proto.PlayerMessage; import android.media.MediaPlayer2Proto.Value; +import android.media.protobuf.InvalidProtocolBufferException; import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; @@ -46,7 +47,6 @@ import android.view.Surface; import android.view.SurfaceHolder; import com.android.internal.annotations.GuardedBy; -import com.android.media.protobuf.InvalidProtocolBufferException; import java.io.ByteArrayOutputStream; import java.io.File; @@ -546,7 +546,7 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting { @Override void process() { if (getState() == PLAYER_STATE_PLAYING) { - pause(); + native_pause(); } playNextDataSource(); } diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index 3a336780b465..9d4bce7b4301 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -893,7 +893,7 @@ public final class AudioAttributes implements Parcelable { * @param muted true to force muting haptic channels. * @return the same Builder instance. */ - public Builder setMuteHapticChannels(boolean muted) { + public @NonNull Builder setHapticChannelsMuted(boolean muted) { mMuteHapticChannels = muted; return this; } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 325e227e6f93..790e189e9109 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -25,7 +25,6 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.os.Binder; -import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -1662,7 +1661,7 @@ public class AudioTrack extends PlayerBase * a better solution. * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 112561552) + @UnsupportedAppUsage(trackingBug = 130237544) public int getLatency() { return native_get_latency(); } diff --git a/media/proto/jarjar-rules.txt b/media/proto/jarjar-rules.txt index bfb0b2782486..e73f86dddac1 100644 --- a/media/proto/jarjar-rules.txt +++ b/media/proto/jarjar-rules.txt @@ -1,2 +1,2 @@ -rule com.google.protobuf.** com.android.media.protobuf.@1 +rule com.google.protobuf.** android.media.protobuf.@1 diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml index a5286364dc26..355bdd8dfc98 100644 --- a/packages/CaptivePortalLogin/AndroidManifest.xml +++ b/packages/CaptivePortalLogin/AndroidManifest.xml @@ -18,7 +18,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.captiveportallogin" - android:versionCode="200000000" + android:versionCode="210000000" android:versionName="Q-initial"> <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" /> diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java index 0a20eaa0b888..a371a1d8db01 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java @@ -21,6 +21,7 @@ import android.content.Intent; import android.content.res.TypedArray; import android.os.UserHandle; import android.util.AttributeSet; +import android.view.Display; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; @@ -203,4 +204,16 @@ public class CarFacetButton extends LinearLayout { mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE); } } + + /** + * @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on + * a display. + */ + public int getDisplayId() { + Display display = getDisplay(); + if (display == null) { + return Display.INVALID_DISPLAY; + } + return display.getDisplayId(); + } } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java index 7811a1caeb88..d20038db9151 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java @@ -22,10 +22,13 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.view.Display; +import android.util.Log; import android.view.View; +import android.view.ViewGroup; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -40,15 +43,16 @@ import javax.inject.Singleton; @Singleton public class CarFacetButtonController { - protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>(); - protected HashMap<String, CarFacetButton> mButtonsByPackage = new HashMap<>(); - protected HashMap<String, CarFacetButton> mButtonsByComponentName = new HashMap<>(); - protected CarFacetButton mSelectedFacetButton; + protected ButtonMap mButtonsByCategory = new ButtonMap(); + protected ButtonMap mButtonsByPackage = new ButtonMap(); + protected ButtonMap mButtonsByComponentName = new ButtonMap(); + protected HashSet<CarFacetButton> mSelectedFacetButtons; protected Context mContext; @Inject public CarFacetButtonController(Context context) { mContext = context; + mSelectedFacetButtons = new HashSet<>(); } /** @@ -59,27 +63,40 @@ public class CarFacetButtonController { public void addFacetButton(CarFacetButton facetButton) { String[] categories = facetButton.getCategories(); for (int i = 0; i < categories.length; i++) { - mButtonsByCategory.put(categories[i], facetButton); + mButtonsByCategory.add(categories[i], facetButton); } String[] facetPackages = facetButton.getFacetPackages(); for (int i = 0; i < facetPackages.length; i++) { - mButtonsByPackage.put(facetPackages[i], facetButton); + mButtonsByPackage.add(facetPackages[i], facetButton); } String[] componentNames = facetButton.getComponentName(); for (int i = 0; i < componentNames.length; i++) { - mButtonsByComponentName.put(componentNames[i], facetButton); + mButtonsByComponentName.add(componentNames[i], facetButton); } - // Using the following as a default button for display id info it's not - // attached to a screen at this point so it can't be extracted here. - mSelectedFacetButton = facetButton; } public void removeAll() { mButtonsByCategory.clear(); mButtonsByPackage.clear(); mButtonsByComponentName.clear(); - mSelectedFacetButton = null; + mSelectedFacetButtons.clear(); + } + + /** + * Iterate through a view looking for CarFacetButtons and adding them to the controller if found + * + * @param v the View that may contain CarFacetButtons + */ + public void addAllFacetButtons(View v) { + if (v instanceof CarFacetButton) { + addFacetButton((CarFacetButton) v); + } else if (v instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) v; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + addAllFacetButtons(viewGroup.getChildAt(i)); + } + } } /** @@ -94,12 +111,10 @@ public class CarFacetButtonController { * @param stackInfoList of the currently running application */ public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) { - int displayId = getDisplayId(); ActivityManager.StackInfo validStackInfo = null; - for (ActivityManager.StackInfo stackInfo : stackInfoList) { - // If the display id is unknown or it matches the stack, it's valid for use - if ((displayId == -1 || displayId == stackInfo.displayId) - && stackInfo.topActivity != null) { + for (ActivityManager.StackInfo stackInfo :stackInfoList) { + // Find the first stack info with a topActivity + if (stackInfo.topActivity != null) { validStackInfo = stackInfo; break; } @@ -110,12 +125,20 @@ public class CarFacetButtonController { return; } - if (mSelectedFacetButton != null) { - mSelectedFacetButton.setSelected(false); + if (mSelectedFacetButtons != null) { + Iterator<CarFacetButton> iterator = mSelectedFacetButtons.iterator(); + while(iterator.hasNext()) { + CarFacetButton carFacetButton = iterator.next(); + if (carFacetButton.getDisplayId() == validStackInfo.displayId) { + carFacetButton.setSelected(false); + iterator.remove(); + } + } } String packageName = validStackInfo.topActivity.getPackageName(); - CarFacetButton facetButton = findFacetButtongByComponentName(validStackInfo.topActivity); + HashSet<CarFacetButton> facetButton = + findFacetButtonByComponentName(validStackInfo.topActivity); if (facetButton == null) { facetButton = mButtonsByPackage.get(packageName); } @@ -127,26 +150,21 @@ public class CarFacetButtonController { } } - if (facetButton != null && facetButton.getVisibility() == View.VISIBLE) { - facetButton.setSelected(true); - mSelectedFacetButton = facetButton; - } - - } - - private int getDisplayId() { - if (mSelectedFacetButton != null) { - Display display = mSelectedFacetButton.getDisplay(); - if (display != null) { - return display.getDisplayId(); + if (facetButton != null) { + for (CarFacetButton carFacetButton : facetButton) { + if (carFacetButton.getDisplayId() == validStackInfo.displayId) { + carFacetButton.setSelected(true); + mSelectedFacetButtons.add(carFacetButton); + } } } - return -1; + } - private CarFacetButton findFacetButtongByComponentName(ComponentName componentName) { - CarFacetButton button = mButtonsByComponentName.get(componentName.flattenToShortString()); - return (button != null) ? button : + private HashSet<CarFacetButton> findFacetButtonByComponentName(ComponentName componentName) { + HashSet<CarFacetButton> buttons = + mButtonsByComponentName.get(componentName.flattenToShortString()); + return (buttons != null) ? buttons : mButtonsByComponentName.get(componentName.flattenToString()); } @@ -168,4 +186,18 @@ public class CarFacetButtonController { } return null; } + + // simple multi-map + private static class ButtonMap extends HashMap<String, HashSet<CarFacetButton>> { + + public boolean add(String key, CarFacetButton value) { + if (containsKey(key)) { + return get(key).add(value); + } + HashSet<CarFacetButton> set = new HashSet<>(); + set.add(value); + put(key, set); + return true; + } + } } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 9bcc1ab90e47..44e8874f8386 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -187,11 +187,13 @@ public class CarStatusBar extends StatusBar implements if (mIsKeyguard) { updateNavBarForKeyguardContent(); } + // CarFacetButtonController was reset therefore we need to re-add the status bar elements + // to the controller. + mCarFacetButtonController.addAllFacetButtons(mStatusBarWindow); } private void addTemperatureViewToController(View v) { if (v instanceof TemperatureView) { - Log.d(TAG, "addTemperatureViewToController: found "); mHvacController.addHvacTextView((TemperatureView) v); } else if (v instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) v; diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java index fcbda1d5c63b..43d7d8fd0ac4 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java @@ -326,6 +326,8 @@ public class DynamicSystemInstallationService extends Service } else if (status == STATUS_READY) { startForeground(NOTIFICATION_ID, buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED)); + } else { + stopSelf(); } } diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml index 45a59a32ec17..73bfd3010983 100644 --- a/packages/ExtServices/AndroidManifest.xml +++ b/packages/ExtServices/AndroidManifest.xml @@ -17,7 +17,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" package="android.ext.services" - android:versionCode="200000000" + android:versionCode="210000000" android:versionName="1" coreApp="true"> diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp index 262e6f633917..5817118bc861 100644 --- a/packages/NetworkStack/Android.bp +++ b/packages/NetworkStack/Android.bp @@ -39,6 +39,7 @@ android_library { ":services-networkstack-shared-srcs", ], static_libs: [ + "androidx.annotation_annotation", "ipmemorystore-client", "netd_aidl_interface-java", "networkstack-aidl-interfaces-java", diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml index b4588e01dc79..ac05c44c1803 100644 --- a/packages/NetworkStack/AndroidManifest.xml +++ b/packages/NetworkStack/AndroidManifest.xml @@ -19,7 +19,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.networkstack" android:sharedUserId="android.uid.networkstack" - android:versionCode="200000000" + android:versionCode="210000000" android:versionName="29 system image" > diff --git a/packages/NetworkStack/res/values/config.xml b/packages/NetworkStack/res/values/config.xml index 52425e57ba3a..90f96e0f7a18 100644 --- a/packages/NetworkStack/res/values/config.xml +++ b/packages/NetworkStack/res/values/config.xml @@ -1,5 +1,38 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <!-- Captive portal http url --> - <string name="config_captive_portal_http_url" translatable="false">http://connectivitycheck.gstatic.com/generate_204</string> + <!-- + OEMs that wish to change the below settings must do so via a runtime resource overlay package + and *NOT* by changing this file. This file is part of the NetworkStack mainline module. + The overlays must apply to the config_* values, not the default_* values. The default_* + values are meant to be the default when no other configuration is specified. + --> + + <!-- HTTP URL for network validation, to use for detecting captive portals. --> + <string name="default_captive_portal_http_url" translatable="false">http://connectivitycheck.gstatic.com/generate_204</string> + + <!-- HTTPS URL for network validation, to use for confirming internet connectivity. --> + <string name="default_captive_portal_https_url" translatable="false">https://www.google.com/generate_204</string> + + <!-- List of fallback URLs to use for detecting captive portals. --> + <string-array name="default_captive_portal_fallback_urls" translatable="false"> + <item>http://www.google.com/gen_204</item> + <item>http://play.googleapis.com/generate_204</item> + </string-array> + + <!-- List of fallback probe specs to use for detecting captive portals. + This is an alternative to fallback URLs that provides more flexibility on detection rules. + Empty, so unused by default. --> + <string-array name="default_captive_portal_fallback_probe_specs" translatable="false"> + </string-array> + + <!-- Configuration hooks for the above settings. + Empty by default but may be overridden by RROs. --> + <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook --> + <string name="config_captive_portal_http_url" translatable="false"></string> + <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook --> + <string name="config_captive_portal_https_url" translatable="false"></string> + <string-array name="config_captive_portal_fallback_urls" translatable="false"> + </string-array> + <string-array name="config_captive_portal_fallback_probe_specs" translatable="false"> + </string-array> </resources>
\ No newline at end of file diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 7b77d66ca01f..588dcf2a83f5 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -28,6 +28,7 @@ import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVI import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.captiveportal.CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs; import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE; import static android.net.metrics.ValidationProbeEvent.DNS_SUCCESS; import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK; @@ -52,6 +53,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; @@ -91,6 +93,9 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import androidx.annotation.ArrayRes; +import androidx.annotation.StringRes; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.RingBufferIndices; import com.android.internal.util.State; @@ -105,7 +110,6 @@ import java.net.URL; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -113,6 +117,7 @@ import java.util.Random; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.Function; /** * {@hide} @@ -122,15 +127,6 @@ public class NetworkMonitor extends StateMachine { private static final boolean DBG = true; private static final boolean VDBG = false; private static final boolean VDBG_STALL = Log.isLoggable(TAG, Log.DEBUG); - // TODO: use another permission for CaptivePortalLoginActivity once it has its own certificate - private static final String PERMISSION_NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS"; - // Default configuration values for captive portal detection probes. - // TODO: append a random length parameter to the default HTTPS url. - // TODO: randomize browser version ids in the default User-Agent String. - private static final String DEFAULT_HTTPS_URL = "https://www.google.com/generate_204"; - private static final String DEFAULT_FALLBACK_URL = "http://www.google.com/gen_204"; - private static final String DEFAULT_OTHER_FALLBACK_URLS = - "http://play.googleapis.com/generate_204"; private static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/60.0.3112.32 Safari/537.36"; @@ -378,7 +374,7 @@ public class NetworkMonitor extends StateMachine { mUseHttps = getUseHttpsValidation(); mCaptivePortalUserAgent = getCaptivePortalUserAgent(); mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl()); - mCaptivePortalHttpUrl = makeURL(deps.getCaptivePortalServerHttpUrl(context)); + mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl()); mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls(); mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs(); mRandom = deps.getRandom(); @@ -1178,8 +1174,22 @@ public class NetworkMonitor extends StateMachine { } private String getCaptivePortalServerHttpsUrl() { - return mDependencies.getSetting(mContext, - Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL); + return getSettingFromResource(mContext, R.string.config_captive_portal_https_url, + R.string.default_captive_portal_https_url, + Settings.Global.CAPTIVE_PORTAL_HTTPS_URL); + } + + /** + * Get the captive portal server HTTP URL that is configured on the device. + * + * NetworkMonitor does not use {@link ConnectivityManager#getCaptivePortalServerUrl()} as + * it has its own updatable strategies to detect captive portals. The framework only advises + * on one URL that can be used, while NetworkMonitor may implement more complex logic. + */ + public String getCaptivePortalServerHttpUrl() { + return getSettingFromResource(mContext, R.string.config_captive_portal_http_url, + R.string.default_captive_portal_http_url, + Settings.Global.CAPTIVE_PORTAL_HTTP_URL); } private int getConsecutiveDnsTimeoutThreshold() { @@ -1208,24 +1218,23 @@ public class NetworkMonitor extends StateMachine { private URL[] makeCaptivePortalFallbackUrls() { try { - String separator = ","; - String firstUrl = mDependencies.getSetting(mContext, - Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL); - String joinedUrls = firstUrl + separator + mDependencies.getSetting(mContext, - Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, - DEFAULT_OTHER_FALLBACK_URLS); - List<URL> urls = new ArrayList<>(); - for (String s : joinedUrls.split(separator)) { - URL u = makeURL(s); - if (u == null) { - continue; - } - urls.add(u); - } - if (urls.isEmpty()) { - Log.e(TAG, String.format("could not create any url from %s", joinedUrls)); + final String firstUrl = mDependencies.getSetting(mContext, + Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, null); + + final URL[] settingProviderUrls; + if (!TextUtils.isEmpty(firstUrl)) { + final String otherUrls = mDependencies.getSetting(mContext, + Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, ""); + // otherUrls may be empty, but .split() ignores trailing empty strings + final String separator = ","; + final String[] urls = (firstUrl + separator + otherUrls).split(separator); + settingProviderUrls = convertStrings(urls, this::makeURL, new URL[0]); + } else { + settingProviderUrls = new URL[0]; } - return urls.toArray(new URL[urls.size()]); + + return getArrayConfig(settingProviderUrls, R.array.config_captive_portal_fallback_urls, + R.array.default_captive_portal_fallback_urls, this::makeURL); } catch (Exception e) { // Don't let a misconfiguration bootloop the system. Log.e(TAG, "Error parsing configured fallback URLs", e); @@ -1237,15 +1246,14 @@ public class NetworkMonitor extends StateMachine { try { final String settingsValue = mDependencies.getSetting( mContext, Settings.Global.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null); - // Probe specs only used if configured in settings - if (TextUtils.isEmpty(settingsValue)) { - return null; - } - - final Collection<CaptivePortalProbeSpec> specs = - CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs(settingsValue); - final CaptivePortalProbeSpec[] specsArray = new CaptivePortalProbeSpec[specs.size()]; - return specs.toArray(specsArray); + final CaptivePortalProbeSpec[] emptySpecs = new CaptivePortalProbeSpec[0]; + final CaptivePortalProbeSpec[] providerValue = TextUtils.isEmpty(settingsValue) + ? emptySpecs + : parseCaptivePortalProbeSpecs(settingsValue).toArray(emptySpecs); + + return getArrayConfig(providerValue, R.array.config_captive_portal_fallback_probe_specs, + R.array.default_captive_portal_fallback_probe_specs, + CaptivePortalProbeSpec::parseSpecOrNull); } catch (Exception e) { // Don't let a misconfiguration bootloop the system. Log.e(TAG, "Error parsing configured fallback probe specs", e); @@ -1253,6 +1261,83 @@ public class NetworkMonitor extends StateMachine { } } + /** + * Read a setting from a resource or the settings provider. + * + * <p>The configuration resource is prioritized, then the provider value, then the default + * resource value. + * @param context The context + * @param configResource The resource id for the configuration parameter + * @param defaultResource The resource id for the default value + * @param symbol The symbol in the settings provider + * @return The best available value + */ + @NonNull + private String getSettingFromResource(@NonNull final Context context, + @StringRes int configResource, @StringRes int defaultResource, + @NonNull String symbol) { + final Resources res = context.getResources(); + String setting = res.getString(configResource); + + if (!TextUtils.isEmpty(setting)) return setting; + + setting = mDependencies.getSetting(context, symbol, null); + if (!TextUtils.isEmpty(setting)) return setting; + + return res.getString(defaultResource); + } + + /** + * Get an array configuration from resources or the settings provider. + * + * <p>The configuration resource is prioritized, then the provider values, then the default + * resource values. + * @param providerValue Values obtained from the setting provider. + * @param configResId ID of the configuration resource. + * @param defaultResId ID of the default resource. + * @param resourceConverter Converter from the resource strings to stored setting class. Null + * return values are ignored. + */ + private <T> T[] getArrayConfig(@NonNull T[] providerValue, @ArrayRes int configResId, + @ArrayRes int defaultResId, @NonNull Function<String, T> resourceConverter) { + final Resources res = mContext.getResources(); + String[] configValue = res.getStringArray(configResId); + + if (configValue.length == 0) { + if (providerValue.length > 0) { + return providerValue; + } + + configValue = res.getStringArray(defaultResId); + } + + return convertStrings(configValue, resourceConverter, Arrays.copyOf(providerValue, 0)); + } + + /** + * Convert a String array to an array of some other type using the specified converter. + * + * <p>Any null value, or value for which the converter throws a {@link RuntimeException}, will + * not be added to the output array, so the output array may be smaller than the input. + */ + private <T> T[] convertStrings( + @NonNull String[] strings, Function<String, T> converter, T[] emptyArray) { + final ArrayList<T> convertedValues = new ArrayList<>(strings.length); + for (String configString : strings) { + T convertedValue = null; + try { + convertedValue = converter.apply(configString); + } catch (Exception e) { + Log.e(TAG, "Error parsing configuration", e); + // Fall through + } + if (convertedValue != null) { + convertedValues.add(convertedValue); + } + } + return convertedValues.toArray(emptyArray); + } + private String getCaptivePortalUserAgent() { return mDependencies.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT); @@ -1694,19 +1779,6 @@ public class NetworkMonitor extends StateMachine { } /** - * Get the captive portal server HTTP URL that is configured on the device. - * - * NetworkMonitor does not use {@link ConnectivityManager#getCaptivePortalServerUrl()} as - * it has its own updatable strategies to detect captive portals. The framework only advises - * on one URL that can be used, while NetworkMonitor may implement more complex logic. - */ - public String getCaptivePortalServerHttpUrl(Context context) { - final String defaultUrl = - context.getResources().getString(R.string.config_captive_portal_http_url); - return NetworkMonitorUtils.getCaptivePortalServerHttpUrl(context, defaultUrl); - } - - /** * Get the value of a global integer setting. * @param symbol Name of the setting * @param defaultValue Value to return if the setting is not defined. diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java index 9f6c7f89d71a..fa41284317bb 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -34,7 +34,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; @@ -48,7 +47,10 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.INetworkMonitorCallbacks; import android.net.InetAddresses; @@ -76,6 +78,7 @@ import android.util.ArrayMap; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -89,6 +92,7 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.URL; +import java.util.HashSet; import java.util.Random; import javax.net.ssl.SSLHandshakeException; @@ -99,6 +103,7 @@ public class NetworkMonitorTest { private static final String LOCATION_HEADER = "location"; private @Mock Context mContext; + private @Mock Resources mResources; private @Mock IpConnectivityLog mLogger; private @Mock SharedLog mValidationLogger; private @Mock NetworkInfo mNetworkInfo; @@ -117,6 +122,9 @@ public class NetworkMonitorTest { private @Mock WifiInfo mWifiInfo; private @Captor ArgumentCaptor<String> mNetworkTestedRedirectUrlCaptor; + private HashSet<WrappedNetworkMonitor> mCreatedNetworkMonitors; + private HashSet<BroadcastReceiver> mRegisteredReceivers; + private static final int TEST_NETID = 4242; private static final String TEST_HTTP_URL = "http://www.google.com/gen_204"; private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204"; @@ -153,14 +161,20 @@ public class NetworkMonitorTest { .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_USE_HTTPS), anyInt())).thenReturn(1); - when(mDependencies.getCaptivePortalServerHttpUrl(any())).thenReturn(TEST_HTTP_URL); - when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), - anyString())).thenReturn(TEST_HTTPS_URL); + when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any())) + .thenReturn(TEST_HTTP_URL); + when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), any())) + .thenReturn(TEST_HTTPS_URL); + doReturn(mNetwork).when(mNetwork).getPrivateDnsBypassingCopy(); when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm); when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony); when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi); + when(mContext.getResources()).thenReturn(mResources); + + when(mResources.getString(anyInt())).thenReturn(""); + when(mResources.getStringArray(anyInt())).thenReturn(new String[0]); when(mNetworkInfo.getType()).thenReturn(ConnectivityManager.TYPE_WIFI); setFallbackUrl(TEST_FALLBACK_URL); @@ -190,14 +204,45 @@ public class NetworkMonitorTest { InetAddresses.parseNumericAddress("192.168.0.0") }).when(mNetwork).getAllByName(any()); + when(mContext.registerReceiver(any(BroadcastReceiver.class), any())).then((invocation) -> { + mRegisteredReceivers.add(invocation.getArgument(0)); + return new Intent(); + }); + + doAnswer((invocation) -> { + mRegisteredReceivers.remove(invocation.getArgument(0)); + return null; + }).when(mContext).unregisterReceiver(any()); + setMinDataStallEvaluateInterval(500); setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); setValidDataStallDnsTimeThreshold(500); setConsecutiveDnsTimeoutThreshold(5); + + mCreatedNetworkMonitors = new HashSet<>(); + mRegisteredReceivers = new HashSet<>(); + } + + @After + public void tearDown() { + assertTrue(mCreatedNetworkMonitors.size() > 0); + // Make a local copy of mCreatedNetworkMonitors because during the iteration below, + // WrappedNetworkMonitor#onQuitting will delete elements from it on the handler threads. + WrappedNetworkMonitor[] networkMonitors = mCreatedNetworkMonitors.toArray( + new WrappedNetworkMonitor[0]); + for (WrappedNetworkMonitor nm : networkMonitors) { + nm.notifyNetworkDisconnected(); + nm.awaitQuit(); + } + assertEquals("NetworkMonitor still running after disconnect", + 0, mCreatedNetworkMonitors.size()); + assertEquals("BroadcastReceiver still registered after disconnect", + 0, mRegisteredReceivers.size()); } private class WrappedNetworkMonitor extends NetworkMonitor { private long mProbeTime = 0; + private final ConditionVariable mQuitCv = new ConditionVariable(false); WrappedNetworkMonitor() { super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger, mDependencies, @@ -217,12 +262,24 @@ public class NetworkMonitorTest { protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) { generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); } + + @Override + protected void onQuitting() { + assertTrue(mCreatedNetworkMonitors.remove(this)); + mQuitCv.open(); + } + + protected void awaitQuit() { + assertTrue("NetworkMonitor did not quit after " + HANDLER_TIMEOUT_MS + "ms", + mQuitCv.block(HANDLER_TIMEOUT_MS)); + } } private WrappedNetworkMonitor makeMonitor() { final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(); nm.start(); waitForIdle(nm.getHandler()); + mCreatedNetworkMonitors.add(nm); return nm; } @@ -475,6 +532,8 @@ public class NetworkMonitorTest { verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) .showProvisioningNotification(any(), any()); + assertEquals(1, mRegisteredReceivers.size()); + // Check that startCaptivePortalApp sends the expected intent. nm.launchCaptivePortalApp(); @@ -497,6 +556,8 @@ public class NetworkMonitorTest { nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED); verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null); + + assertEquals(0, mRegisteredReceivers.size()); } @Test @@ -637,21 +698,25 @@ public class NetworkMonitorTest { private void runPortalNetworkTest() { runNetworkTest(NETWORK_TEST_RESULT_INVALID); + assertEquals(1, mRegisteredReceivers.size()); assertNotNull(mNetworkTestedRedirectUrlCaptor.getValue()); } private void runNotPortalNetworkTest() { runNetworkTest(NETWORK_TEST_RESULT_VALID); + assertEquals(0, mRegisteredReceivers.size()); assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); } private void runFailedNetworkTest() { runNetworkTest(NETWORK_TEST_RESULT_INVALID); + assertEquals(0, mRegisteredReceivers.size()); assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); } private void runPartialConnectivityNetworkTest() { runNetworkTest(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY); + assertEquals(0, mRegisteredReceivers.size()); assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); } @@ -668,6 +733,7 @@ public class NetworkMonitorTest { } catch (RemoteException e) { fail("Unexpected exception: " + e); } + waitForIdle(monitor.getHandler()); return monitor; } diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index bde1b25b914f..55ff591e3a1f 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -43,7 +43,6 @@ import android.content.pm.PackageUserState; import android.net.Uri; import android.os.Bundle; import android.os.Process; -import android.os.RemoteException; import android.os.UserManager; import android.provider.Settings; import android.util.Log; @@ -472,16 +471,6 @@ public class PackageInstallerActivity extends AlertActivity { mOriginatingUid, mOriginatingPackage); switch (appOpMode) { case AppOpsManager.MODE_DEFAULT: - try { - int result = mIpm.checkUidPermission( - Manifest.permission.REQUEST_INSTALL_PACKAGES, mOriginatingUid); - if (result == PackageManager.PERMISSION_GRANTED) { - initiateInstall(); - break; - } - } catch (RemoteException exc) { - Log.e(TAG, "Unable to talk to package manager"); - } mAppOpsManager.setMode(appOpCode, mOriginatingUid, mOriginatingPackage, AppOpsManager.MODE_ERRORED); // fall through diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index d4d0519fcc5f..e02709e10e83 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -83,17 +83,18 @@ import java.util.regex.Pattern; * as needed. */ public class ApplicationsState { - static final String TAG = "ApplicationsState"; - static final boolean DEBUG = false; - static final boolean DEBUG_LOCKING = false; + private static final String TAG = "ApplicationsState"; public static final int SIZE_UNKNOWN = -1; public static final int SIZE_INVALID = -2; - static final Pattern REMOVE_DIACRITICALS_PATTERN + private static final boolean DEBUG = false; + private static final boolean DEBUG_LOCKING = false; + private static final Object sLock = new Object(); + private static final Pattern REMOVE_DIACRITICALS_PATTERN = Pattern.compile("\\p{InCombiningDiacriticalMarks}+"); - static final Object sLock = new Object(); + @VisibleForTesting static ApplicationsState sInstance; public static ApplicationsState getInstance(Application app) { @@ -126,13 +127,12 @@ public class ApplicationsState { // Information about all applications. Synchronize on mEntriesMap // to protect access to these. - final ArrayList<Session> mSessions = new ArrayList<Session>(); - final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>(); + final ArrayList<Session> mSessions = new ArrayList<>(); + final ArrayList<Session> mRebuildingSessions = new ArrayList<>(); private InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges(); // Map: userid => (Map: package name => AppEntry) - final SparseArray<HashMap<String, AppEntry>> mEntriesMap = - new SparseArray<HashMap<String, AppEntry>>(); - final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>(); + final SparseArray<HashMap<String, AppEntry>> mEntriesMap = new SparseArray<>(); + final ArrayList<AppEntry> mAppEntries = new ArrayList<>(); List<ApplicationInfo> mApplications = new ArrayList<>(); long mCurId = 1; UUID mCurComputingSizeUuid; @@ -182,9 +182,10 @@ public class ApplicationsState { mInterestingConfigChanges = interestingConfigChanges; } - public static final @SessionFlags int DEFAULT_SESSION_FLAGS = + @SessionFlags + public static final int DEFAULT_SESSION_FLAGS = FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS | - FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER; + FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER; private ApplicationsState(Application app, IPackageManager iPackageManager) { mContext = app; @@ -194,7 +195,7 @@ public class ApplicationsState { mUm = mContext.getSystemService(UserManager.class); mStats = mContext.getSystemService(StorageStatsManager.class); for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) { - mEntriesMap.put(userId, new HashMap<String, AppEntry>()); + mEntriesMap.put(userId, new HashMap<>()); } mThread = new HandlerThread("ApplicationsState.Loader", @@ -683,9 +684,16 @@ public class ApplicationsState { private AppEntry getEntryLocked(ApplicationInfo info) { int userId = UserHandle.getUserId(info.uid); AppEntry entry = mEntriesMap.get(userId).get(info.packageName); - if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry); + if (DEBUG) { + Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry); + } if (entry == null) { - if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName); + if (mHiddenModules.contains(info.packageName)) { + return null; + } + if (DEBUG) { + Log.i(TAG, "Creating AppEntry for " + info.packageName); + } entry = new AppEntry(mContext, info, mCurId++); mEntriesMap.get(userId).put(info.packageName, entry); mAppEntries.add(entry); @@ -759,7 +767,8 @@ public class ApplicationsState { boolean mRebuildForeground; private final boolean mHasLifecycle; - @SessionFlags private int mFlags = DEFAULT_SESSION_FLAGS; + @SessionFlags + private int mFlags = DEFAULT_SESSION_FLAGS; Session(Callbacks callbacks, Lifecycle lifecycle) { mCallbacks = callbacks; @@ -771,7 +780,8 @@ public class ApplicationsState { } } - public @SessionFlags int getSessionFlags() { + @SessionFlags + public int getSessionFlags() { return mFlags; } @@ -863,25 +873,32 @@ public class ApplicationsState { filter.init(mContext); } - List<AppEntry> apps; + final List<AppEntry> apps; synchronized (mEntriesMap) { apps = new ArrayList<>(mAppEntries); } - ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>(); - if (DEBUG) Log.i(TAG, "Rebuilding..."); - for (int i = 0; i < apps.size(); i++) { - AppEntry entry = apps.get(i); + ArrayList<AppEntry> filteredApps = new ArrayList<>(); + if (DEBUG) { + Log.i(TAG, "Rebuilding..."); + } + for (AppEntry entry : apps) { if (entry != null && (filter == null || filter.filterApp(entry))) { synchronized (mEntriesMap) { - if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock"); + if (DEBUG_LOCKING) { + Log.v(TAG, "rebuild acquired lock"); + } if (comparator != null) { // Only need the label if we are going to be sorting. entry.ensureLabel(mContext); } - if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry); + if (DEBUG) { + Log.i(TAG, "Using " + entry.info.packageName + ": " + entry); + } filteredApps.add(entry); - if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock"); + if (DEBUG_LOCKING) { + Log.v(TAG, "rebuild releasing lock"); + } } } } @@ -1290,7 +1307,8 @@ public class ApplicationsState { } } - private @SessionFlags int getCombinedSessionFlags(List<Session> sessions) { + @SessionFlags + private int getCombinedSessionFlags(List<Session> sessions) { synchronized (mEntriesMap) { int flags = 0; for (Session session : sessions) { @@ -1601,7 +1619,7 @@ public class ApplicationsState { } if (object1.info != null && object2.info != null) { compareResult = - sCollator.compare(object1.info.packageName, object2.info.packageName); + sCollator.compare(object1.info.packageName, object2.info.packageName); if (compareResult != 0) { return compareResult; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index 46e9129d9cf8..0d972c5e2e0e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -28,6 +28,8 @@ public class BluetoothUtils { public static final boolean V = false; // verbose logging public static final boolean D = true; // regular logging + public static final int META_INT_ERROR = -1; + private static ErrorListener sErrorListener; public static int getConnectionStateSummary(int connectionState) { @@ -133,20 +135,16 @@ public class BluetoothUtils { final Pair<Drawable, String> pair = BluetoothUtils.getBtClassDrawableWithDescription( context, cachedDevice); final BluetoothDevice bluetoothDevice = cachedDevice.getDevice(); - final boolean untetheredHeadset = bluetoothDevice != null - ? Boolean.parseBoolean(bluetoothDevice.getMetadata( - BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)) - : false; + final boolean untetheredHeadset = getBooleanMetaData( + bluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET); final int iconSize = context.getResources().getDimensionPixelSize( R.dimen.bt_nearby_icon_size); final Resources resources = context.getResources(); // Deal with untethered headset if (untetheredHeadset) { - final String uriString = bluetoothDevice != null - ? bluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON) - : null; - final Uri iconUri = uriString != null ? Uri.parse(uriString) : null; + final Uri iconUri = getUriMetaData(bluetoothDevice, + BluetoothDevice.METADATA_MAIN_ICON); if (iconUri != null) { try { context.getContentResolver().takePersistableUriPermission(iconUri, @@ -194,4 +192,77 @@ public class BluetoothUtils { return adaptiveIcon; } + + /** + * Get boolean Bluetooth metadata + * + * @param bluetoothDevice the BluetoothDevice to get metadata + * @param key key value within the list of BluetoothDevice.METADATA_* + * @return the boolean metdata + */ + public static boolean getBooleanMetaData(BluetoothDevice bluetoothDevice, int key) { + if (bluetoothDevice == null) { + return false; + } + final byte[] data = bluetoothDevice.getMetadata(key); + if (data == null) { + return false; + } + return Boolean.parseBoolean(new String(data)); + } + + /** + * Get String Bluetooth metadata + * + * @param bluetoothDevice the BluetoothDevice to get metadata + * @param key key value within the list of BluetoothDevice.METADATA_* + * @return the String metdata + */ + public static String getStringMetaData(BluetoothDevice bluetoothDevice, int key) { + if (bluetoothDevice == null) { + return null; + } + final byte[] data = bluetoothDevice.getMetadata(key); + if (data == null) { + return null; + } + return new String(data); + } + + /** + * Get integer Bluetooth metadata + * + * @param bluetoothDevice the BluetoothDevice to get metadata + * @param key key value within the list of BluetoothDevice.METADATA_* + * @return the int metdata + */ + public static int getIntMetaData(BluetoothDevice bluetoothDevice, int key) { + if (bluetoothDevice == null) { + return META_INT_ERROR; + } + final byte[] data = bluetoothDevice.getMetadata(key); + if (data == null) { + return META_INT_ERROR; + } + try { + return Integer.parseInt(new String(data)); + } catch (NumberFormatException e) { + return META_INT_ERROR; + } + } + + /** + * Get URI Bluetooth metadata + * + * @param bluetoothDevice the BluetoothDevice to get metadata + * @param key key value within the list of BluetoothDevice.METADATA_* + * @return the URI metdata + */ + public static Uri getUriMetaData(BluetoothDevice bluetoothDevice, int key) { + String data = getStringMetaData(bluetoothDevice, key); + if (data == null) { + return null; + } + return Uri.parse(data); + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 2405666af500..ff34578d0fea 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -881,16 +881,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> //when profile is connected, information would be available if (profileConnected) { // Update Meta data for connected device - if (Boolean.parseBoolean( - mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))) { - try { - leftBattery = Integer.parseInt( - mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)); - rightBattery = Integer.parseInt(mDevice.getMetadata( - BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY)); - } catch (NumberFormatException e) { - Log.d(TAG, "Parse error for unthethered battery level."); - } + if (BluetoothUtils.getBooleanMetaData( + mDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) { + leftBattery = BluetoothUtils.getIntMetaData(mDevice, + BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY); + rightBattery = BluetoothUtils.getIntMetaData(mDevice, + BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY); } // Set default string with battery level in device connected situation. diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java index 530c73a2448b..fb5c16b92930 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java @@ -19,6 +19,7 @@ package com.android.settingslib.fuelgauge; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import android.os.PowerManager; import android.provider.Settings.Global; import android.provider.Settings.Secure; @@ -33,7 +34,25 @@ import android.util.Slog; public class BatterySaverUtils { private static final String TAG = "BatterySaverUtils"; - public static final String EXTRA_CONFIRM_ONLY = "extra_confirm_only"; + /** + * When set to "true" the notification will be a generic confirm message instead of asking the + * user if they want to turn on battery saver. If set to false the dialog will specifically + * talk about turning on battery saver and provide a button for taking the action. + */ + public static final String EXTRA_CONFIRM_TEXT_ONLY = "extra_confirm_only"; + /** + * Ignored if {@link #EXTRA_CONFIRM_TEXT_ONLY} is "false". Can be set to any of the values in + * {@link PowerManager.AutoPowerSaveModeTriggers}. If set the dialog will set the power + * save mode trigger to the specified value after the user acknowledges the trigger. + */ + public static final String EXTRA_POWER_SAVE_MODE_TRIGGER = "extra_power_save_mode_trigger"; + /** + * Ignored if {@link #EXTRA_CONFIRM_TEXT_ONLY} is "false". can be set to any value between + * 0-100 that will be used if {@link #EXTRA_POWER_SAVE_MODE_TRIGGER} is + * {@link PowerManager#POWER_SAVE_MODE_TRIGGER_PERCENTAGE}. + */ + public static final String EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL = + "extra_power_save_mode_trigger_level"; private BatterySaverUtils() { } @@ -98,7 +117,10 @@ public class BatterySaverUtils { } final ContentResolver cr = context.getContentResolver(); - if (enable && needFirstTimeWarning && maybeShowBatterySaverConfirmation(context, false)) { + final Bundle confirmationExtras = new Bundle(1); + confirmationExtras.putBoolean(EXTRA_CONFIRM_TEXT_ONLY, false); + if (enable && needFirstTimeWarning + && maybeShowBatterySaverConfirmation(context, confirmationExtras)) { return false; } if (enable && !needFirstTimeWarning) { @@ -118,7 +140,7 @@ public class BatterySaverUtils { && Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0 && Secure.getInt(cr, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) { - showAutoBatterySaverSuggestion(context, false); + showAutoBatterySaverSuggestion(context, confirmationExtras); } } @@ -129,34 +151,36 @@ public class BatterySaverUtils { /** * Shows the battery saver confirmation warning if it hasn't been acknowledged by the user in - * the past before. When confirmOnly is true, the dialog will have generic info about battery - * saver but will only update that the user has been shown the notification and take no - * further action. if confirmOnly is false it will show a more specific version of the dialog - * that toggles battery saver when acknowledged + * the past before. Various extras can be provided that will change the behavior of this + * notification as well as the ui for it. * @param context A valid context - * @param confirmOnly Whether to show the actionless generic dialog (true) or the specific one - * that toggles battery saver (false) + * @param extras Any extras to include in the intent to trigger this confirmation that will + * help the system disambiguate what to show/do + * * @return True if it showed the notification because it has not been previously acknowledged. + * @see #EXTRA_CONFIRM_TEXT_ONLY + * @see #EXTRA_POWER_SAVE_MODE_TRIGGER + * @see #EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL */ - public static boolean maybeShowBatterySaverConfirmation(Context context, boolean confirmOnly) { + public static boolean maybeShowBatterySaverConfirmation(Context context, Bundle extras) { if (Secure.getInt(context.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 0) != 0) { return false; // Already shown. } context.sendBroadcast( - getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION, confirmOnly)); + getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION, extras)); return true; } - private static void showAutoBatterySaverSuggestion(Context context, boolean confirmOnly) { - context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION, confirmOnly)); + private static void showAutoBatterySaverSuggestion(Context context, Bundle extras) { + context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION, extras)); } - private static Intent getSystemUiBroadcast(String action, boolean confirmOnly) { + private static Intent getSystemUiBroadcast(String action, Bundle extras) { final Intent i = new Intent(action); i.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); i.setPackage(SYSUI_PACKAGE); - i.putExtra(EXTRA_CONFIRM_ONLY, confirmOnly); + i.putExtras(extras); return i; } diff --git a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java index 74057be8434b..ff40d8e00603 100644 --- a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java +++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java @@ -20,14 +20,12 @@ import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; -import android.graphics.drawable.Drawable; import android.location.SettingInjectorService; import android.os.Bundle; import android.os.Handler; @@ -37,9 +35,9 @@ import android.os.Messenger; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.AttributeSet; -import android.util.IconDrawableFactory; import android.util.Log; import android.util.Xml; @@ -56,8 +54,8 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashSet; -import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -157,22 +155,8 @@ public class SettingsInjector { * Adds the InjectedSetting information to a Preference object */ private void populatePreference(Preference preference, InjectedSetting setting) { - final PackageManager pm = mContext.getPackageManager(); - Drawable appIcon = null; - try { - final PackageItemInfo itemInfo = new PackageItemInfo(); - itemInfo.icon = setting.iconId; - itemInfo.packageName = setting.packageName; - final ApplicationInfo appInfo = pm.getApplicationInfo(setting.packageName, - PackageManager.GET_META_DATA); - appIcon = IconDrawableFactory.newInstance(mContext) - .getBadgedIcon(itemInfo, appInfo, setting.mUserHandle.getIdentifier()); - } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "Can't get ApplicationInfo for " + setting.packageName, e); - } preference.setTitle(setting.title); preference.setSummary(R.string.loading_injected_setting_summary); - preference.setIcon(appIcon); preference.setOnPreferenceClickListener(new ServiceSettingClickedListener(setting)); } @@ -182,13 +166,15 @@ public class SettingsInjector { * @param profileId Identifier of the user/profile to obtain the injected settings for or * UserHandle.USER_CURRENT for all profiles associated with current user. */ - public List<Preference> getInjectedSettings(Context prefContext, final int profileId) { + public Map<Integer, List<Preference>> getInjectedSettings(Context prefContext, + final int profileId) { final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); final List<UserHandle> profiles = um.getUserProfiles(); - ArrayList<Preference> prefs = new ArrayList<>(); + final ArrayMap<Integer, List<Preference>> result = new ArrayMap<>(); mSettings.clear(); for (UserHandle userHandle : profiles) { if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) { + final List<Preference> prefs = new ArrayList<>(); Iterable<InjectedSetting> settings = getSettings(userHandle); for (InjectedSetting setting : settings) { Preference preference = createPreference(prefContext, setting); @@ -196,12 +182,14 @@ public class SettingsInjector { prefs.add(preference); mSettings.add(new Setting(setting, preference)); } + if (!prefs.isEmpty()) { + result.put(userHandle.getIdentifier(), prefs); + } } } reloadStatusMessages(); - - return prefs; + return result; } /** @@ -303,28 +291,6 @@ public class SettingsInjector { } /** - * Checks wheteher there is any preference that other apps have injected. - * - * @param profileId Identifier of the user/profile to obtain the injected settings for or - * UserHandle.USER_CURRENT for all profiles associated with current user. - */ - public boolean hasInjectedSettings(final int profileId) { - final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - final List<UserHandle> profiles = um.getUserProfiles(); - final int profileCount = profiles.size(); - for (int i = 0; i < profileCount; ++i) { - final UserHandle userHandle = profiles.get(i); - if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) { - Iterable<InjectedSetting> settings = getSettings(userHandle); - for (InjectedSetting setting : settings) { - return true; - } - } - } - return false; - } - - /** * Reloads the status messages for all the preference items. */ public void reloadStatusMessages() { diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java index 2711e3175957..3a53d29f7618 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java @@ -22,6 +22,7 @@ import android.graphics.drawable.Drawable; import android.util.Log; import android.util.Pair; +import com.android.settingslib.R; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -47,7 +48,9 @@ public class BluetoothMediaDevice extends MediaDevice { @Override public String getSummary() { - return mCachedDevice.getConnectionSummary(); + return isConnected() || mCachedDevice.isBusy() + ? mCachedDevice.getConnectionSummary() + : mContext.getString(R.string.bluetooth_disconnected); } @Override diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index b228cf7c10c6..3a95852c0ba9 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -25,6 +25,7 @@ import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.util.Pair; import com.android.settingslib.widget.AdaptiveIcon; @@ -47,6 +48,9 @@ public class BluetoothUtilsTest { private BluetoothDevice mBluetoothDevice; private Context mContext; + private static final String STRING_METADATA = "string_metadata"; + private static final String BOOL_METADATA = "true"; + private static final String INT_METADATA = "25"; @Before public void setUp() { @@ -78,7 +82,7 @@ public class BluetoothUtilsTest { @Test public void getBtRainbowDrawableWithDescription_normalHeadset_returnAdaptiveIcon() { when(mBluetoothDevice.getMetadata( - BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn("false"); + BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn("false".getBytes()); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); when(mCachedBluetoothDevice.getAddress()).thenReturn("1f:aa:bb"); @@ -86,4 +90,64 @@ public class BluetoothUtilsTest { RuntimeEnvironment.application, mCachedBluetoothDevice).first).isInstanceOf(AdaptiveIcon.class); } -}
\ No newline at end of file + + @Test + public void getStringMetaData_hasMetaData_getCorrectMetaData() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON)).thenReturn( + STRING_METADATA.getBytes()); + + assertThat(BluetoothUtils.getStringMetaData(mBluetoothDevice, + BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON)).isEqualTo(STRING_METADATA); + } + + @Test + public void getIntMetaData_hasMetaData_getCorrectMetaData() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn( + INT_METADATA.getBytes()); + + assertThat(BluetoothUtils.getIntMetaData(mBluetoothDevice, + BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)) + .isEqualTo(Integer.parseInt(INT_METADATA)); + } + + @Test + public void getIntMetaData_invalidMetaData_getErrorCode() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(null); + + assertThat(BluetoothUtils.getIntMetaData(mBluetoothDevice, + BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON)) + .isEqualTo(BluetoothUtils.META_INT_ERROR); + } + + @Test + public void getBooleanMetaData_hasMetaData_getCorrectMetaData() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( + BOOL_METADATA.getBytes()); + + assertThat(BluetoothUtils.getBooleanMetaData(mBluetoothDevice, + BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).isEqualTo(true); + } + + @Test + public void getUriMetaData_hasMetaData_getCorrectMetaData() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_MAIN_ICON)).thenReturn( + STRING_METADATA.getBytes()); + + assertThat(BluetoothUtils.getUriMetaData(mBluetoothDevice, + BluetoothDevice.METADATA_MAIN_ICON)).isEqualTo(Uri.parse(STRING_METADATA)); + } + + @Test + public void getUriMetaData_nullMetaData_getNullUri() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_MAIN_ICON)).thenReturn(null); + + assertThat(BluetoothUtils.getUriMetaData(mBluetoothDevice, + BluetoothDevice.METADATA_MAIN_ICON)).isNull(); + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index 79b84b9e5829..c0a1f11a0a87 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -455,12 +455,12 @@ public class CachedBluetoothDeviceTest { updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED); when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID); - when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn( - "true"); - when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn( - TWS_BATTERY_LEFT); - when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY)).thenReturn( - TWS_BATTERY_RIGHT); + when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( + "true".getBytes()); + when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn( + TWS_BATTERY_LEFT.getBytes()); + when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn( + TWS_BATTERY_RIGHT.getBytes()); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( "Active, L: 15% battery, R: 25% battery"); @@ -472,12 +472,12 @@ public class CachedBluetoothDeviceTest { updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED); updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED); when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); - when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET)).thenReturn( - "true"); - when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn( - TWS_BATTERY_LEFT); - when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY)).thenReturn( - TWS_BATTERY_RIGHT); + when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( + "true".getBytes()); + when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn( + TWS_BATTERY_LEFT.getBytes()); + when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn( + TWS_BATTERY_RIGHT.getBytes()); assertThat(mCachedDevice.getConnectionSummary()).isEqualTo( "L: 15% battery, R: 25% battery"); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 3a9a99385c28..314b74a69147 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -154,6 +154,7 @@ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> <!-- Permission needed to rename bugreport notifications (so they're not shown as Shell) --> <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" /> + <uses-permission android:name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" /> <!-- Permission needed to hold a wakelock in dumpstate.cpp (drop_root_user()) --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- Permission needed to enable/disable overlays --> diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java index cec97ab49954..79c691cf45e1 100644 --- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/RecentsActivity.java @@ -87,13 +87,13 @@ import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent; import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent; import com.android.systemui.recents.events.ui.focus.NavigateTaskViewEvent.Direction; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.recents.utilities.Utilities; import com.android.systemui.recents.model.RecentsTaskLoadPlan; import com.android.systemui.recents.model.RecentsTaskLoader; -import com.android.systemui.shared.recents.model.Task; import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.recents.utilities.Utilities; import com.android.systemui.recents.views.RecentsView; import com.android.systemui.recents.views.SystemBarScrimViews; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import java.io.FileDescriptor; @@ -370,8 +370,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY); // Getting system scrim colors ignoring wallpaper visibility since it should never be grey. - ColorExtractor.GradientColors systemColors = mColorExtractor.getColors( - ColorExtractor.TYPE_DARK, WallpaperManager.FLAG_SYSTEM, true); + ColorExtractor.GradientColors systemColors = mColorExtractor.getNeutralColors(); // We don't want to interpolate colors because we're defining the initial state. // Gradient should be set/ready when you open "Recents". mRecentsView.setScrimColors(systemColors, false); @@ -397,9 +396,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { // Recents doesn't care about the wallpaper being visible or not, it always // wants to scrim with wallpaper colors - ColorExtractor.GradientColors colors = mColorExtractor.getColors( - WallpaperManager.FLAG_SYSTEM, - ColorExtractor.TYPE_DARK, true /* ignoreVis */); + ColorExtractor.GradientColors colors = mColorExtractor.getNeutralColors(); boolean darkText = colors.supportsDarkText(); if (darkText != mUsingDarkText) { mUsingDarkText = darkText; diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java index 8723fb9ea7c8..e60ffba435ff 100644 --- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java @@ -34,7 +34,6 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; -import android.os.IRemoteCallback; import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; @@ -50,7 +49,7 @@ import android.widget.FrameLayout; import android.widget.TextView; import com.android.internal.colorextraction.ColorExtractor; -import com.android.internal.colorextraction.drawable.GradientDrawable; +import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.Utils; @@ -87,9 +86,9 @@ import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.shared.recents.model.Task; import com.android.systemui.recents.model.TaskStack; import com.android.systemui.recents.utilities.Utilities; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat; import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture; import com.android.systemui.shared.recents.view.RecentsTransition; @@ -134,7 +133,7 @@ public class RecentsView extends FrameLayout { private int mDividerSize; private float mBusynessFactor; - private GradientDrawable mBackgroundScrim; + private ScrimDrawable mBackgroundScrim; private ColorDrawable mMultiWindowBackgroundScrim; private ValueAnimator mBackgroundScrimAnimator; private Point mTmpDisplaySize = new Point(); @@ -172,7 +171,7 @@ public class RecentsView extends FrameLayout { mDividerSize = ssp.getDockedDividerSize(context); mTouchHandler = new RecentsViewTouchHandler(this); mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f); - mBackgroundScrim = new GradientDrawable(context); + mBackgroundScrim = new ScrimDrawable(); mMultiWindowBackgroundScrim = new ColorDrawable(); LayoutInflater inflater = LayoutInflater.from(context); @@ -395,7 +394,7 @@ public class RecentsView extends FrameLayout { * @param animated Interpolate colors if true. */ public void setScrimColors(ColorExtractor.GradientColors scrimColors, boolean animated) { - mBackgroundScrim.setColors(scrimColors, animated); + mBackgroundScrim.setColor(scrimColors.getMainColor(), animated); int alpha = mMultiWindowBackgroundScrim.getAlpha(); mMultiWindowBackgroundScrim.setColor(scrimColors.getMainColor()); mMultiWindowBackgroundScrim.setAlpha(alpha); @@ -467,7 +466,6 @@ public class RecentsView extends FrameLayout { // Needs to know the screen size since the gradient never scales up or down // even when bounds change. mContext.getDisplay().getRealSize(mTmpDisplaySize); - mBackgroundScrim.setScreenSize(mTmpDisplaySize.x, mTmpDisplaySize.y); mBackgroundScrim.setBounds(left, top, right, bottom); mMultiWindowBackgroundScrim.setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y); diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java index fbd863dd2470..bc6547f6898f 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java @@ -59,14 +59,16 @@ public interface SensorManagerPlugin extends Plugin { public static final int TYPE_WAKE_DISPLAY = 2; public static final int TYPE_SWIPE = 3; - int mType; + private int mType; + public Sensor(int type) { + mType = type; + } public int getType() { return mType; } - - public Sensor(int type) { - mType = type; + public String toString() { + return "{PluginSensor type=\"" + mType + "\"}"; } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java index 42600c157387..834f4fc75dce 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java @@ -117,6 +117,7 @@ public interface QSTile { public String expandedAccessibilityClassName; public SlashState slash; public boolean handlesLongClick = true; + public boolean showRippleEffect = true; public boolean copyTo(State other) { if (other == null) throw new IllegalArgumentException(); @@ -135,7 +136,8 @@ public interface QSTile { || !Objects.equals(other.isTransient, isTransient) || !Objects.equals(other.dualTarget, dualTarget) || !Objects.equals(other.slash, slash) - || !Objects.equals(other.handlesLongClick, handlesLongClick); + || !Objects.equals(other.handlesLongClick, handlesLongClick) + || !Objects.equals(other.showRippleEffect, showRippleEffect); other.icon = icon; other.iconSupplier = iconSupplier; other.label = label; @@ -149,6 +151,7 @@ public interface QSTile { other.isTransient = isTransient; other.slash = slash != null ? slash.copy() : null; other.handlesLongClick = handlesLongClick; + other.showRippleEffect = showRippleEffect; return changed; } diff --git a/packages/SystemUI/res-keyguard/layout/type_aod_clock.xml b/packages/SystemUI/res-keyguard/layout/type_aod_clock.xml deleted file mode 100644 index 28ff5a253317..000000000000 --- a/packages/SystemUI/res-keyguard/layout/type_aod_clock.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2019 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> -<com.android.keyguard.clock.ClockLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - > - <include layout="@layout/typographic_clock" /> -</com.android.keyguard.clock.ClockLayout> diff --git a/packages/SystemUI/res-keyguard/layout/typographic_clock.xml b/packages/SystemUI/res-keyguard/layout/typographic_clock.xml deleted file mode 100644 index 73bb4b9a0fc9..000000000000 --- a/packages/SystemUI/res-keyguard/layout/typographic_clock.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2019 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - --> -<com.android.keyguard.clock.TypographicClock - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/type_clock" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingStart="50dp" - android:textAlignment="viewStart" - style="@style/widget_big" - android:textSize="40dp" - /> diff --git a/packages/SystemUI/res-keyguard/values-ml/strings.xml b/packages/SystemUI/res-keyguard/values-ml/strings.xml index d07caa51a61f..6836f1e33776 100644 --- a/packages/SystemUI/res-keyguard/values-ml/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ml/strings.xml @@ -169,13 +169,66 @@ <item msgid="2233497913571137419">"പത്ത്"</item> <item msgid="5621554266768657830">"പതിനൊന്ന്"</item> </string-array> - <!-- no translation found for type_clock_minutes:1 (2091812961809760681) --> - <!-- no translation found for type_clock_minutes:2 (1496435384877290709) --> - <!-- no translation found for type_clock_minutes:3 (881846472976674129) --> - <!-- no translation found for type_clock_minutes:4 (2784477043911540584) --> - <!-- no translation found for type_clock_minutes:5 (1610928853656700614) --> - <!-- no translation found for type_clock_minutes:6 (2317806244043886658) --> - <!-- no translation found for type_clock_minutes:7 (3318687539120971327) --> - <!-- no translation found for type_clock_minutes:8 (5701600693712102348) --> - <!-- no translation found for type_clock_minutes:9 (3247381605947372495) --> + <string-array name="type_clock_minutes"> + <item msgid="8322049385467207985">"മണി"</item> + <item msgid="2091812961809760681">"ഓ ഒരു മിനിറ്റ്"</item> + <item msgid="1496435384877290709">"ഓ രണ്ട് മിനിറ്റ്"</item> + <item msgid="881846472976674129">"ഓ മൂന്ന് മിനിറ്റ്"</item> + <item msgid="2784477043911540584">"ഓ നാലു മിനിറ്റ്"</item> + <item msgid="1610928853656700614">"ഓ അഞ്ച് മിനിറ്റ്"</item> + <item msgid="2317806244043886658">"ഓ ആറ് മിനിറ്റ്"</item> + <item msgid="3318687539120971327">"ഓ ഏഴ് മിനിറ്റ്"</item> + <item msgid="5701600693712102348">"ഓ എട്ട് മിനിറ്റ്"</item> + <item msgid="3247381605947372495">"ഓ ഒമ്പത് മിനിറ്റ്"</item> + <item msgid="3508406095411245038">"പത്ത്"</item> + <item msgid="7161996337755311711">"പതിനൊന്ന്"</item> + <item msgid="4044549963329624197">"പന്ത്രണ്ട്"</item> + <item msgid="333373157917379088">"പതിമൂന്ന്"</item> + <item msgid="2631202907124819385">"പതിനാല്"</item> + <item msgid="6472396076858033453">"പതിനഞ്ച്"</item> + <item msgid="8656981856181581643">"പതിനാറ്"</item> + <item msgid="7289026608562030619">"പതിനേഴ്"</item> + <item msgid="3881477602692646573">"പതിനെട്ട്"</item> + <item msgid="3358129827772984226">"പത്തൊമ്പത്"</item> + <item msgid="3308575407402865807">"ഇരുപത്"</item> + <item msgid="5346560955382229629">"ഇരുപത്തിയൊന്ന്\n"</item> + <item msgid="226750304761473436">"ഇരുപത്തിരണ്ട്\n"</item> + <item msgid="616811325336838734">"ഇരുപത്തിമൂന്ന്\n"</item> + <item msgid="616346116869053440">"ഇരുപത്തിനാല്\n"</item> + <item msgid="4642996410384042830">"ഇരുപത്തിയഞ്ച്\n"</item> + <item msgid="7506092849993571465">"ഇരുപത്തിയാറ്\n"</item> + <item msgid="1915078191101042031">"ഇരുപത്തിയേഴ്\n"</item> + <item msgid="4292378641900520252">"ഇരുപത്തിയെട്ട്\n"</item> + <item msgid="5339513901773103696">"ഇരുപത്തിയൊമ്പത്\n"</item> + <item msgid="3574673250891657607">"മുപ്പത്"</item> + <item msgid="5796923836589110940">"മുപ്പത്തിയൊന്ന്\n"</item> + <item msgid="5859323597571702052">"മുപ്പത്തിരണ്ട്\n"</item> + <item msgid="5133326723148876507">"മുപ്പത്തിമൂന്ന്\n"</item> + <item msgid="2693999494655663096">"മുപ്പത്തിനാല്\n"</item> + <item msgid="3316754944962836197">"അമ്പത്തിയഞ്ച്\n"</item> + <item msgid="816891008836796723">"മുപ്പത്തിയാറ്\n"</item> + <item msgid="9158890488666520078">"മുപ്പത്തിയേഴ്\n"</item> + <item msgid="1894769703213894011">"മുപ്പത്തിയെട്ട്\n"</item> + <item msgid="5638820345598572399">"മുപ്പത്തിയൊമ്പത്\n"</item> + <item msgid="8838304023017895439">"നാൽപത്"</item> + <item msgid="1834742948932559597">"നാൽപ്പത്തിയൊന്ന്\n"</item> + <item msgid="6573707308847773944">"നാൽപത്തിരണ്ട്\n"</item> + <item msgid="2450149950652678001">"നാൽപ്പത്തിമൂന്ന്\n"</item> + <item msgid="2874667401318178036">"നാൽപത്തിനാല്\n"</item> + <item msgid="3391101532763048862">"നാൽപത്തിയഞ്ച്\n"</item> + <item msgid="1671489330863254362">"നാൽപ്പത്തിയാറ്\n"</item> + <item msgid="5916017359554531038">"നാൽപത്തിയേഴ്\n"</item> + <item msgid="8205413177993059967">"നാൽപത്തിയെട്ട്\n"</item> + <item msgid="6607867415142171302">"നാൽപത്തിയൊമ്പത്\n"</item> + <item msgid="8358850748472089162">"അമ്പത്"</item> + <item msgid="3551313125255080234">"അമ്പത്തിയൊന്ന്\n"</item> + <item msgid="1559678130725716542">"അമ്പത്തിരണ്ട്\n"</item> + <item msgid="431441994725492377">"അമ്പത്തിമൂന്ന്\n"</item> + <item msgid="6345774640539623024">"അമ്പത്തിനാല്\n"</item> + <item msgid="8018192990793931120">"അമ്പത്തിയഞ്ച്\n"</item> + <item msgid="6187650843754604534">"അമ്പത്തിയാറ്\n"</item> + <item msgid="8727240174015993259">"അമ്പത്തിയേഴ്\n"</item> + <item msgid="848339003778952950">"അമ്പത്തിയെട്ട്\n"</item> + <item msgid="5798985802835423618">"അമ്പത്തിയൊമ്പത്\n"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml index bfceb15099ba..6baa8c43cf82 100644 --- a/packages/SystemUI/res-keyguard/values-th/strings.xml +++ b/packages/SystemUI/res-keyguard/values-th/strings.xml @@ -69,7 +69,7 @@ <item quantity="other">ลองอีกครั้งใน <xliff:g id="NUMBER">%d</xliff:g> วินาที</item> <item quantity="one">ลองอีกครั้งใน 1 วินาที</item> </plurals> - <string name="kg_pattern_instructions" msgid="5547646893001491340">"วาดรูปแบบของคุณ"</string> + <string name="kg_pattern_instructions" msgid="5547646893001491340">"ลากรูปแบบของคุณ"</string> <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"ป้อน PIN ของซิม"</string> <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"ป้อน PIN ของซิมสำหรับ \"<xliff:g id="CARRIER">%1$s</xliff:g>\""</string> <string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> ปิดใช้ eSIM เพื่อใช้อุปกรณ์โดยไม่มีบริการมือถือ"</string> diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index 2f2f84a97434..4738887d546e 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -405,106 +405,6 @@ number">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item> </plurals> - <!-- Time displayed on typographic clock face, which displays the time in words. - Example: - - It's - Four - Twenty - Nine - - This string requires two arguments: the first in the hours of the time and - the second is the minutes of the time. The hours string is obtained from - string-array type_clock_hours below and the minutes string is obtained - from string-array type_clock_minutes below. - - [CHAR LIMIT=8] --> - <plurals name="type_clock_header"> - <item quantity="one"><annotation name="color">It\u2019s</annotation>\n^1\n^2</item> - <item quantity="few"><annotation name="color">It\u2019s</annotation>\n^1\n^2</item> - <item quantity="other"><annotation name="color">It\u2019s</annotation>\n^1\n^2</item> - </plurals> - - <!-- Hour displayed in words on the typographic clock face. [CHAR LIMIT=12] --> - <string-array name="type_clock_hours"> - <item>Twelve</item> - <item>One</item> - <item>Two</item> - <item>Three</item> - <item>Four</item> - <item>Five</item> - <item>Six</item> - <item>Seven</item> - <item>Eight</item> - <item>Nine</item> - <item>Ten</item> - <item>Eleven</item> - </string-array> - - <!-- Minutes displayed in words on the typographic clock face. [CHAR LIMIT=20] --> - <string-array name="type_clock_minutes"> - <item>O\u2019Clock</item> - <item>Oh One</item> - <item>Oh Two</item> - <item>Oh Three</item> - <item>Oh Four</item> - <item>Oh Five</item> - <item>Oh Six</item> - <item>Oh Seven</item> - <item>Oh Eight</item> - <item>Oh Nine</item> - <item>Ten</item> - <item>Eleven</item> - <item>Twelve</item> - <item>Thirteen</item> - <item>Fourteen</item> - <item>Fifteen</item> - <item>Sixteen</item> - <item>Seventeen</item> - <item>Eighteen</item> - <item>Nineteen</item> - <item>Twenty</item> - <item>Twenty\nOne</item> - <item>Twenty\nTwo</item> - <item>Twenty\nThree</item> - <item>Twenty\nFour</item> - <item>Twenty\nFive</item> - <item>Twenty\nSix</item> - <item>Twenty\nSeven</item> - <item>Twenty\nEight</item> - <item>Twenty\nNine</item> - <item>Thirty</item> - <item>Thirty\nOne</item> - <item>Thirty\nTwo</item> - <item>Thirty\nThree</item> - <item>Thirty\nFour</item> - <item>Thirty\nFive</item> - <item>Thirty\nSix</item> - <item>Thirty\nSeven</item> - <item>Thirty\nEight</item> - <item>Thirty\nNine</item> - <item>Forty</item> - <item>Forty\nOne</item> - <item>Forty\nTwo</item> - <item>Forty\nThree</item> - <item>Forty\nFour</item> - <item>Forty\nFive</item> - <item>Forty\nSix</item> - <item>Forty\nSeven</item> - <item>Forty\nEight</item> - <item>Forty\nNine</item> - <item>Fifty</item> - <item>Fifty\nOne</item> - <item>Fifty\nTwo</item> - <item>Fifty\nThree</item> - <item>Fifty\nFour</item> - <item>Fifty\nFive</item> - <item>Fifty\nSix</item> - <item>Fifty\nSeven</item> - <item>Fifty\nEight</item> - <item>Fifty\nNine</item> - </string-array> - <!-- Title for default clock face that will appear in the picker app next to a preview image of the clock face. [CHAR LIMIT=8] --> <string name="clock_title_default" translatable="false">Default</string> diff --git a/packages/SystemUI/res/drawable/ic_5g_e_mobiledata.xml b/packages/SystemUI/res/drawable/ic_5g_e_mobiledata.xml new file mode 100644 index 000000000000..fe1bb265880c --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_5g_e_mobiledata.xml @@ -0,0 +1,31 @@ +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="22" + android:viewportHeight="17" + android:width="22dp" + android:height="17dp"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M1.22,8.49l0.43-4.96h4.33v1.17H2.67L2.44,7.41c0.41-0.29,0.85-0.43,1.33-0.43c0.77,0,1.38,0.3,1.83,0.9 s0.66,1.41,0.66,2.43c0,1.03-0.24,1.84-0.72,2.43s-1.14,0.88-1.98,0.88c-0.75,0-1.36-0.24-1.83-0.73s-0.74-1.16-0.81-2.02h1.13 c0.07,0.57,0.23,1,0.49,1.29c0.26,0.29,0.59,0.43,1.01,0.43c0.47,0,0.84-0.2,1.1-0.61c0.26-0.41,0.4-0.96,0.4-1.65 c0-0.65-0.14-1.18-0.43-1.59S3.96,8.11,3.47,8.11c-0.4,0-0.72,0.1-0.96,0.31L2.19,8.75L1.22,8.49z" /> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M14.14,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13c0.56-0.7,1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79c0.54,0.53,0.83,1.28,0.89,2.26h-1.25 c-0.05-0.62-0.22-1.1-0.52-1.45c-0.29-0.35-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7C9.14,5.63,8.96,6.37,8.95,7.4v2.03 c0,1,0.19,1.77,0.57,2.31c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z" /> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M20.96,8.88h-3.52v3.53h4.1v1.07h-5.35V3.52h5.28V4.6h-4.03V7.8h3.52V8.88z" /> + +</vector> diff --git a/packages/SystemUI/res/drawable/ic_sysbar_back.xml b/packages/SystemUI/res/drawable/ic_sysbar_back.xml index 144884349c52..ee402622d52b 100644 --- a/packages/SystemUI/res/drawable/ic_sysbar_back.xml +++ b/packages/SystemUI/res/drawable/ic_sysbar_back.xml @@ -17,6 +17,7 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" + android:autoMirrored="true" android:viewportWidth="28" android:viewportHeight="28"> diff --git a/packages/SystemUI/res/drawable/ic_sysbar_back_quick_step.xml b/packages/SystemUI/res/drawable/ic_sysbar_back_quick_step.xml index 93b2f9c85bd1..442fafcebb84 100644 --- a/packages/SystemUI/res/drawable/ic_sysbar_back_quick_step.xml +++ b/packages/SystemUI/res/drawable/ic_sysbar_back_quick_step.xml @@ -17,6 +17,7 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" + android:autoMirrored="true" android:viewportWidth="28" android:viewportHeight="28"> <path diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 895192a03156..baca541582e9 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index b2e68c21ce43..e3250448f40b 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5ጂ"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5ጂ+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index cda4defa1eba..876609e3d453 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"شبكة الجيل الرابع أو أحدث"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"شبكة 5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"شبكة 5G والأحدث"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 6d09133f6bfa..4b1db9d0c821 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"এলটিই"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"এলটিই+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -277,8 +279,7 @@ <item quantity="one"> ভিতৰত আৰু <xliff:g id="NUMBER_1">%s</xliff:g>টা জাননী আছে।</item> <item quantity="other"> ভিতৰত আৰু <xliff:g id="NUMBER_1">%s</xliff:g>টা জাননী আছে।</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"জাননীৰ ছেটিংসমূহ"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ছেটিংসমূহ"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"আপোনাৰ ফ\'নৰ স্ক্ৰীণ স্বয়ংক্ৰিয়ভাৱে ঘূৰিব৷"</string> @@ -620,30 +621,24 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"এই জাননীবোৰে আপোনাক সতৰ্ক কৰিব"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"আপুনি সাধাৰণতে এই জাননীসমূহ অগ্ৰাহ্য কৰে। \nসেইবোৰ দেখুওৱাই থাকিব লাগিবনে?"</string> <string name="inline_done_button" msgid="492513001558716452">"কৰা হ’ল"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"প্ৰয়োগ কৰক"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"এই জাননীসমূহ দেখুওৱাই থাকিব লাগিবনে?"</string> <string name="inline_stop_button" msgid="4172980096860941033">"জাননী বন্ধ কৰক"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"নিৰৱে ডেলিভাৰ কৰক"</string> <string name="inline_block_button" msgid="8735843688021655065">"অৱৰোধ কৰক"</string> <string name="inline_keep_button" msgid="6665940297019018232">"দেখুওৱাই থাকক"</string> <string name="inline_minimize_button" msgid="966233327974702195">"সৰু কৰক"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"মৃদু"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"নীৰৱ হৈ থাকক"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"বাধা দিয়া"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"সতৰ্ক কৰি থাকক"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"জাননী অফ কৰক"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"এই এপটোৰ জাননী দেখুওৱাই থাকিব লাগিবনে?"</string> <string name="hint_text_block" msgid="3554459167504485284">"অৱৰোধ কৰা জাননী ক’তো দেখা নাযায় বা সেইবোৰে কোনো শব্দ নকৰে। আপুনি ছেটিংসমূহলৈ গৈ জাননীসমূহ অৱৰোধৰ পৰা আঁতৰাব পাৰে৷"</string> <string name="hint_text_silent" msgid="859468056340177016">"নিৰৱ জাননীসমূহ শ্বেডত দেখা যায়, কিন্তু লক স্ক্ৰীণত আৰু বেনাৰ হিচাপে দেখা নাযায় আৰু কোনো শব্দ নকৰে।"</string> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_alert" msgid="2721169810318722524">"এই জাননীবোৰে এটা শব্দ কৰিব আৰু জাননী দেৰাজ, স্থিতি দণ্ড আৰু লক স্ক্ৰীণত দেখা যাব"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"এই জাননীসমূহ বন্ধ কৰিব নোৱাৰি"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"এই ধৰণৰ জাননীবোৰ ইয়াত কনফিগাৰ কৰিব পৰা নাযায়"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ জৰিয়তে"</string> <string name="appops_camera" msgid="8100147441602585776">"এই এপে কেমেৰা ব্য়ৱহাৰ কৰি আছে।"</string> <string name="appops_microphone" msgid="741508267659494555">"এই এপে মাইক্ৰ\'ফ\'ন ব্য়ৱহাৰ কৰি আছে।"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 36c297ed3d64..5354e0d9ade2 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 00f13ccd912b..242db2efcb2e 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -376,7 +377,7 @@ <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Prevucite udesno da biste brzo promenili aplikacije"</string> <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Uključi/isključi pregled"</string> <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjena je"</string> - <string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string> + <string name="expanded_header_battery_charging" msgid="205623198487189724">"Puni se"</string> <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> do kraja punjenja"</string> <string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"Ne puni se"</string> <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Mreža se možda\nnadgleda"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 5ff11d7b344d..396c4be82d71 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 776424414a31..dd6ffb275d68 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 94b32a89fab7..e8d3d3f64947 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -277,8 +279,7 @@ <item quantity="one">ভিতরে আরও <xliff:g id="NUMBER_1">%s</xliff:g>টি বিজ্ঞপ্তি আছে।</item> <item quantity="other">ভিতরে আরও <xliff:g id="NUMBER_1">%s</xliff:g>টি বিজ্ঞপ্তি আছে।</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"বিজ্ঞপ্তির সেটিংস"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> সেটিংস"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"স্ক্রিন অটোমেটিক ঘুরে যাবে৷"</string> @@ -620,30 +621,24 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"এই বিজ্ঞপ্তিগুলি আপনাকে সতর্ক করে দেবে"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"এই বিজ্ঞপ্তিগুলিকে আপনি সাধারণত বাতিল করেন। \nসেগুলি দেখতে চান?"</string> <string name="inline_done_button" msgid="492513001558716452">"হয়ে গেছে"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"প্রয়োগ করুন"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"এই বিজ্ঞপ্তিগুলি পরেও দেখে যেতে চান?"</string> <string name="inline_stop_button" msgid="4172980096860941033">"বিজ্ঞপ্তি বন্ধ করুন"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"সাইলেন্ট মোডে দেখান"</string> <string name="inline_block_button" msgid="8735843688021655065">"ব্লক করুন"</string> <string name="inline_keep_button" msgid="6665940297019018232">"দেখতে থাকুন"</string> <string name="inline_minimize_button" msgid="966233327974702195">"ছোট করে দিন"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"সাইলেন্ট মোডে"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"বিজ্ঞপ্তি মিউট করুন"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"বিরক্তিকর"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"বিজ্ঞপ্তি পান"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"বিজ্ঞপ্তি বন্ধ করুন"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"এই অ্যাপের বিজ্ঞপ্তি পরেও দেখে যেতে চান?"</string> <string name="hint_text_block" msgid="3554459167504485284">"ব্লক করা বিজ্ঞপ্তি কোথাও দেখানো হয় না ও সেটির শব্দ শোনা যায় না। আপনি সেটিংস থেকে বিজ্ঞপ্তি আনব্লক করতে পারেন।"</string> <string name="hint_text_silent" msgid="859468056340177016">"নীরব বিজ্ঞপ্তি শেডে দেখানো হয়, কিন্তু লক স্ক্রিনে দেখানো হয় না। একইসাথে, এটি ব্যানার দেখাতে পারে না বা আওয়াজও করতে পারে না।"</string> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_alert" msgid="2721169810318722524">"আওয়াজ হলেই জানতে পারবেন বিজ্ঞপ্তি এসেছে এবং সেটি বিজ্ঞপ্তি ড্রয়ার, স্ট্যাটাস বার এবং লক স্ক্রিনে দেখা যাবে"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"এই বিজ্ঞপ্তিগুলি বন্ধ করা যাবে না"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"এই সমস্ত বিজ্ঞপ্তিকে এখানে কনফিগার করা যাবে না"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর মাধ্যমে"</string> <string name="appops_camera" msgid="8100147441602585776">"এই অ্যাপটি ক্যামেরা ব্যবহার করছে।"</string> <string name="appops_microphone" msgid="741508267659494555">"এই অ্যাপটি মাইক্রোফোন ব্যবহার করছে।"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index f85ed358552c..a51ba975afd6 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -817,7 +818,7 @@ <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="5755818559638850294">"Zaključavanje ekrana"</string> <string name="pip_phone_expand" msgid="5889780005575693909">"Proširi"</string> - <string name="pip_phone_minimize" msgid="1079119422589131792">"Umanji"</string> + <string name="pip_phone_minimize" msgid="1079119422589131792">"Minimiziraj"</string> <string name="pip_phone_close" msgid="8416647892889710330">"Zatvori"</string> <string name="pip_phone_settings" msgid="8080777499521528521">"Postavke"</string> <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"Povucite prema dolje da odbacite"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index ce683ef52760..fdf51f2b46ea 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -604,7 +605,7 @@ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Vols activar el Bluetooth?"</string> <string name="enable_bluetooth_message" msgid="9106595990708985385">"Per connectar el teclat amb la tauleta, primer has d\'activar el Bluetooth."</string> <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Activa"</string> - <string name="show_silently" msgid="6841966539811264192">"Mostra les notificacions de manera silenciosa"</string> + <string name="show_silently" msgid="6841966539811264192">"Mostra les notificacions en silenci"</string> <string name="block" msgid="2734508760962682611">"Bloqueja totes les notificacions"</string> <string name="do_not_silence" msgid="6878060322594892441">"No silenciïs"</string> <string name="do_not_silence_block" msgid="4070647971382232311">"No silenciïs ni bloquegis"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 1ba20c2b8e3e..924ad5a5d8b9 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index c861c4f8b0de..d347f8e65ad2 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index c185d1cae1b9..fb7628ad03f1 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -840,7 +841,7 @@ <string name="lockscreen_unlock_right" msgid="1529992940510318775">"Rechte Verknüpfung entsperrt außerdem"</string> <string name="lockscreen_none" msgid="4783896034844841821">"Keine"</string> <string name="tuner_launch_app" msgid="1527264114781925348">"<xliff:g id="APP">%1$s</xliff:g> starten"</string> - <string name="tuner_other_apps" msgid="4726596850501162493">"Weitere Apps"</string> + <string name="tuner_other_apps" msgid="4726596850501162493">"Sonstige Apps"</string> <string name="tuner_circle" msgid="2340998864056901350">"Kreis"</string> <string name="tuner_plus" msgid="6792960658533229675">"Plus"</string> <string name="tuner_minus" msgid="4806116839519226809">"Minus"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index c5fc1263a01d..ba5e12273295 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 98d551ec95fc..8ea35affa6ce 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index d2fac3dda42a..92416703443d 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 98d551ec95fc..8ea35affa6ce 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 98d551ec95fc..8ea35affa6ce 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 2f76dd15b18c..f28f761e4c22 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 10f49ed73a01..fdee8a81324e 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 2153ab760808..8e620d19b889 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5G E"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 7df6494dc532..59616ace92dd 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 2c917b67c787..71a15840c2b6 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -38,12 +38,12 @@ <string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"Aktibatu"</string> <string name="battery_saver_start_action" msgid="8187820911065797519">"Aktibatu bateria-aurrezlea"</string> <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Ezarpenak"</string> - <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string> + <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wifia"</string> <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Biratu pantaila automatikoki"</string> <string name="status_bar_settings_mute_label" msgid="554682549917429396">"DESAKTIBATU AUDIOA"</string> <string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"AUTO"</string> <string name="status_bar_settings_notifications" msgid="397146176280905137">"Jakinarazpenak"</string> - <string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetootha konektatu da"</string> + <string name="bluetooth_tethered" msgid="7094101612161133267">"Bluetooth-a konektatu da"</string> <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Konfiguratu idazketa-metodoak"</string> <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Teklatu fisikoa"</string> <string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> atzitzeko baimena eman nahi diozu <xliff:g id="APPLICATION">%1$s</xliff:g> aplikazioari?"</string> @@ -125,8 +125,8 @@ <string name="accessibility_face_dialog_face_icon" msgid="2658119009870383490">"Aurpegiaren ikonoa"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Zoom-bateragarritasunaren botoia."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Handiagotu pantaila txikia."</string> - <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetootha konektatuta."</string> - <string name="accessibility_bluetooth_disconnected" msgid="7416648669976870175">"Bluetootha deskonektatuta."</string> + <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth-a konektatuta."</string> + <string name="accessibility_bluetooth_disconnected" msgid="7416648669976870175">"Bluetooth-a deskonektatuta."</string> <string name="accessibility_no_battery" msgid="358343022352820946">"Ez dago bateriarik."</string> <string name="accessibility_battery_one_bar" msgid="7774887721891057523">"Bateriak barra bat du."</string> <string name="accessibility_battery_two_bars" msgid="8500650438735009973">"Bateriak bi barra ditu."</string> @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -295,8 +296,8 @@ <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Alarmak soilik"</string> <string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"Isiltasun osoa"</string> <string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth-a"</string> - <string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetootha (<xliff:g id="NUMBER">%d</xliff:g> gailu)"</string> - <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetootha desaktibatuta"</string> + <string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth-a (<xliff:g id="NUMBER">%d</xliff:g> gailu)"</string> + <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth-a desaktibatuta"</string> <string name="quick_settings_bluetooth_detail_empty_text" msgid="4910015762433302860">"Ez dago parekatutako gailurik erabilgarri"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="7106697106764717416">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Audioa"</string> @@ -668,7 +669,7 @@ <item quantity="other">%d minutu</item> <item quantity="one">%d minutu</item> </plurals> - <string name="battery_panel_title" msgid="7944156115535366613">"Bateriaren erabilera"</string> + <string name="battery_panel_title" msgid="7944156115535366613">"Bateria-erabilera"</string> <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Bateria-aurrezlea ez dago erabilgarri gailua kargatzen ari denean"</string> <string name="battery_detail_switch_title" msgid="6285872470260795421">"Bateria-aurrezlea"</string> <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Errendimendua eta atzeko planoko datuen erabilera murrizten ditu"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 9a7d1b79355b..e673a49e9157 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 9203a19c86a2..512dc5cc1dac 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 6e0bd3378060..3293f41db529 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index b4cc5bd44b4d..f13cdc3e1420 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 62da3b643dbc..17177d85baa7 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -704,7 +706,7 @@ <string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Volver"</string> <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificacións"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Atallos de teclado"</string> - <string name="keyboard_shortcut_group_system_switch_input" msgid="8413348767825486492">"Cambiar de deseño de teclado"</string> + <string name="keyboard_shortcut_group_system_switch_input" msgid="8413348767825486492">"Cambiar deseño do teclado"</string> <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicacións"</string> <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asistente"</string> <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 42e1fef0b78c..8535a3f0634f 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -277,8 +279,7 @@ <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> વધુ સૂચના અંદર છે.</item> <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> વધુ સૂચના અંદર છે.</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"સૂચનાઓની સેટિંગ્સ"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> સેટિંગ્સ"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"સ્ક્રીન આપમેળે ફરશે."</string> @@ -620,30 +621,24 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"આ બધા નોટિફિકેશન તમને અલર્ટ કરશે"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"તમે સામાન્ય રીતે આ નોટીફિકેશનને છોડી દો છો. \nતેમને બતાવવાનું ચાલુ રાખીએ?"</string> <string name="inline_done_button" msgid="492513001558716452">"થઈ ગયું"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"લાગુ કરો"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"આ નોટિફિકેશન બતાવવાનું ચાલુ રાખીએ?"</string> <string name="inline_stop_button" msgid="4172980096860941033">"નોટિફિકેશન બંધ કરો"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"ચુપચાપ મોકલો"</string> <string name="inline_block_button" msgid="8735843688021655065">"બ્લૉક કરો"</string> <string name="inline_keep_button" msgid="6665940297019018232">"બતાવવાનું ચાલુ રાખો"</string> <string name="inline_minimize_button" msgid="966233327974702195">"નાનું કરો"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"સાઇલન્ટ નોટિફિકેશન"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"સાઇલન્ટ મોડ ચાલુ રાખો"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"વિક્ષેપ કરી શકતા"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"અલર્ટ કરવાનું ચાલુ રાખો"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"નોટિફિકેશન બંધ કરો"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"આ ઍપમાંથી નોટિફિકેશન બતાવવાનું ચાલુ રાખીએ?"</string> <string name="hint_text_block" msgid="3554459167504485284">"બ્લૉક કરેલાં નોટિફિકેશન ક્યાંય દેખાતાં નથી કે અવાજ સંભળાવતાં નથી. તમે સેટિંગમાં જઈને નોટિફિકેશનને અનબ્લૉક કરી શકો છો."</string> <string name="hint_text_silent" msgid="859468056340177016">"સાઇલન્ટ નોટિફિકેશન શેડમાં દેખાય છે, પણ લૉક સ્ક્રીન પર દેખાતાં નથી, બૅનર પ્રસ્તુત કરે છે અથવા ધ્વનિ વગાડે છે."</string> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_alert" msgid="2721169810318722524">"આ બધા નોટિફિકેશન કોઈ સાઉન્ડ વગાડશે અને તે નોટિફિકેશન ડ્રોઅર, સ્ટેટસ બાર તેમજ લૉક સ્ક્રીનમાં પણ દેખાશે"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"આ નોટિફિકેશન બંધ કરી શકશો નહીં"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"નોટિફિકેશનના આ ગ્રૂપની ગોઠવણી અહીં કરી શકાશે નહીં"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> મારફતે"</string> <string name="appops_camera" msgid="8100147441602585776">"આ ઍપ કૅમેરાનો ઉપયોગ કરી રહી છે."</string> <string name="appops_microphone" msgid="741508267659494555">"આ ઍપ માઇક્રોફોનનો ઉપયોગ કરી રહી છે."</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index cf1617968634..7d38dd9873aa 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -26,8 +26,8 @@ <string name="status_bar_latest_events_title" msgid="6594767438577593172">"सूचनाएं"</string> <string name="battery_low_title" msgid="9187898087363540349">"बैटरी जल्दी ही खत्म हो जाएगी"</string> <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> शेष"</string> - <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> बची है, आपके इस्तेमाल करने के तरीके के हिसाब से बैटरी लगभग <xliff:g id="TIME">%2$s</xliff:g> चलेगी"</string> - <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> बची है, बैटरी लगभग <xliff:g id="TIME">%2$s</xliff:g> चलेगी"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> बची है, आपके इस्तेमाल करने के हिसाब से बैटरी करीब <xliff:g id="TIME">%2$s</xliff:g> चलेगी"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> बची है, बैटरी करीब <xliff:g id="TIME">%2$s</xliff:g> चलेगी"</string> <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> बैटरी बची है. बैटरी सेवर चालू है."</string> <string name="invalid_charger" msgid="2741987096648693172">"यूएसबी के ज़रिए चार्ज नहीं किया जा सकता. अपने डिवाइस के साथ मिलने वाले चार्जर का इस्तेमाल करें."</string> <string name="invalid_charger_title" msgid="2836102177577255404">"यूएसबी के ज़रिए चार्ज नहीं किया जा सकता"</string> @@ -175,6 +175,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"एलटीई"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -284,8 +286,7 @@ <item quantity="one">इसमें <xliff:g id="NUMBER_1">%s</xliff:g> और सूचनाएं हैं.</item> <item quantity="other">इसमें <xliff:g id="NUMBER_1">%s</xliff:g> और सूचनाएं हैं.</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"सूचना सेटिंग"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> सेटिंग"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"स्क्रीन स्वचालित रूप से घूमेगी."</string> @@ -397,7 +398,7 @@ <string name="zen_silence_introduction" msgid="3137882381093271568">"इससे अलार्म, संगीत, वीडियो और गेम सहित सभी आवाज़ और कंपन (वाइब्रेशन) रोक दिए जाते हैं."</string> <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string> <string name="speed_bump_explanation" msgid="1288875699658819755">"कम अत्यावश्यक सूचनाएं नीचे दी गई हैं"</string> - <string name="notification_tap_again" msgid="7590196980943943842">"खोलने के लिए पुन: टैप करें"</string> + <string name="notification_tap_again" msgid="7590196980943943842">"खोलने के लिए फिर से टैप करें"</string> <string name="keyguard_unlock" msgid="8043466894212841998">"अनलॉक करने के लिए ऊपर स्वाइप करें"</string> <string name="do_disclosure_generic" msgid="5615898451805157556">"इस डिवाइस का प्रबंधन आपका संगठन करता है"</string> <string name="do_disclosure_with_name" msgid="5640615509915445501">"इस डिवाइस के प्रबंधक <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> हैं"</string> @@ -427,7 +428,7 @@ <string name="guest_exit_guest_dialog_title" msgid="8480693520521766688">"अतिथि को निकालें?"</string> <string name="guest_exit_guest_dialog_message" msgid="4155503224769676625">"इस सत्र के सभी ऐप्स और डेटा को हटा दिया जाएगा."</string> <string name="guest_exit_guest_dialog_remove" msgid="7402231963862520531">"निकालें"</string> - <string name="guest_wipe_session_title" msgid="6419439912885956132">"अतिथि, आपका पुन: स्वागत है!"</string> + <string name="guest_wipe_session_title" msgid="6419439912885956132">"अतिथि, आपका फिर से स्वागत है!"</string> <string name="guest_wipe_session_message" msgid="8476238178270112811">"क्या आप अपना सत्र जारी रखना चाहते हैं?"</string> <string name="guest_wipe_session_wipe" msgid="5065558566939858884">"फिर से शुरू करें"</string> <string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"हां, जारी रखें"</string> @@ -611,7 +612,7 @@ <string name="activity_not_found" msgid="348423244327799974">"ऐप्लिकेशन आपके डिवाइस पर इंस्टॉल नहीं है"</string> <string name="clock_seconds" msgid="7689554147579179507">"घड़ी के सेकंड दिखाएं"</string> <string name="clock_seconds_desc" msgid="6282693067130470675">"स्टेटस बार में सेकंड में समय दिखाएं. इससे बैटरी लाइफ़ पर असर पड़ सकता है."</string> - <string name="qs_rearrange" msgid="8060918697551068765">"त्वरित सेटिंग को पुन: व्यवस्थित करें"</string> + <string name="qs_rearrange" msgid="8060918697551068765">"त्वरित सेटिंग को फिर से व्यवस्थित करें"</string> <string name="show_brightness" msgid="6613930842805942519">"त्वरित सेटिंग में स्क्रीन की रोशनी दिखाएं"</string> <string name="experimental" msgid="6198182315536726162">"प्रयोगात्मक"</string> <string name="enable_bluetooth_title" msgid="5027037706500635269">"ब्लूटूथ चालू करें?"</string> @@ -632,32 +633,26 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ये सूचनाएं आपको अलर्ट करेंगी"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"अाप अक्सर इन सूचनाओं को खारिज कर देते हैं. \nआगे भी इन्हें देखना जारी रखना चाहते हैं?"</string> <string name="inline_done_button" msgid="492513001558716452">"हो गया"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"लागू करें"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"ये सूचनाएं दिखाना जारी रखें?"</string> <string name="inline_stop_button" msgid="4172980096860941033">"सूचनाएं दिखाना बंद करें"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"बिना आवाज़ के भेजें"</string> <string name="inline_block_button" msgid="8735843688021655065">"ब्लॉक करें"</string> <string name="inline_keep_button" msgid="6665940297019018232">"दिखाना जारी रखें"</string> <string name="inline_minimize_button" msgid="966233327974702195">"सूचनाएं छोटी करें"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"बिना आवाज़ के"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"साइलेंट मोड में सूचनाएं पाएं"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"आवाज़ वाली सूचनाएं"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"सूचना देना जारी रखें"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"सूचनाएं बंद करें"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"इस ऐप्लिकेशन से जुड़ी सूचनाएं दिखाना जारी रखें?"</string> <!-- no translation found for hint_text_block (3554459167504485284) --> <skip /> <!-- no translation found for hint_text_silent (859468056340177016) --> <skip /> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_alert" msgid="2721169810318722524">"यह सूचनाएं आवाज़ करेंगी और सूचना की दराज, स्टेटस बार और लॉक स्क्रीन में दिखाई देंगी"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"ये सूचनाएं दिखाया जाना बंद नहीं किया जा सकता"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"सूचनाओं के इस समूह को यहां कॉन्फ़िगर नहीं किया जा सकता"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> के ज़रिए"</string> <string name="appops_camera" msgid="8100147441602585776">"यह ऐप्लिकेशन कैमरे का इस्तेमाल कर रहा है."</string> <string name="appops_microphone" msgid="741508267659494555">"यह ऐप्लिकेशन माइक्रोफ़ोन का इस्तेमाल कर रहा है."</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 7018cfd500b3..c6d9c7509222 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G i više"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 7bc45eaf6408..ef0b84b4660b 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -309,7 +310,7 @@ <string name="accessibility_quick_settings_rotation" msgid="4231661040698488779">"Automatikus képernyőforgatás"</string> <string name="accessibility_quick_settings_rotation_value" msgid="8187398200140760213">"<xliff:g id="ID_1">%s</xliff:g> mód"</string> <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Elforgatás zárolva"</string> - <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Álló"</string> + <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portré"</string> <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Fekvő"</string> <string name="quick_settings_ime_label" msgid="7073463064369468429">"Beviteli módszer"</string> <string name="quick_settings_location_label" msgid="5011327048748762257">"Tartózkodási hely"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index a95bbb145e3f..aaf74b4726f9 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 0f8686d613f6..a816f42bf9de 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -626,7 +627,7 @@ <string name="inline_block_button" msgid="8735843688021655065">"Blokir"</string> <string name="inline_keep_button" msgid="6665940297019018232">"Terus tampilkan"</string> <string name="inline_minimize_button" msgid="966233327974702195">"Perkecil"</string> - <string name="inline_silent_button_silent" msgid="6904727667411781466">"Lembut"</string> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"Senyap"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Tetap nonaktif"</string> <string name="inline_silent_button_alert" msgid="2449191160203602471">"Mengganggu"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Terus beri tahu"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 54787a2d81f5..7a291459905d 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 6c1bf7147210..d304bedc028e 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index a271e6edb12f..f1e5a1ccdaba 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"+4G"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"+LTE"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"+G5"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 261c05efafa7..ebe6dccaa2ab 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 643e9ff47e9c..1b326a1ce03c 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index fcb314cfa8cf..b74aeb18e31a 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -225,8 +227,8 @@ <string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"үнсіз"</string> <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"тек дабылдар"</string> <string name="accessibility_quick_settings_dnd" msgid="5555155552520665891">"Мазаламау."</string> - <string name="accessibility_quick_settings_dnd_changed_off" msgid="2757071272328547807">"Мазаламау режимі өшірілді."</string> - <string name="accessibility_quick_settings_dnd_changed_on" msgid="6808220653747701059">"мазаламау режимі қосылды."</string> + <string name="accessibility_quick_settings_dnd_changed_off" msgid="2757071272328547807">"\"Мазаламау\" режимі өшірілді."</string> + <string name="accessibility_quick_settings_dnd_changed_on" msgid="6808220653747701059">"\"Мазаламау\" режимі қосылды."</string> <string name="accessibility_quick_settings_bluetooth" msgid="6341675755803320038">"Bluetooth."</string> <string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"Bluetooth өшірулі."</string> <string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"Bluetooth қосулы."</string> @@ -441,8 +443,8 @@ <string name="battery_saver_notification_title" msgid="8614079794522291840">"Battery saver қосулы"</string> <string name="battery_saver_notification_text" msgid="820318788126672692">"Өнімділікті және фондық деректерді азайтады"</string> <string name="battery_saver_notification_action_text" msgid="132118784269455533">"Battery saver функциясын өшіру"</string> - <string name="media_projection_dialog_text" msgid="5751657130671431216">"Жазу және трансляциялау кезінде <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> сіз ойнайтын аудиомазмұн және құпия сөздер, төлем ақпараттары, фотосуреттер және хабарлар сияқты кез келген құпия ақпаратты сақтай аласыз."</string> - <string name="media_projection_dialog_title" msgid="8124184308671641248">"Трансляциялау/Жазу кезінде құпия ақпаратты көрсету"</string> + <string name="media_projection_dialog_text" msgid="5751657130671431216">"Жазу және трансляциялау кезінде <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ойнатылған аудиомазмұн, құпия сөздер, төлем мәліметтері, фотосуреттер және хабарлар сияқты кез келген құпия ақпаратты сақтай алады."</string> + <string name="media_projection_dialog_title" msgid="8124184308671641248">"Трансляциялау/жазу кезінде құпия ақпаратты көрсету"</string> <string name="media_projection_remember_text" msgid="3103510882172746752">"Қайта көрсетпеу"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"Барлығын тазалау"</string> <string name="manage_notifications_text" msgid="2386728145475108753">"Басқару"</string> @@ -632,8 +634,8 @@ <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Хабарландырулар келе берсін"</string> <string name="inline_turn_off_notifications" msgid="8635596135532202355">"Хабарландыруларды өшіру"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"Осы қолданбаның хабарландырулары көрсетілсін бе?"</string> - <string name="hint_text_block" msgid="3554459167504485284">"Тыйым салынған хабарландырулар еш жерде көрсетілмейді немесе дыбыс шығармайды. Хабарландыруларды параметрлерден бөгей алмайсыз."</string> - <string name="hint_text_silent" msgid="859468056340177016">"Дыбыссыз хабарландырулар көлеңкеде шығады, бірақ құлыптау экранына шықпайды, баннерде ұсынылады және дыбысты ойнатады."</string> + <string name="hint_text_block" msgid="3554459167504485284">"Тыйым салынған хабарландырулар еш жерде көрсетілмейді немесе дыбыс шығармайды. Тыйымды алу үшін параметрлерге өтіңіз."</string> + <string name="hint_text_silent" msgid="859468056340177016">"Дыбыссыз хабарландырулар көлеңкеде шығады, бірақ құлып экранына шықпайды, баннерде ұсынылады және дыбысты ойнатады."</string> <string name="hint_text_alert" msgid="2721169810318722524">"Бұл дыбыстық хабарландырулар хабарландыру тартпасында, күй жолағында және құлып экранында көрсетіледі."</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"Хабарландыруларды өшіру мүмкін емес"</string> <string name="notification_multichannel_desc" msgid="4695920306092240550">"Мұндай хабарландырулар бұл жерде конфигурацияланбайды."</string> @@ -717,7 +719,7 @@ <string name="tuner_full_zen_title" msgid="4540823317772234308">"Дыбыс деңгейін басқару элементтерімен бірге көрсету"</string> <string name="volume_and_do_not_disturb" msgid="1750270820297253561">"Мазаламау"</string> <string name="volume_dnd_silent" msgid="4363882330723050727">"Дыбыс деңгейі түймелерінің төте жолы"</string> - <string name="volume_up_silent" msgid="7545869833038212815">"Дыбысы арттырылған кезде, мазаламау режимінен шығу"</string> + <string name="volume_up_silent" msgid="7545869833038212815">"Дыбысы арттырылған кезде, \"Мазаламау\" режимінен шығу"</string> <string name="battery" msgid="7498329822413202973">"Батарея"</string> <string name="clock" msgid="7416090374234785905">"Сағат"</string> <string name="headset" msgid="4534219457597457353">"Құлақаспап жинағы"</string> @@ -898,10 +900,10 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"Датчиктер өшірулі"</string> <string name="device_services" msgid="1191212554435440592">"Құрылғы қызметтері"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"Атауы жоқ"</string> - <string name="restart_button_description" msgid="2035077840254950187">"Бұл қолданбаны іске қосу үшін түртіп, толық экранға өтіңіз."</string> + <string name="restart_button_description" msgid="2035077840254950187">"Бұл қолданбаны қайта қосып, толық экранға өту үшін түртіңіз."</string> <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын ашу"</string> <string name="bubbles_settings_button_description" msgid="2970630476657287189">"<xliff:g id="APP_NAME">%1$s</xliff:g> қалқымалы анықтамаларының параметрлері"</string> - <string name="bubbles_prompt" msgid="8807968030159469710">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасында қалқымалы анықтамаларға рұқсат етілсін бе?"</string> + <string name="bubbles_prompt" msgid="8807968030159469710">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасының қалқымалы анықтамаларына рұқсат етілсін бе?"</string> <string name="no_bubbles" msgid="337101288173078247">"Тыйым салу"</string> <string name="yes_bubbles" msgid="668809525728633841">"Рұқсат беру"</string> <string name="ask_me_later_bubbles" msgid="2147688438402939029">"Кейінірек сұралсын"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 0d19e1f7e004..11764e1c25aa 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 5a6a52cfaab0..04c7bd2d813f 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -277,8 +279,7 @@ <item quantity="one"><xliff:g id="NUMBER_1">%s</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಅಧಿಸೂಚನೆಗಳು ಒಳಗಿವೆ.</item> <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಅಧಿಸೂಚನೆಗಳು ಒಳಗಿವೆ.</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"ಅಧಿಸೂಚನೆ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"ಪರದೆಯು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ತಿರುಗುತ್ತದೆ."</string> @@ -620,30 +621,24 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ಈ ಸೂಚನೆಗಳು ನಿಮ್ಮನ್ನು ಎಚ್ಚರಿಸುತ್ತವೆ"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"ನೀವು ಸಾಮಾನ್ಯವಾಗಿ ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ವಜಾಗೊಳಿಸಿದ್ದೀರಿ. \nಅವುಗಳನ್ನು ತೋರಿಸುತ್ತಲೇ ಇರಬೇಕೆ?"</string> <string name="inline_done_button" msgid="492513001558716452">"ಪೂರ್ಣಗೊಂಡಿದೆ"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"ಅನ್ವಯಿಸಿ"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸುತ್ತಲೇ ಇರಬೇಕೆ?"</string> <string name="inline_stop_button" msgid="4172980096860941033">"ಅಧಿಸೂಚನೆಗಳನ್ನು ನಿಲ್ಲಿಸಿ"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"ಮೌನವಾಗಿ ವಿತರಿಸಿ"</string> <string name="inline_block_button" msgid="8735843688021655065">"ನಿರ್ಬಂಧಿಸಿ"</string> <string name="inline_keep_button" msgid="6665940297019018232">"ತೋರಿಸುತ್ತಲಿರಿ"</string> <string name="inline_minimize_button" msgid="966233327974702195">"ಕಿರಿದುಗೊಳಿಸಿ"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"ಹಿತವಾಗಿ"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"ಮೌನವಾಗಿರಿ"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"ಅಡಚಣೆ"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"ಎಚ್ಚರಿಸುತ್ತಿರಿ"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"ಅಧಿಸೂಚನೆಗಳನ್ನು ಆಫ್ ಮಾಡಿ"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"ಈ ಅಪ್ಲಿಕೇಶನ್ನಿಂದ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸುತ್ತಲೇ ಇರಬೇಕೆ?"</string> <string name="hint_text_block" msgid="3554459167504485284">"ನಿರ್ಬಂಧಿಸಲಾದ ಅಧಿಸೂಚನೆಗಳು ಎಲ್ಲಿಯೂ ಗೋಚರಿಸುವುದಿಲ್ಲ ಅಥವಾ ಯಾವುದೇ ಧ್ವನಿಯನ್ನು ಪ್ಲೇ ಮಾಡುವುದಿಲ್ಲ. ನೀವು ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಅಧಿಸೂಚನೆಗಳ ನಿರ್ಬಂಧವನ್ನು ರದ್ದುಗೊಳಿಸಬಹುದು."</string> <string name="hint_text_silent" msgid="859468056340177016">"ನಿಶ್ಶಬ್ಧ ಅಧಿಸೂಚನೆಗಳು ಶೇಡ್ನಲ್ಲಿ ಗೋಚರಿಸುತ್ತವೆ, ಆದರೆ ಲಾಕ್ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಗೋಚರಿಸುವುದಿಲ್ಲ, ಬ್ಯಾನರ್ ಪ್ರಸ್ತುತಪಡಿಸುತ್ತವೆ ಅಥವಾ ಧ್ವನಿಯನ್ನು ಪ್ಲೇ ಮಾಡುತ್ತವೆ."</string> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_alert" msgid="2721169810318722524">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಸ್ವೀಕರಿಸಿದಾಗ ಧ್ವನಿಯನ್ನು ಮಾಡುತ್ತವೆ ಮತ್ತು ಅಧಿಸೂಚನೆ ಡ್ರಾಯರ್, ಸ್ಥಿತಿ ಪಟ್ಟಿ ಮತ್ತು ಲಾಕ್ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ತೋರಿಸಲಾಗುತ್ತದೆ"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆಫ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"ಈ ಗುಂಪಿನ ಅಧಿಸೂಚನೆಗಳನ್ನು ಇಲ್ಲಿ ಕಾನ್ಫಿಗರ್ ಮಾಡಲಾಗಿರುವುದಿಲ್ಲ"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಮೂಲಕ"</string> <string name="appops_camera" msgid="8100147441602585776">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸುತ್ತಿದೆ."</string> <string name="appops_microphone" msgid="741508267659494555">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಮೈಕ್ರೊಫೋನ್ ಅನ್ನು ಬಳಸುತ್ತಿದೆ."</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index d7bf351ccbbc..56a6af28a277 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G 이상"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 937c1469f883..75308a5245b9 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 8046632c6492..b2103c8ce589 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index e8500c18aa23..28fa2c6d4ac0 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 1b6db067fcba..77ff3b0eb206 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-mcc310-mnc030/config.xml b/packages/SystemUI/res/values-mcc310-mnc030/config.xml new file mode 100644 index 000000000000..26b9192e0cc3 --- /dev/null +++ b/packages/SystemUI/res/values-mcc310-mnc030/config.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- Enable 5 bar signal strength icon --> + <bool name="config_inflateSignalStrength">true</bool> +</resources> + diff --git a/packages/SystemUI/res/values-mcc310-mnc070/config.xml b/packages/SystemUI/res/values-mcc310-mnc070/config.xml new file mode 100644 index 000000000000..26b9192e0cc3 --- /dev/null +++ b/packages/SystemUI/res/values-mcc310-mnc070/config.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- Enable 5 bar signal strength icon --> + <bool name="config_inflateSignalStrength">true</bool> +</resources> + diff --git a/packages/SystemUI/res/values-mcc310-mnc170/config.xml b/packages/SystemUI/res/values-mcc310-mnc170/config.xml new file mode 100644 index 000000000000..26b9192e0cc3 --- /dev/null +++ b/packages/SystemUI/res/values-mcc310-mnc170/config.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- Enable 5 bar signal strength icon --> + <bool name="config_inflateSignalStrength">true</bool> +</resources> + diff --git a/packages/SystemUI/res/values-mcc310-mnc280/config.xml b/packages/SystemUI/res/values-mcc310-mnc280/config.xml new file mode 100644 index 000000000000..26b9192e0cc3 --- /dev/null +++ b/packages/SystemUI/res/values-mcc310-mnc280/config.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- Enable 5 bar signal strength icon --> + <bool name="config_inflateSignalStrength">true</bool> +</resources> + diff --git a/packages/SystemUI/res/values-mcc310-mnc380/config.xml b/packages/SystemUI/res/values-mcc310-mnc380/config.xml new file mode 100644 index 000000000000..26b9192e0cc3 --- /dev/null +++ b/packages/SystemUI/res/values-mcc310-mnc380/config.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- Enable 5 bar signal strength icon --> + <bool name="config_inflateSignalStrength">true</bool> +</resources> + diff --git a/packages/SystemUI/res/values-mcc310-mnc410/config.xml b/packages/SystemUI/res/values-mcc310-mnc410/config.xml new file mode 100644 index 000000000000..26b9192e0cc3 --- /dev/null +++ b/packages/SystemUI/res/values-mcc310-mnc410/config.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- Enable 5 bar signal strength icon --> + <bool name="config_inflateSignalStrength">true</bool> +</resources> + diff --git a/packages/SystemUI/res/values-mcc310-mnc560/config.xml b/packages/SystemUI/res/values-mcc310-mnc560/config.xml new file mode 100644 index 000000000000..26b9192e0cc3 --- /dev/null +++ b/packages/SystemUI/res/values-mcc310-mnc560/config.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- Enable 5 bar signal strength icon --> + <bool name="config_inflateSignalStrength">true</bool> +</resources> + diff --git a/packages/SystemUI/res/values-mcc310-mnc950/config.xml b/packages/SystemUI/res/values-mcc310-mnc950/config.xml new file mode 100644 index 000000000000..26b9192e0cc3 --- /dev/null +++ b/packages/SystemUI/res/values-mcc310-mnc950/config.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- Enable 5 bar signal strength icon --> + <bool name="config_inflateSignalStrength">true</bool> +</resources> + diff --git a/packages/SystemUI/res/values-mcc311-mnc180/config.xml b/packages/SystemUI/res/values-mcc311-mnc180/config.xml new file mode 100644 index 000000000000..26b9192e0cc3 --- /dev/null +++ b/packages/SystemUI/res/values-mcc311-mnc180/config.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <!-- Enable 5 bar signal strength icon --> + <bool name="config_inflateSignalStrength">true</bool> +</resources> + diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 6ac95b9a5e4c..985fa7b689db 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -704,7 +705,7 @@ <string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Назад"</string> <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Известувања"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Кратенки на тастатурата"</string> - <string name="keyboard_shortcut_group_system_switch_input" msgid="8413348767825486492">"Промени распоред на тастатура"</string> + <string name="keyboard_shortcut_group_system_switch_input" msgid="8413348767825486492">"Промени јазик на тастатура"</string> <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Апликации"</string> <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Помош"</string> <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Прелистувач"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 1f71904c32c2..25b4720b65b0 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -63,10 +63,8 @@ <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ഉപകരണത്തിൽ ഇപ്പോൾ സൈൻ ഇൻ ചെയ്തിരിക്കുന്ന ഉപയോക്താവിന് USB ഡീബഗ്ഗിംഗ് ഓണാക്കാനാകില്ല. ഈ ഫീച്ചർ ഉപയോഗിക്കാൻ പ്രാഥമിക ഉപയോക്താവിലേക്ക് മാറുക."</string> <string name="usb_contaminant_title" msgid="206854874263058490">"USB പോർട്ട് പ്രവർത്തനരഹിതമാക്കി"</string> <string name="usb_contaminant_message" msgid="2205845572186473860">"ദ്രാവകത്തിൽ നിന്നോ പൊടിയിൽ നിന്നോ നിങ്ങളുടെ ഉപകരണത്തെ പരിരക്ഷിക്കാനായി USB പോർട്ട് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നതിനാൽ അത് ആക്സസറികളൊന്നും തിരിച്ചറിയില്ല.\n\n USB പോർട്ട് സുരക്ഷിതമായി വീണ്ടും ഉപയോഗിക്കാനാകുമ്പോൾ നിങ്ങളെ അറിയിക്കും."</string> - <!-- no translation found for usb_port_enabled (7906141351687694867) --> - <skip /> - <!-- no translation found for usb_disable_contaminant_detection (2103905315747120033) --> - <skip /> + <string name="usb_port_enabled" msgid="7906141351687694867">"ആക്സസറികളും ചാർജറുകളും കണ്ടെത്താൻ USB പോർട്ട് പ്രവർത്തനക്ഷമമാക്കുക"</string> + <string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB പ്രവർത്തനക്ഷമമാക്കുക"</string> <string name="compat_mode_on" msgid="6623839244840638213">"സ്ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ സൂം ചെയ്യുക"</string> <string name="compat_mode_off" msgid="4434467572461327898">"സ്ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ വലിച്ചുനീട്ടുക"</string> <string name="global_action_screenshot" msgid="8329831278085426283">"സ്ക്രീൻഷോട്ട്"</string> @@ -113,8 +111,7 @@ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കാതെ അൺലോക്കുചെയ്യുക"</string> <string name="accessibility_scanning_face" msgid="769545173211758586">"മുഖം സ്കാൻ ചെയ്യുന്നു"</string> <string name="accessibility_send_smart_reply" msgid="7766727839703044493">"അയയ്ക്കുക"</string> - <!-- no translation found for accessibility_manage_notification (2026361503393549753) --> - <skip /> + <string name="accessibility_manage_notification" msgid="2026361503393549753">"അറിയിപ്പുകൾ മാനേജ് ചെയ്യുക"</string> <string name="unlock_label" msgid="8779712358041029439">"അൺലോക്കുചെയ്യുക"</string> <string name="phone_label" msgid="2320074140205331708">"ഫോൺ തുറക്കുക"</string> <string name="voice_assist_label" msgid="3956854378310019854">"വോയ്സ് അസിസ്റ്റ് തുറക്കുക"</string> @@ -175,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -185,8 +184,7 @@ <string name="accessibility_cell_data" msgid="5326139158682385073">"മൊബൈൽ ഡാറ്റ"</string> <string name="accessibility_cell_data_on" msgid="5927098403452994422">"മൊബൈൽ ഡാറ്റ ഓണാണ്"</string> <string name="cell_data_off_content_description" msgid="4356113230238585072">"മൊബൈൽ ഡാറ്റ ഓഫാണ്"</string> - <!-- no translation found for not_default_data_content_description (9194667237765917844) --> - <skip /> + <string name="not_default_data_content_description" msgid="9194667237765917844">"ഡാറ്റ ഉപയോഗിക്കുന്നതിന് സജ്ജീകരിച്ചിട്ടില്ല"</string> <string name="cell_data_off" msgid="1051264981229902873">"ഓഫ്"</string> <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"ബ്ലൂടൂത്ത് ടെതറിംഗ്."</string> <string name="accessibility_airplane_mode" msgid="834748999790763092">"ഫ്ലൈറ്റ് മോഡ്."</string> @@ -228,12 +226,9 @@ <string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"ഫ്ലൈറ്റ് മോഡ് ഓണാക്കി."</string> <string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"പൂർണ്ണ നിശബ്ദത"</string> <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"അലാറങ്ങൾ മാത്രം"</string> - <!-- no translation found for accessibility_quick_settings_dnd (5555155552520665891) --> - <skip /> - <!-- no translation found for accessibility_quick_settings_dnd_changed_off (2757071272328547807) --> - <skip /> - <!-- no translation found for accessibility_quick_settings_dnd_changed_on (6808220653747701059) --> - <skip /> + <string name="accessibility_quick_settings_dnd" msgid="5555155552520665891">"ശല്യപ്പെടുത്തരുത്."</string> + <string name="accessibility_quick_settings_dnd_changed_off" msgid="2757071272328547807">"ശല്യപ്പെടുത്തരുത് എന്നത് ഓഫാക്കി."</string> + <string name="accessibility_quick_settings_dnd_changed_on" msgid="6808220653747701059">"ശല്യപ്പെടുത്തരുത് എന്നത് ഓണാക്കി."</string> <string name="accessibility_quick_settings_bluetooth" msgid="6341675755803320038">"Bluetooth"</string> <string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"ബ്ലൂടൂത്ത് ഓഫാണ്."</string> <string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"ബ്ലൂടൂത്ത് ഓണാണ്."</string> @@ -284,8 +279,7 @@ <item quantity="other">ഉള്ളിൽ <xliff:g id="NUMBER_1">%s</xliff:g> അറിയിപ്പുകൾ കൂടിയുണ്ട്.</item> <item quantity="one">ഉള്ളിൽ <xliff:g id="NUMBER_0">%s</xliff:g> അറിയിപ്പ് കൂടിയുണ്ട്.</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"അറിയിപ്പ് ക്രമീകരണങ്ങൾ"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ക്രമീകരണം"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"സ്ക്രീൻ സ്വയമേവ തിരിയും."</string> @@ -298,8 +292,7 @@ <string name="start_dreams" msgid="5640361424498338327">"സ്ക്രീൻ സേവർ"</string> <string name="ethernet_label" msgid="7967563676324087464">"ഇതർനെറ്റ്"</string> <string name="quick_settings_header_onboarding_text" msgid="8030309023792936283">"കൂടുതൽ ഓപ്ഷനുകൾക്കായി ഐക്കണുകൾ സ്പർശിച്ച് പിടിക്കുക"</string> - <!-- no translation found for quick_settings_dnd_label (7112342227663678739) --> - <skip /> + <string name="quick_settings_dnd_label" msgid="7112342227663678739">"ശല്യപ്പെടുത്തരുത്"</string> <string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"മുൻഗണന മാത്രം"</string> <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"അലാറങ്ങൾ മാത്രം"</string> <string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"പൂർണ്ണ നിശബ്ദത"</string> @@ -450,10 +443,8 @@ <string name="battery_saver_notification_title" msgid="8614079794522291840">"ബാറ്ററി ലാഭിക്കൽ ഓണാണ്"</string> <string name="battery_saver_notification_text" msgid="820318788126672692">"പ്രവർത്തനവും പശ്ചാത്തല ഡാറ്റയും കുറയ്ക്കുന്നു"</string> <string name="battery_saver_notification_action_text" msgid="132118784269455533">"ബാറ്ററി ലാഭിക്കൽ ഓഫാക്കുക"</string> - <!-- no translation found for media_projection_dialog_text (5751657130671431216) --> - <skip /> - <!-- no translation found for media_projection_dialog_title (8124184308671641248) --> - <skip /> + <string name="media_projection_dialog_text" msgid="5751657130671431216">"റിക്കോർഡ് ചെയ്യുമ്പോഴോ കാസ്റ്റ് ചെയ്യുമ്പോഴോ, നിങ്ങൾ പ്ലേ ചെയ്യുന്ന ഓഡിയോയും പാസ്വേഡുകളും, പേയ്മെന്റ് വിവരം, ഫോട്ടോകൾ, സന്ദേശങ്ങളും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ന് ക്യാപ്ചർ ചെയ്യാനാവും."</string> + <string name="media_projection_dialog_title" msgid="8124184308671641248">"കാസ്റ്റ് /റിക്കോർഡ് ചെയ്യുമ്പോൾ സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട വിവരം വെളിപ്പെടുത്തുന്നു"</string> <string name="media_projection_remember_text" msgid="3103510882172746752">"വീണ്ടും കാണിക്കരുത്"</string> <string name="clear_all_notifications_text" msgid="814192889771462828">"എല്ലാം മായ്ക്കുക"</string> <string name="manage_notifications_text" msgid="2386728145475108753">"മാനേജ് ചെയ്യുക"</string> @@ -528,10 +519,8 @@ <string name="accessibility_volume_settings" msgid="4915364006817819212">"ശബ്ദ ക്രമീകരണം"</string> <string name="accessibility_volume_expand" msgid="5946812790999244205">"വികസിപ്പിക്കുക"</string> <string name="accessibility_volume_collapse" msgid="3609549593031810875">"ചുരുക്കുക"</string> - <!-- no translation found for volume_odi_captions_tip (1193653197906918269) --> - <skip /> - <!-- no translation found for accessibility_volume_close_odi_captions_tip (1163987066404128967) --> - <skip /> + <string name="volume_odi_captions_tip" msgid="1193653197906918269">"മീഡിയയ്ക്ക് സ്വയമേവ ക്യാപ്ഷൻ"</string> + <string name="accessibility_volume_close_odi_captions_tip" msgid="1163987066404128967">"അടിക്കുറിപ്പുകൾക്കുള്ള നുറുങ്ങ്"</string> <string name="accessibility_output_chooser" msgid="8185317493017988680">"ഔട്ട്പുട്ട് ഉപകരണം മാറുക"</string> <string name="screen_pinning_title" msgid="3273740381976175811">"സ്ക്രീൻ പിൻ ചെയ്തു"</string> <string name="screen_pinning_description" msgid="8909878447196419623">"നിങ്ങൾ അൺപിൻ ചെയ്യുന്നതുവരെ ഇത് കാണുന്ന വിധത്തിൽ നിലനിർത്തും. അൺപിൻ ചെയ്യാൻ \'തിരികെ\', \'ചുരുക്കവിവരണം\' എന്നിവ സ്പർശിച്ച് പിടിക്കുക."</string> @@ -632,32 +621,24 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ഈ അറിയിപ്പുകൾ നിങ്ങൾക്ക് മുന്നറിയിപ്പ് നൽകും"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"സാധാരണയായി നിങ്ങൾ ഈ അറിയിപ്പുകൾ നിരാകരിക്കുന്നു. \nഅവ തുടർന്നും കാണിക്കണോ?"</string> <string name="inline_done_button" msgid="492513001558716452">"പൂർത്തിയായി"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"ബാധകമാക്കുക"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"ഈ അറിയിപ്പുകൾ തുടർന്നും കാണിക്കണോ?"</string> <string name="inline_stop_button" msgid="4172980096860941033">"അറിയിപ്പുകൾ നിർത്തുക"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"നിശബ്ദമായി ഡെലിവർ ചെയ്യുക"</string> <string name="inline_block_button" msgid="8735843688021655065">"ബ്ലോക്ക് ചെയ്യുക"</string> <string name="inline_keep_button" msgid="6665940297019018232">"തുടർന്നും കാണിക്കുക"</string> <string name="inline_minimize_button" msgid="966233327974702195">"ചെറുതാക്കുക"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"നിശബ്ദമായ"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"നിശബ്ദമായ നിലയിൽ തുടരുക"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"തടസ്സപ്പെടുത്തുന്ന"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"മുന്നറിയിപ്പ് നൽകുന്നത് തുടരുക"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"അറിയിപ്പുകൾ ഓഫാക്കുക"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"ഈ ആപ്പിൽ നിന്നുള്ള അറിയിപ്പുകൾ തുടർന്നും കാണിക്കണോ?"</string> - <!-- no translation found for hint_text_block (3554459167504485284) --> - <skip /> - <!-- no translation found for hint_text_silent (859468056340177016) --> - <skip /> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_block" msgid="3554459167504485284">"ബ്ലോക്ക് ചെയ്ത അറിയിപ്പുകൾ എവിടെയും ദൃശ്യമാവുകയോ ശബ്ദം പ്ലേ ചെയ്യുകയോ ഇല്ല. ക്രമീകരണത്തിൽ നിങ്ങൾക്ക് അറിയിപ്പുകൾ അൺബ്ലോക്ക് ചെയ്യാനാവും."</string> + <string name="hint_text_silent" msgid="859468056340177016">"നിശബ്ദ അറിയിപ്പുകൾ ഷെയ്ഡിൽ ദൃശ്യമാകും, പക്ഷെ ലോക്ക് സ്ക്രീനിൽ ദൃശ്യമാവുകയോ ബാനർ അവതരിപ്പിക്കുകയോ ഒരു ശബ്ദം പ്ലേ ചെയ്യുകയോ ഇല്ല."</string> + <string name="hint_text_alert" msgid="2721169810318722524">"ഈ അറിയിപ്പുകൾ ഒരു ശബ്ദമുണ്ടാക്കുകയും അറിയിപ്പ് ഡ്രോയർ, സ്റ്റാറ്റസ് ബാർ, ലോക്ക് സ്ക്രീൻ എന്നിവയിൽ കാണിക്കുകയും ചെയ്യും"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"ഈ അറിയിപ്പുകൾ ഓഫാക്കാനാവില്ല"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"അറിയിപ്പുകളുടെ ഈ ഗ്രൂപ്പ് ഇവിടെ കോണ്ഫിഗര് ചെയ്യാൻ കഴിയില്ല"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> വഴി"</string> <string name="appops_camera" msgid="8100147441602585776">"ഈ ആപ്പ് ക്യാമറ ഉപയോഗിക്കുന്നുണ്ട്."</string> <string name="appops_microphone" msgid="741508267659494555">"ഈ ആപ്പ് മൈക്രോഫോൺ ഉപയോഗിക്കുന്നു."</string> @@ -736,11 +717,9 @@ <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string> <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"കലണ്ടർ"</string> <string name="tuner_full_zen_title" msgid="4540823317772234308">"വോളിയം നിയന്ത്രണങ്ങളോടൊപ്പം കാണിക്കുക"</string> - <!-- no translation found for volume_and_do_not_disturb (1750270820297253561) --> - <skip /> + <string name="volume_and_do_not_disturb" msgid="1750270820297253561">"ശല്യപ്പെടുത്തരുത്"</string> <string name="volume_dnd_silent" msgid="4363882330723050727">"വോളിയം ബട്ടൺ കുറുക്കുവഴി"</string> - <!-- no translation found for volume_up_silent (7545869833038212815) --> - <skip /> + <string name="volume_up_silent" msgid="7545869833038212815">"ശബ്ദം കൂടുമ്പോൾ \'ശല്യപ്പെടുത്തരുതിൽ\' നിന്ന് പുറത്ത് കടക്കൂ"</string> <string name="battery" msgid="7498329822413202973">"ബാറ്ററി"</string> <string name="clock" msgid="7416090374234785905">"ക്ലോക്ക്"</string> <string name="headset" msgid="4534219457597457353">"ഹെഡ്സെറ്റ്"</string> @@ -881,8 +860,7 @@ <string name="go_to_web" msgid="2650669128861626071">"ബ്രൗസറിലേക്ക് പോവുക"</string> <string name="mobile_data" msgid="7094582042819250762">"മൊബൈൽ ഡാറ്റ"</string> <string name="mobile_data_text_format" msgid="3526214522670876454">"<xliff:g id="ID_1">%1$s</xliff:g> — <xliff:g id="ID_2">%2$s</xliff:g>"</string> - <!-- no translation found for mobile_carrier_text_format (3241721038678469804) --> - <skip /> + <string name="mobile_carrier_text_format" msgid="3241721038678469804">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string> <string name="wifi_is_off" msgid="1838559392210456893">"വൈഫൈ ഓഫാണ്"</string> <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth ഓഫാണ്"</string> <string name="dnd_is_off" msgid="6167780215212497572">"\'ശല്യപ്പെടുത്തരുത്\' ഓഫാണ്"</string> @@ -922,30 +900,18 @@ <string name="sensor_privacy_mode" msgid="8982771253020769598">"സെൻസറുകൾ ഓഫാണ്"</string> <string name="device_services" msgid="1191212554435440592">"ഉപകരണ സേവനങ്ങള്"</string> <string name="music_controls_no_title" msgid="5236895307087002011">"പേരില്ല"</string> - <!-- no translation found for restart_button_description (2035077840254950187) --> - <skip /> + <string name="restart_button_description" msgid="2035077840254950187">"ഈ ആപ്പ് റീസ്റ്റാർട്ട് ചെയ്യാനും പൂർണ്ണ സ്ക്രീനാവാനും ടാപ്പ് ചെയ്യുക."</string> <string name="bubbles_deep_link_button_description" msgid="8895837143057564517">"<xliff:g id="APP_NAME">%1$s</xliff:g> തുറക്കുക"</string> - <!-- no translation found for bubbles_settings_button_description (2970630476657287189) --> - <skip /> - <!-- no translation found for bubbles_prompt (8807968030159469710) --> - <skip /> - <!-- no translation found for no_bubbles (337101288173078247) --> - <skip /> + <string name="bubbles_settings_button_description" msgid="2970630476657287189">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനുള്ള ബബിളുകളുടെ ക്രമീകരണം"</string> + <string name="bubbles_prompt" msgid="8807968030159469710">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ൽ നിന്നും ബബിളുകളെ അനുവദിക്കണോ?"</string> + <string name="no_bubbles" msgid="337101288173078247">"നിരസിക്കുക"</string> <string name="yes_bubbles" msgid="668809525728633841">"അനുവദിക്കുക"</string> - <!-- no translation found for ask_me_later_bubbles (2147688438402939029) --> - <skip /> - <!-- no translation found for bubble_content_description_single (1184462974339387516) --> - <skip /> - <!-- no translation found for bubble_content_description_stack (8666349184095622232) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move (1794879742234803840) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_top_left (104736832249802724) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_top_right (1671844272347036806) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bottom_left (206369104473183217) --> - <skip /> - <!-- no translation found for bubble_accessibility_action_move_bottom_right (8705660152384312329) --> - <skip /> + <string name="ask_me_later_bubbles" msgid="2147688438402939029">"എന്നോട് പിന്നീട് ചോദിക്കുക"</string> + <string name="bubble_content_description_single" msgid="1184462974339387516">"<xliff:g id="APP_NAME">%2$s</xliff:g>-ൽ നിന്നുള്ള <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> + <string name="bubble_content_description_stack" msgid="8666349184095622232">"<xliff:g id="APP_NAME">%2$s</xliff:g> എന്നതിൽ നിന്നുള്ള <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>, <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g> കൂടുതലും"</string> + <string name="bubble_accessibility_action_move" msgid="1794879742234803840">"നീക്കുക"</string> + <string name="bubble_accessibility_action_move_top_left" msgid="104736832249802724">"മുകളിൽ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string> + <string name="bubble_accessibility_action_move_top_right" msgid="1671844272347036806">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string> + <string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"ചുവടെ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string> + <string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"ചുവടെ വലതുഭാഗത്തേക്ക് നീക്കുക"</string> </resources> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index dcb5c2da29c7..e414ea9e3141 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index e84e03ad4dc4..a913a0095b92 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"४G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"१X"</string> @@ -277,8 +279,7 @@ <item quantity="one">आत आणखी <xliff:g id="NUMBER_1">%s</xliff:g> सूचना.</item> <item quantity="other">आत आणखी <xliff:g id="NUMBER_1">%s</xliff:g> सूचना.</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"सूचना सेटिंग्ज"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> सेटिंग्ज"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"स्क्रीन स्वयंचलितपणे फिरेल."</string> @@ -620,30 +621,24 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"या सूचना तुम्हाला इशारा देतील"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"तुम्ही या सूचना सामान्यतः डिसमिस करता. \nते दाखवत राहायचे?"</string> <string name="inline_done_button" msgid="492513001558716452">"पूर्ण झाले"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"लागू करा"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"या सूचना दाखवणे सुरू ठेवायचे?"</string> <string name="inline_stop_button" msgid="4172980096860941033">"सूचना थांबवा"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"शांतपणे पाठवा"</string> <string name="inline_block_button" msgid="8735843688021655065">"ब्लॉक करा"</string> <string name="inline_keep_button" msgid="6665940297019018232">"दाखवणे सुरू ठेवा"</string> <string name="inline_minimize_button" msgid="966233327974702195">"लहान करा"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"नाजूक"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"सायलंट रहा"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"व्यत्यय आणणारे"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"सूचना देत रहा"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"सूचना बंद करा"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"या अॅपकडील सूचना दाखवणे सुरू ठेवायचे?"</string> <string name="hint_text_block" msgid="3554459167504485284">"ब्लॉक केलेल्या सूचना कुठेही दिसत नाहीत किंवा आवाज प्ले करत नाहीत. तुम्ही सेटिंग्जमध्ये सूचना अनब्लॉक करू शकता."</string> <string name="hint_text_silent" msgid="859468056340177016">"सायलंट सूचना रंगछटेमध्ये दिसतात, परंतु लॉक स्क्रीनवर दिसत नाहीत, बॅनर दाखवत नाहीत किंवा आवाज प्ले करत नाहीत."</string> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_alert" msgid="2721169810318722524">"या सूचना आवाज करतील आणि सूचना ड्राॅवर, स्टेटस बार आणि लॉक स्क्रीनवर दाखवल्या जातील"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"या सूचना बंद करता येत नाहीत"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"या सूचनांचा संच येथे कॉन्फिगर केला जाऊ शकत नाही"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> मार्गे"</string> <string name="appops_camera" msgid="8100147441602585776">"हे अॅप कॅमेरा वापरत आहे."</string> <string name="appops_microphone" msgid="741508267659494555">"हे अॅप मायक्रोफोन वापरत आहे."</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index dc3d8327c1a4..76472b6a9094 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 40b21cc3c7c9..ac9b53195021 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 75a5730cd430..c35c3ece5de9 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index ebe76ec08fe6..98e1a96f38de 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -277,8 +279,7 @@ <item quantity="other">भित्र थप <xliff:g id="NUMBER_1">%s</xliff:g> सूचनाहरू छन्।</item> <item quantity="one">भित्र थप <xliff:g id="NUMBER_0">%s</xliff:g> सूचना छ।</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"अधिसूचना सेटिङहरू"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> सेटिङहरू"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"स्क्रिन स्वतः घुम्ने छ।"</string> @@ -620,30 +621,24 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"यी सूचनाहरूले तपाईंलाई सतर्क गरिने छ"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"तपाईं सामान्यतया यी सूचनाहरूलाई खारेज गर्ने गर्नुहुन्छ। \nतिनलाई देखाइरहने हो?"</string> <string name="inline_done_button" msgid="492513001558716452">"सम्पन्न भयो"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"लागू गर्नुहोस्"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"यी सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string> <string name="inline_stop_button" msgid="4172980096860941033">"सूचनाहरू देखाउन छाड्नुहोस्"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"मौन रूपमा डेलिभर गर्नुहोस्"</string> <string name="inline_block_button" msgid="8735843688021655065">"रोक लगाउनुहोस्"</string> <string name="inline_keep_button" msgid="6665940297019018232">"देखाउने क्रम जारी राख्नुहोस्"</string> <string name="inline_minimize_button" msgid="966233327974702195">"सानो बनाउनुहोस्"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"हलुका"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"मौन रहनुहोस्"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"बाधा पुर्याइरहने"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"सर्तक गराइरहनुहोस्"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"सूचनाहरू निष्क्रिय पार्नुहोस्"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"यो अनुप्रयोगका सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string> <string name="hint_text_block" msgid="3554459167504485284">"रोक लगाइएका सूचनाहरू कतै पनि देखिँदैनन् वा कुनै आवाज गर्दैनन्। तपाईं सेटिङहरूमा सूचनाहरूमाथिको रोक हटाउन सक्नुहुन्छ।"</string> <string name="hint_text_silent" msgid="859468056340177016">"मौन सूचनाहरू ओझेलमा देखिन्छन् तर स्क्रिन लक हुँदा देखिँदैनन्, ब्यानर देखाउँदैनन् अनि कुनै आवाज पनि दिँदैनन्।"</string> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_alert" msgid="2721169810318722524">"यी सूचनाहरू आउँदा ध्वनि बज्ने छ र तिनीहरू सूचनाको ड्रअर, स्थिति पट्टी र लक स्क्रिनमा देखिने छन्"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"यी सूचनाहरूलाई निष्क्रिय पार्न सकिँदैन"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"यहाँबाट सूचनाहरूको यो समूह कन्फिगर गर्न सकिँदैन"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> मार्फत"</string> <string name="appops_camera" msgid="8100147441602585776">"यो अनुप्रयोगले क्यामेराको प्रयोग गर्दै छ।"</string> <string name="appops_microphone" msgid="741508267659494555">"यो अनुप्रयोगले माइक्रोफोनको प्रयोग गर्दै छ।"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index bad9821835ea..d963138ae3a6 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -25,10 +25,10 @@ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Actief"</string> <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meldingen"</string> <string name="battery_low_title" msgid="9187898087363540349">"Batterij is bijna leeg"</string> - <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> resterend"</string> - <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> resterend, nog ongeveer <xliff:g id="TIME">%2$s</xliff:g> over op basis van je gebruik"</string> - <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> resterend, nog ongeveer <xliff:g id="TIME">%2$s</xliff:g> over"</string> - <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> resterend. Batterijbesparing is ingeschakeld."</string> + <string name="battery_low_percent_format" msgid="2900940511201380775">"Nog <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> + <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"Nog <xliff:g id="PERCENTAGE">%1$s</xliff:g>, dat is ongeveer <xliff:g id="TIME">%2$s</xliff:g> op basis van je gebruik"</string> + <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"Nog <xliff:g id="PERCENTAGE">%1$s</xliff:g>, dat is ongeveer <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"Nog <xliff:g id="PERCENTAGE">%s</xliff:g>. Batterijbesparing is ingeschakeld."</string> <string name="invalid_charger" msgid="2741987096648693172">"Kan niet opladen via USB. Gebruik de oplader die bij je apparaat is geleverd."</string> <string name="invalid_charger_title" msgid="2836102177577255404">"Kan niet opladen via USB"</string> <string name="invalid_charger_text" msgid="6480624964117840005">"Gebruik de oplader die bij je apparaat is geleverd"</string> @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -795,7 +796,7 @@ <string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"<xliff:g id="ID_1">%1$s</xliff:g>-melding: <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="dock_forced_resizable" msgid="5914261505436217520">"App werkt mogelijk niet met gesplitst scherm."</string> <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"App biedt geen ondersteuning voor gesplitst scherm."</string> - <string name="forced_resizable_secondary_display" msgid="4230857851756391925">"App werkt mogelijk niet op een secundair display."</string> + <string name="forced_resizable_secondary_display" msgid="4230857851756391925">"App werkt mogelijk niet op een secundair scherm."</string> <string name="activity_launch_on_secondary_display_failed_text" msgid="7793821742158306742">"App kan niet op secundaire displays worden gestart."</string> <string name="accessibility_quick_settings_settings" msgid="6132460890024942157">"Instellingen openen."</string> <string name="accessibility_quick_settings_expand" msgid="2375165227880477530">"Snelle instellingen openen."</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 4c68841cd27d..65ac6c04d0dc 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index d8d5429d3ef6..6a5c11d210df 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -225,7 +227,7 @@ <string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"ਸੰਪੂਰਨ ਖਾਮੋਸ਼ੀ"</string> <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"ਸਿਰਫ਼ ਅਲਾਰਮ"</string> <string name="accessibility_quick_settings_dnd" msgid="5555155552520665891">"ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ।"</string> - <string name="accessibility_quick_settings_dnd_changed_off" msgid="2757071272328547807">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਬੰਦ ਕਰੋ।"</string> + <string name="accessibility_quick_settings_dnd_changed_off" msgid="2757071272328547807">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string> <string name="accessibility_quick_settings_dnd_changed_on" msgid="6808220653747701059">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string> <string name="accessibility_quick_settings_bluetooth" msgid="6341675755803320038">"ਬਲੂਟੁੱਥ।"</string> <string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"Bluetooth ਬੰਦ।"</string> @@ -277,8 +279,7 @@ <item quantity="one">ਅੰਦਰ <xliff:g id="NUMBER_1">%s</xliff:g> ਹੋਰ ਸੂਚਨਾਵਾਂ।</item> <item quantity="other">ਅੰਦਰ <xliff:g id="NUMBER_1">%s</xliff:g> ਹੋਰ ਸੂਚਨਾਵਾਂ।</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"ਸੂਚਨਾ ਸੈਟਿੰਗਾਂ"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ਸੈਟਿੰਗਾਂ"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"ਸਕ੍ਰੀਨ ਆਟੋਮੈਟਿਕਲੀ ਰੋਟੇਟ ਕਰੇਗੀ।"</string> @@ -620,30 +621,24 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ਇਹ ਸੂਚਨਾਵਾਂ ਤੁਹਾਨੂੰ ਸੁਚੇਤ ਕਰਨਗੀਆਂ"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"ਤੁਸੀਂ ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਆਮ ਤੌਰ \'ਤੇ ਖਾਰਜ ਕਰਦੇ ਹੋ। \nਕੀ ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string> <string name="inline_done_button" msgid="492513001558716452">"ਹੋ ਗਿਆ"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"ਲਾਗੂ ਕਰੋ"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"ਕੀ ਇਨ੍ਹਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string> <string name="inline_stop_button" msgid="4172980096860941033">"ਸੂਚਨਾਵਾਂ ਬੰਦ ਕਰੋ"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"ਚੁੱਪ-ਚਪੀਤੇ ਡਿਲੀਵਰ ਕਰੋ"</string> <string name="inline_block_button" msgid="8735843688021655065">"ਬਲਾਕ ਕਰੋ"</string> <string name="inline_keep_button" msgid="6665940297019018232">"ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖੋ"</string> <string name="inline_minimize_button" msgid="966233327974702195">"ਛੋਟਾ ਕਰੋ"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"ਅਰਾਮਦੇਹ"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"ਚੁੱਪ ਰਹੋ"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"ਰੁਕਾਵਟੀ"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"ਸੁਚੇਤ ਰਖੋ"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"ਸੂਚਨਾਵਾਂ ਬੰਦ ਕਰੋ"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"ਕੀ ਇਸ ਐਪ ਤੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string> <string name="hint_text_block" msgid="3554459167504485284">"ਬਲਾਕ ਕੀਤੀਆਂ ਸੂਚਨਾਵਾਂ ਕਿਤੇ ਵੀ ਨਹੀਂ ਦਿਸਦੀਆਂ ਜਾਂ ਕੋਈ ਧੁਨੀ ਨਹੀਂ ਵਜਾਉਂਦੀਆਂ। ਤੁਸੀਂ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਸੂਚਨਾਵਾਂ ਨੂੰ ਅਣਬਲਾਕ ਕਰ ਸਕਦੇ ਹੋ।"</string> - <string name="hint_text_silent" msgid="859468056340177016">"ਚੁੱਪ ਸੂਚਨਾਵਾਂ ਭਾਹ ਵਿੱਚ ਦਿਸਦੀਆਂ ਹਨ ਪਰ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ, ਬੈਨਰ ਦੇ ਰੂਪ ਵਿੱਚ ਨਹੀਂ ਦਿਸਦੀਆਂ ਹਨ ਅਤੇ ਇੱਕ ਧੁਨੀ ਵਜਾਉਂਦੀਆਂ ਹਨ।"</string> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_silent" msgid="859468056340177016">"ਚੁੱਪ ਸੂਚਨਾਵਾਂ ਭਾਹ ਵਿੱਚ ਦਿਸਦੀਆਂ ਹਨ ਪਰ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ, ਬੈਨਰ ਦੇ ਰੂਪ ਵਿੱਚ ਨਹੀਂ ਦਿਸਦੀਆਂ ਹਨ, ਜਾਂ ਕੋਈ ਧੁਨੀ ਵਜਾਉਂਦੀਆਂ ਹਨ।"</string> + <string name="hint_text_alert" msgid="2721169810318722524">"ਇਹ ਸੂਚਨਾਵਾਂ ਅਵਾਜ਼ ਕਰਨਗੀਆਂ ਅਤੇ ਸੂਚਨਾ ਦਰਾਜ਼, ਸਥਿਤੀ ਪੱਟੀ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ ਵਿੱਚ ਦਿਸਣਗੀਆਂ"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"ਇਨ੍ਹਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੰਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"ਇਹ ਸੂਚਨਾਵਾਂ ਦਾ ਗਰੁੱਪ ਇੱਥੇ ਸੰਰੂਪਿਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਰਾਹੀਂ"</string> <string name="appops_camera" msgid="8100147441602585776">"ਇਹ ਐਪ ਕੈਮਰੇ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ।"</string> <string name="appops_microphone" msgid="741508267659494555">"ਇਹ ਐਪ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਦੀ ਵਰਤੋਂ ਕਰ ਰਹੀ ਹੈ।"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index af4acd368877..c42faae9182f 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -172,6 +172,9 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- String.format failed for translation --> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -678,7 +681,7 @@ <item quantity="other">%d minuty</item> <item quantity="one">]%d minuta</item> </plurals> - <string name="battery_panel_title" msgid="7944156115535366613">"Wykorzystanie baterii"</string> + <string name="battery_panel_title" msgid="7944156115535366613">"Zużycie baterii"</string> <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Oszczędzanie baterii nie jest dostępne podczas ładowania"</string> <string name="battery_detail_switch_title" msgid="6285872470260795421">"Oszczędzanie baterii"</string> <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Zmniejsza wydajność i ogranicza dane w tle"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 8b7449fe6fc6..1d3cf8d1022a 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -34,7 +34,7 @@ <string name="invalid_charger_text" msgid="6480624964117840005">"Usar o carregador que acompanha o dispositivo"</string> <string name="battery_low_why" msgid="4553600287639198111">"Configurações"</string> <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"Ativar Economia de bateria?"</string> - <string name="battery_saver_confirmation_title_generic" msgid="2090922638411744540">"Sobre a \"Economia de bateria\""</string> + <string name="battery_saver_confirmation_title_generic" msgid="2090922638411744540">"Sobre a Economia de bateria"</string> <string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"Ativar"</string> <string name="battery_saver_start_action" msgid="8187820911065797519">"Ativar a Economia de bateria"</string> <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Configurações"</string> @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -880,11 +881,11 @@ <string name="slice_permission_checkbox" msgid="7986504458640562900">"Permitir que <xliff:g id="APP">%1$s</xliff:g> mostre partes de qualquer app"</string> <string name="slice_permission_allow" msgid="2340244901366722709">"Permitir"</string> <string name="slice_permission_deny" msgid="7683681514008048807">"Negar"</string> - <string name="auto_saver_title" msgid="1217959994732964228">"Toque para programar o recurso \"Economia de bateria\""</string> + <string name="auto_saver_title" msgid="1217959994732964228">"Toque para programar o recurso Economia de bateria"</string> <string name="auto_saver_text" msgid="6324376061044218113">"Ativar automaticamente quando a bateria estiver em <xliff:g id="PERCENTAGE">%d</xliff:g>%%"</string> <string name="no_auto_saver_action" msgid="8086002101711328500">"Não"</string> - <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Programação do recurso \"Economia de bateria\" ativada"</string> - <string name="auto_saver_enabled_text" msgid="874711029884777579">"O recurso \"Economia de bateria\" será ativado automaticamente depois que a bateria ficar abaixo de <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string> + <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Programação do recurso Economia de bateria ativada"</string> + <string name="auto_saver_enabled_text" msgid="874711029884777579">"O recurso Economia de bateria será ativado automaticamente depois que a bateria ficar abaixo de <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string> <string name="open_saver_setting_action" msgid="8314624730997322529">"Configurações"</string> <string name="auto_saver_okay_action" msgid="2701221740227683650">"Ok"</string> <string name="heap_dump_tile_name" msgid="9141031328971226374">"Despejar pilha SysUI"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 104eb4591199..e58de4f5548f 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 8b7449fe6fc6..1d3cf8d1022a 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -34,7 +34,7 @@ <string name="invalid_charger_text" msgid="6480624964117840005">"Usar o carregador que acompanha o dispositivo"</string> <string name="battery_low_why" msgid="4553600287639198111">"Configurações"</string> <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"Ativar Economia de bateria?"</string> - <string name="battery_saver_confirmation_title_generic" msgid="2090922638411744540">"Sobre a \"Economia de bateria\""</string> + <string name="battery_saver_confirmation_title_generic" msgid="2090922638411744540">"Sobre a Economia de bateria"</string> <string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"Ativar"</string> <string name="battery_saver_start_action" msgid="8187820911065797519">"Ativar a Economia de bateria"</string> <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Configurações"</string> @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -880,11 +881,11 @@ <string name="slice_permission_checkbox" msgid="7986504458640562900">"Permitir que <xliff:g id="APP">%1$s</xliff:g> mostre partes de qualquer app"</string> <string name="slice_permission_allow" msgid="2340244901366722709">"Permitir"</string> <string name="slice_permission_deny" msgid="7683681514008048807">"Negar"</string> - <string name="auto_saver_title" msgid="1217959994732964228">"Toque para programar o recurso \"Economia de bateria\""</string> + <string name="auto_saver_title" msgid="1217959994732964228">"Toque para programar o recurso Economia de bateria"</string> <string name="auto_saver_text" msgid="6324376061044218113">"Ativar automaticamente quando a bateria estiver em <xliff:g id="PERCENTAGE">%d</xliff:g>%%"</string> <string name="no_auto_saver_action" msgid="8086002101711328500">"Não"</string> - <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Programação do recurso \"Economia de bateria\" ativada"</string> - <string name="auto_saver_enabled_text" msgid="874711029884777579">"O recurso \"Economia de bateria\" será ativado automaticamente depois que a bateria ficar abaixo de <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string> + <string name="auto_saver_enabled_title" msgid="6726474226058316862">"Programação do recurso Economia de bateria ativada"</string> + <string name="auto_saver_enabled_text" msgid="874711029884777579">"O recurso Economia de bateria será ativado automaticamente depois que a bateria ficar abaixo de <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string> <string name="open_saver_setting_action" msgid="8314624730997322529">"Configurações"</string> <string name="auto_saver_okay_action" msgid="2701221740227683650">"Ok"</string> <string name="heap_dump_tile_name" msgid="9141031328971226374">"Despejar pilha SysUI"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index ee3092688d64..2837aa78ae49 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 7ee314d2f321..1236224f9c9b 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index a75f3d2594d1..bba9e797b9ae 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 535a8685a07f..f38889ba16b3 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -678,7 +680,7 @@ <item quantity="other">%d minút</item> <item quantity="one">%d minúta</item> </plurals> - <string name="battery_panel_title" msgid="7944156115535366613">"Využitie batérie"</string> + <string name="battery_panel_title" msgid="7944156115535366613">"Spotreba batérie"</string> <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Počas nabíjania nie je Šetrič batérie k dispozícii"</string> <string name="battery_detail_switch_title" msgid="6285872470260795421">"Šetrič batérie"</string> <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Obmedzí výkonnosť a údaje na pozadí"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 11b262a7f692..f8c3c9918d69 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -714,7 +715,7 @@ <string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Nazaj"</string> <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Obvestila"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Bližnjične tipke"</string> - <string name="keyboard_shortcut_group_system_switch_input" msgid="8413348767825486492">"Preklop razporeda tipkovnice"</string> + <string name="keyboard_shortcut_group_system_switch_input" msgid="8413348767825486492">"Preklop postavitve tipkovnice"</string> <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikacije"</string> <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Pomoč"</string> <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Brskalnik"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 5b728a6bbb37..6cc23e2438c1 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -497,12 +499,12 @@ <string name="monitoring_description_ca_cert_settings_separator" msgid="4987350385906393626">" "</string> <string name="monitoring_description_ca_cert_settings" msgid="5489969458872997092">"Hap kredencialet e besuara"</string> <string name="monitoring_description_network_logging" msgid="7223505523384076027">"Administratori yt ka aktivizuar regjistrimin e rrjetit, i cili monitoron trafikun në pajisjen tënde.\n\nPër më shumë informacione, kontakto me administratorin."</string> - <string name="monitoring_description_vpn" msgid="4445150119515393526">"I dhe leje një aplikacioni që të konfigurojë një lidhje VPN.\n\nKy aplikacion mund të monitorojë pajisjen tënde dhe aktivitetin e rrjetit, përfshirë mailet, aplikacionet dhe sajtet e uebit."</string> + <string name="monitoring_description_vpn" msgid="4445150119515393526">"I dhe leje një aplikacioni që të konfigurojë një lidhje VPN.\n\nKy aplikacion mund të monitorojë pajisjen tënde dhe aktivitetin e rrjetit, përfshirë email-et, aplikacionet dhe sajtet e uebit."</string> <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"Profili yt i punës menaxhohet nga <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministratori yt mund të monitorojë aktivitetin tënd të rrjetit, duke përfshirë email-et, aplikacionet dhe sajtet e uebit.\n\nPër më shumë informacion, kontakto me administratorin tënd.\n\nJe i lidhur edhe me një VPN, që mund të monitorojë aktivitetin tënd të rrjetit."</string> <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string> <string name="monitoring_description_app" msgid="1828472472674709532">"Je i lidhur me aplikacionin <xliff:g id="APPLICATION">%1$s</xliff:g> i cili mund të monitorojë aktivitetin tënd në rrjet, duke përfshirë mail-et, aplikacionet dhe sajtet e uebit."</string> - <string name="monitoring_description_app_personal" msgid="484599052118316268">"Je i lidhur me aplikacionin <xliff:g id="APPLICATION">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd personal në rrjet, përfshirë mailet, aplikacionet dhe sajtet e uebit."</string> - <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Je i lidhur me aplikacionin <xliff:g id="APPLICATION">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd personal në rrjet, përfshirë mailet, aplikacionet dhe sajtet e uebit."</string> + <string name="monitoring_description_app_personal" msgid="484599052118316268">"Je i lidhur me aplikacionin <xliff:g id="APPLICATION">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd personal në rrjet, përfshirë email-et, aplikacionet dhe sajtet e uebit."</string> + <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"Je i lidhur me aplikacionin <xliff:g id="APPLICATION">%1$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd personal në rrjet, përfshirë email-et, aplikacionet dhe sajtet e uebit."</string> <string name="monitoring_description_app_work" msgid="4612997849787922906">"Profili yt i punës menaxhohet nga <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Profili është i lidhur me <xliff:g id="APPLICATION">%2$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd të punës në rrjet, duke përfshirë mail-et, aplikacionet dhe sajtet e uebit.\n\nPër më shumë informacione, kontakto me administratorin."</string> <string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"Profili yt i punës menaxhohet nga <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Profili është i lidhur me <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd të punës në rrjet, duke përfshirë mail-et, aplikacionet dhe sajtet e uebit.\n\nJe lidhur gjithashtu edhe me <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, i cili mund të monitorojë aktivitetin tënd personal në rrjet."</string> <string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"Shkyçur për <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index c3e1c941067b..8b1a6959329b 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -376,7 +377,7 @@ <string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Превуците удесно да бисте брзо променили апликације"</string> <string name="quick_step_accessibility_toggle_overview" msgid="7171470775439860480">"Укључи/искључи преглед"</string> <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Напуњена је"</string> - <string name="expanded_header_battery_charging" msgid="205623198487189724">"Пуњење"</string> + <string name="expanded_header_battery_charging" msgid="205623198487189724">"Пуни се"</string> <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> до краја пуњења"</string> <string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"Не пуни се"</string> <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Мрежа се можда\nнадгледа"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 380acc830c27..b335402ed6c2 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index f359ac0388b7..494dfc92dd7e 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index edfb20de3806..c9b9dcad34eb 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -176,6 +176,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -285,8 +287,7 @@ <item quantity="other">உள்ளே மேலும் <xliff:g id="NUMBER_1">%s</xliff:g> அறிவிப்புகள் உள்ளன.</item> <item quantity="one">உள்ளே மேலும் <xliff:g id="NUMBER_0">%s</xliff:g> அறிவிப்பு உள்ளது.</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"அறிவிப்பு அமைப்புகள்"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> அமைப்புகள்"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"திரை தானாகச் சுழலும்."</string> @@ -633,32 +634,26 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"இந்த அறிவிப்புகள் விழிப்பூட்டலாக அமையும்"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"வழக்கமாக, இந்த அறிவிப்புகளை நிராகரிக்கிறீர்கள். \nதொடர்ந்து இவற்றைக் காட்டலாமா?"</string> <string name="inline_done_button" msgid="492513001558716452">"முடிந்தது"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"பயன்படுத்து"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"இந்த அறிவிப்புகளைத் தொடர்ந்து காட்டவா?"</string> <string name="inline_stop_button" msgid="4172980096860941033">"அறிவிப்புகளை நிறுத்து"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"ஒலியின்றி அறிவிப்புகளை வழங்கு"</string> <string name="inline_block_button" msgid="8735843688021655065">"தடு"</string> <string name="inline_keep_button" msgid="6665940297019018232">"அறிவிப்புகளைத் தொடர்ந்து காட்டு"</string> <string name="inline_minimize_button" msgid="966233327974702195">"சிறிதாக்கு"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"ஜென்டில்"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"அறிவிப்புகளை ஒலியின்றிக் காட்டு"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"இண்ட்டரப்டிவ்"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"தொடர்ந்து விழிப்பூட்டு"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"அறிவிப்புகளை முடக்கு"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"இந்தப் பயன்பாட்டின் அறிவிப்புகளைத் தொடர்ந்து காட்டவா?"</string> <!-- no translation found for hint_text_block (3554459167504485284) --> <skip /> <!-- no translation found for hint_text_silent (859468056340177016) --> <skip /> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_alert" msgid="2721169810318722524">"அறிவிப்பு டிராயரிலும், நிலைப் பட்டியிலும், பூட்டுத் திரையிலும் ஒலியுடன் அறிவிக்கப்படும்"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"இந்த அறிவிப்புகளை ஆஃப் செய்ய முடியாது"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"இந்த அறிவுப்புக் குழுக்களை இங்கே உள்ளமைக்க இயலாது"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> மூலமாக"</string> <string name="appops_camera" msgid="8100147441602585776">"இந்த ஆப்ஸானது கேமராவை உபயோகிக்கிறது."</string> <string name="appops_microphone" msgid="741508267659494555">"இந்த ஆப்ஸானது, மைக்ரோஃபோனை உபயோகிக்கிறது."</string> @@ -727,7 +722,7 @@ <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"அறிவிப்புகள்"</string> <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"கீபோர்ட் ஷார்ட்கட்கள்"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="8413348767825486492">"கீபோர்டு லே அவுட்டை மாற்று"</string> - <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"பயன்பாடுகள்"</string> + <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"ஆப்ஸ்"</string> <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"அசிஸ்ட்"</string> <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"உலாவி"</string> <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"தொடர்புகள்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index ab562042e7a3..d87f11b7f1c6 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -41,7 +41,7 @@ <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string> <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"స్క్రీన్ ఆటో-రొటేట్"</string> <string name="status_bar_settings_mute_label" msgid="554682549917429396">"మ్యూట్"</string> - <string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"స్వయంచాలకం"</string> + <string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"ఆటోమేటిక్"</string> <string name="status_bar_settings_notifications" msgid="397146176280905137">"నోటిఫికేషన్లు"</string> <string name="bluetooth_tethered" msgid="7094101612161133267">"బ్లూటూత్ టీథర్ చేయబడింది"</string> <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ఇన్పుట్ పద్ధతులను సెటప్ చేయండి"</string> @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -225,7 +227,7 @@ <string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"మొత్తం నిశ్శబ్దం"</string> <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"అలారాలు మాత్రమే"</string> <string name="accessibility_quick_settings_dnd" msgid="5555155552520665891">"అంతరాయం కలిగించవద్దు."</string> - <string name="accessibility_quick_settings_dnd_changed_off" msgid="2757071272328547807">"అంతరాయం కలిగించవద్దు ఆఫ్ చేయబడింది."</string> + <string name="accessibility_quick_settings_dnd_changed_off" msgid="2757071272328547807">"\'అంతరాయం కలిగించవద్దు\' ఆఫ్ చేయబడింది."</string> <string name="accessibility_quick_settings_dnd_changed_on" msgid="6808220653747701059">"అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string> <string name="accessibility_quick_settings_bluetooth" msgid="6341675755803320038">"బ్లూటూత్."</string> <string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"బ్లూటూత్ ఆఫ్లో ఉంది."</string> @@ -277,8 +279,7 @@ <item quantity="other">లోపల మరో <xliff:g id="NUMBER_1">%s</xliff:g> నోటిఫికేషన్లు ఉన్నాయి.</item> <item quantity="one">లోపల మరో <xliff:g id="NUMBER_0">%s</xliff:g> నోటిఫికేషన్ ఉంది.</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"నోటిఫికేషన్ సెట్టింగ్లు"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> సెట్టింగ్లు"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"స్క్రీన్ స్వయంచాలకంగా తిప్పబడుతుంది."</string> @@ -337,7 +338,7 @@ <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"పరికరాలు ఏవీ అందుబాటులో లేవు"</string> <string name="quick_settings_cast_no_wifi" msgid="2696477881905521882">"Wi‑Fi కనెక్ట్ కాలేదు"</string> <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ప్రకాశం"</string> - <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"స్వయంచాలకం"</string> + <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ఆటోమేటిక్"</string> <string name="quick_settings_inversion_label" msgid="8790919884718619648">"రంగులను తారుమారు చేయి"</string> <string name="quick_settings_color_space_label" msgid="853443689745584770">"రంగు సవరణ మోడ్"</string> <string name="quick_settings_more_settings" msgid="326112621462813682">"మరిన్ని సెట్టింగ్లు"</string> @@ -518,7 +519,7 @@ <string name="accessibility_volume_settings" msgid="4915364006817819212">"ధ్వని సెట్టింగ్లు"</string> <string name="accessibility_volume_expand" msgid="5946812790999244205">"విస్తరింపజేయండి"</string> <string name="accessibility_volume_collapse" msgid="3609549593031810875">"కుదించండి"</string> - <string name="volume_odi_captions_tip" msgid="1193653197906918269">"ఆటోమేటిక్ క్యాప్షన్ మీడియా"</string> + <string name="volume_odi_captions_tip" msgid="1193653197906918269">"మీడియాకు ఆటోమేటిక్ శీర్షికలు"</string> <string name="accessibility_volume_close_odi_captions_tip" msgid="1163987066404128967">"ఉపశీర్షికల చిట్కాను మూసివేయండి"</string> <string name="accessibility_output_chooser" msgid="8185317493017988680">"పరికరం అవుట్పుట్ని మార్చండి"</string> <string name="screen_pinning_title" msgid="3273740381976175811">"స్క్రీన్ పిన్ చేయబడింది"</string> @@ -620,30 +621,24 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"ఈ నోటిఫికేషన్లు మిమ్మల్ని హెచ్చరిస్తాయి"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"మీరు సాధారణంగా ఈ నోటిఫికేషన్లను విస్మరిస్తారు. \nవాటి ప్రదర్శనను కొనసాగించాలా?"</string> <string name="inline_done_button" msgid="492513001558716452">"పూర్తయింది"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"వర్తింపజేయి"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"ఈ నోటిఫికేషన్లను చూపిస్తూ ఉండాలా?"</string> <string name="inline_stop_button" msgid="4172980096860941033">"నోటిఫికేషన్లను ఆపివేయి"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"నిశ్శబ్దంగా బట్వాడా చేయండి"</string> <string name="inline_block_button" msgid="8735843688021655065">"బ్లాక్ చేయి"</string> <string name="inline_keep_button" msgid="6665940297019018232">"చూపిస్తూనే ఉండు"</string> <string name="inline_minimize_button" msgid="966233327974702195">"కుదించు"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"సున్నితం"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"నిశబ్దంగా తెలియజేయి"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"అంతరాయం"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"ఎప్పటికప్పుడు హెచ్చరించు"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"నోటిఫికేషన్లను ఆఫ్ చేయి"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"ఈ యాప్ నుండి నోటిఫికేషన్లను చూపిస్తూ ఉండాలా?"</string> <string name="hint_text_block" msgid="3554459167504485284">"బ్లాక్ చేసిన నోటిఫికేషన్లు ఎక్కడా కనిపించవు, అలాగే శబ్దం ప్లే కాదు. మీరు సెట్టింగ్లలో నోటిఫికేషన్లను అన్బ్లాక్ చేయవచ్చు."</string> <string name="hint_text_silent" msgid="859468056340177016">"నిశ్శబ్ద నోటిఫికేషన్లు షేడ్లో కనిపిస్తాయి, కానీ లాక్ స్క్రీన్పై కనిపించవు, బ్యానర్లుగా అందించబడవు, అలాగే సౌండ్ ప్లే కాదు."</string> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_alert" msgid="2721169810318722524">"ఈ నోటిఫికేషన్లు శబ్దాన్ని చేస్తూ నోటిఫికేషన్ డ్రాయర్, స్థితి పట్టీ మరియు లాక్ స్క్రీన్లో చూపుతాయి"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"ఈ నోటిఫికేషన్లను ఆఫ్ చేయలేరు"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"ఈ నోటిఫికేషన్ల సమూహాన్ని ఇక్కడ కాన్ఫిగర్ చేయలేము"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"<xliff:g id="APP_NAME">%1$s</xliff:g> ద్వారా"</string> <string name="appops_camera" msgid="8100147441602585776">"ఈ యాప్ ఈ కెమెరాను ఉపయోగిస్తోంది."</string> <string name="appops_microphone" msgid="741508267659494555">"ఈ యాప్ మైక్రోఫోన్ను ఉపయోగిస్తుంది."</string> @@ -724,7 +719,7 @@ <string name="tuner_full_zen_title" msgid="4540823317772234308">"వాల్యూమ్ నియంత్రణలతో చూపు"</string> <string name="volume_and_do_not_disturb" msgid="1750270820297253561">"అంతరాయం కలిగించవద్దు"</string> <string name="volume_dnd_silent" msgid="4363882330723050727">"వాల్యూమ్ బటన్ల షార్ట్కట్"</string> - <string name="volume_up_silent" msgid="7545869833038212815">"వాల్యూమ్ పెంచితే అంతరాయం కలిగించవద్దు నుండి నిష్క్రమిస్తుంది"</string> + <string name="volume_up_silent" msgid="7545869833038212815">"వాల్యూమ్ పెంచితే \'అంతరాయం కలిగించవద్దు\' ను ఆపివేస్తుంది"</string> <string name="battery" msgid="7498329822413202973">"బ్యాటరీ"</string> <string name="clock" msgid="7416090374234785905">"గడియారం"</string> <string name="headset" msgid="4534219457597457353">"హెడ్సెట్"</string> @@ -911,7 +906,7 @@ <string name="bubbles_prompt" msgid="8807968030159469710">"<xliff:g id="APP_NAME">%1$s</xliff:g> నుండి బబుల్లను అనుమతించాలా?"</string> <string name="no_bubbles" msgid="337101288173078247">"తిరస్కరించు"</string> <string name="yes_bubbles" msgid="668809525728633841">"అనుమతించు"</string> - <string name="ask_me_later_bubbles" msgid="2147688438402939029">"నన్ను తర్వాత అడగండి"</string> + <string name="ask_me_later_bubbles" msgid="2147688438402939029">"నన్ను తర్వాత అడగు"</string> <string name="bubble_content_description_single" msgid="1184462974339387516">"<xliff:g id="APP_NAME">%2$s</xliff:g> నుండి <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string> <string name="bubble_content_description_stack" msgid="8666349184095622232">"<xliff:g id="APP_NAME">%2$s</xliff:g> నుండి <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> మరియు మరో <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string> <string name="bubble_accessibility_action_move" msgid="1794879742234803840">"తరలించు"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 0068ea6a5d87..98d45a19ebdc 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -706,7 +707,7 @@ <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"แป้นพิมพ์ลัด"</string> <string name="keyboard_shortcut_group_system_switch_input" msgid="8413348767825486492">"สลับรูปแบบแป้นพิมพ์"</string> <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"แอปพลิเคชัน"</string> - <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"การสนับสนุน"</string> + <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"ผู้ช่วย"</string> <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"เบราว์เซอร์"</string> <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"รายชื่อติดต่อ"</string> <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"อีเมล"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 4b843a3586eb..5c5180c601b1 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 793ac7231b39..3e39588b924f 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -172,6 +172,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 110d76ef9ec4..5b66836daa54 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index b4372d9c923d..5205250397cf 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -175,6 +175,8 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <!-- no translation found for data_connection_5ge (4699478963278829331) --> + <skip /> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -284,8 +286,7 @@ <item quantity="other">اندر <xliff:g id="NUMBER_1">%s</xliff:g> مزید اطلاعات ہیں۔ </item> <item quantity="one">اندر <xliff:g id="NUMBER_0">%s</xliff:g> مزید اطلاع ہے۔</item> </plurals> - <!-- no translation found for notification_summary_message_format (715071952312553396) --> - <skip /> + <string name="notification_summary_message_format" msgid="715071952312553396">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string> <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"اطلاع کی ترتیبات"</string> <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> ترتیبات"</string> <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"اسکرین خود بخود گردش کرے گی۔"</string> @@ -632,32 +633,26 @@ <string name="notification_channel_unsilenced" msgid="4790904571552394137">"یہ اطلاعات آپ کو الرٹ کریں گی"</string> <string name="inline_blocking_helper" msgid="3055064577771478591">"آپ عام طور پر ان اطلاعات کو مسترد کرتے ہیں۔ \nان کو دکھاتے رہیں؟"</string> <string name="inline_done_button" msgid="492513001558716452">"ہو گیا"</string> - <!-- no translation found for inline_ok_button (975600017662930615) --> - <skip /> + <string name="inline_ok_button" msgid="975600017662930615">"لاگو کریں"</string> <string name="inline_keep_showing" msgid="8945102997083836858">"یہ اطلاعات دکھانا جاری رکھیں؟"</string> <string name="inline_stop_button" msgid="4172980096860941033">"اطلاعات روک دیں"</string> <string name="inline_deliver_silently_button" msgid="7756289895745629140">"خاموشی سے ڈیلیور کریں"</string> <string name="inline_block_button" msgid="8735843688021655065">"مسدود کریں"</string> <string name="inline_keep_button" msgid="6665940297019018232">"دکھانا جاری رکھیں"</string> <string name="inline_minimize_button" msgid="966233327974702195">"چھوٹا کریں"</string> - <!-- no translation found for inline_silent_button_silent (6904727667411781466) --> - <skip /> + <string name="inline_silent_button_silent" msgid="6904727667411781466">"لطیف"</string> <string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"خاموش رہیں"</string> - <!-- no translation found for inline_silent_button_alert (2449191160203602471) --> - <skip /> + <string name="inline_silent_button_alert" msgid="2449191160203602471">"خلل انداز"</string> <string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"متنبہ کرنا جاری رکھیں"</string> - <!-- no translation found for inline_turn_off_notifications (8635596135532202355) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="8635596135532202355">"اطلاعات کو آف کریں"</string> <string name="inline_keep_showing_app" msgid="1723113469580031041">"اس ایپ کی طرف سے اطلاعات دکھانا جاری رکھیں؟"</string> <!-- no translation found for hint_text_block (3554459167504485284) --> <skip /> <!-- no translation found for hint_text_silent (859468056340177016) --> <skip /> - <!-- no translation found for hint_text_alert (2721169810318722524) --> - <skip /> + <string name="hint_text_alert" msgid="2721169810318722524">"یہ اطلاعات آواز پیدا کریں گی اور اطلاعاتی دراز، اسٹیٹس بار میں اور مقفل اسکرین پر ظاہر ہوں گی"</string> <string name="notification_unblockable_desc" msgid="1037434112919403708">"ان اطلاعات کو آف نہیں کیا جا سکتا"</string> - <!-- no translation found for notification_multichannel_desc (4695920306092240550) --> - <skip /> + <string name="notification_multichannel_desc" msgid="4695920306092240550">"اطلاعات کے اس گروپ کو یہاں کنفیگر نہیں کیا جا سکتا"</string> <string name="notification_delegate_header" msgid="9167022191405284627">"بذریعہ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="appops_camera" msgid="8100147441602585776">"یہ ایپ کیمرے کا استعمال کر رہی ہے۔"</string> <string name="appops_microphone" msgid="741508267659494555">"یہ ایپ مائیکروفون کا استعمال کر رہی ہے۔"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 292f4ef6e731..52253058468e 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -160,7 +160,7 @@ <string name="accessibility_three_bars" msgid="2648241415119396648">"Uchta ustun."</string> <string name="accessibility_signal_full" msgid="9122922886519676839">"Signal to‘liq."</string> <string name="accessibility_desc_on" msgid="2385254693624345265">"Yoniq"</string> - <string name="accessibility_desc_off" msgid="6475508157786853157">"O‘chiq"</string> + <string name="accessibility_desc_off" msgid="6475508157786853157">"Yoqilmagan"</string> <string name="accessibility_desc_connected" msgid="8366256693719499665">"Ulangan."</string> <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Ulanmoqda…"</string> <string name="data_connection_gprs" msgid="7652872568358508452">"GPRS"</string> @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -189,7 +190,7 @@ <string name="accessibility_vpn_on" msgid="5993385083262856059">"VPN yoniq."</string> <string name="accessibility_no_sims" msgid="3957997018324995781">"SIM karta yo‘q."</string> <string name="carrier_network_change_mode" msgid="8149202439957837762">"Mobil tarmoqni o‘zgartirish"</string> - <string name="accessibility_battery_details" msgid="7645516654955025422">"Batareya quvvati sarfi haqida ma’lumot"</string> + <string name="accessibility_battery_details" msgid="7645516654955025422">"Quvvat sarfi tafsilotlari"</string> <string name="accessibility_battery_level" msgid="7451474187113371965">"Batareya <xliff:g id="NUMBER">%d</xliff:g> foiz."</string> <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"Batareya quvvat olmoqda (<xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%)."</string> <string name="accessibility_settings_button" msgid="799583911231893380">"Tizim sozlamalari."</string> @@ -604,13 +605,13 @@ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Bluetooth yoqilsinmi?"</string> <string name="enable_bluetooth_message" msgid="9106595990708985385">"Klaviaturani planshetingizga ulash uchun Bluetooth xizmatini yoqishingiz kerak."</string> <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Yoqish"</string> - <string name="show_silently" msgid="6841966539811264192">"Bildirishnomalar ovozsiz ko‘rsatilsin"</string> + <string name="show_silently" msgid="6841966539811264192">"Tovushsiz chiqsin"</string> <string name="block" msgid="2734508760962682611">"Barcha bildirishnomalar bloklansin"</string> <string name="do_not_silence" msgid="6878060322594892441">"Ovozi o‘chirilmasin"</string> <string name="do_not_silence_block" msgid="4070647971382232311">"Ovozi o‘chirilmasin yoki bloklanmasin"</string> <string name="tuner_full_importance_settings" msgid="3207312268609236827">"Bildirishnomalar uchun kengaytirilgan boshqaruv"</string> <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"Yoniq"</string> - <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"O‘chiq"</string> + <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"Yoqilmagan"</string> <string name="power_notification_controls_description" msgid="4372459941671353358">"Bildirishnomalar uchun kengaytirilgan boshqaruv yordamida ilova bildirishnomalarining muhimlik darajasini (0-5) sozlash mumkin. \n\n"<b>"5-daraja"</b>" \n- Bildirishnomani ro‘yxatning boshida ko‘rsatish \n- To‘liq ekranli bildirishnomalarni ko‘rsatish \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"4-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatish \n\n"<b>"3-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n\n"<b>"2-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n\n"<b>"1-daraja"</b>" \n- To‘liq ekranli bildirishnomalarni ko‘rsatmaslik \n- Qalqib chiquvchi bildirishnomalarni ko‘rsatmaslik \n- Ovoz va tebranishdan foydalanmaslik \n- Ekran qulfi va holat qatorida ko‘rsatmaslik \n- Bildirishnomani ro‘yxatning oxirida ko‘rsatish \n\n"<b>"0-daraja"</b>" \n- Ilovadan keladigan barcha bildirishnomalarni bloklash"</string> <string name="notification_header_default_channel" msgid="7506845022070889909">"Bildirishnomalar"</string> <string name="notification_channel_disabled" msgid="344536703863700565">"Bu bildirishnomalar endi chiqmaydi"</string> @@ -728,7 +729,7 @@ <string name="accessibility_data_saver_on" msgid="8454111686783887148">"Trafik tejash yoniq"</string> <string name="accessibility_data_saver_off" msgid="8841582529453005337">"Trafik tejash o‘chiq"</string> <string name="switch_bar_on" msgid="1142437840752794229">"Yoniq"</string> - <string name="switch_bar_off" msgid="8803270596930432874">"O‘chiq"</string> + <string name="switch_bar_off" msgid="8803270596930432874">"Yoqilmagan"</string> <string name="nav_bar" msgid="1993221402773877607">"Navigatsiya paneli"</string> <string name="nav_bar_layout" msgid="3664072994198772020">"Sxema"</string> <string name="left_nav_bar_button_type" msgid="8555981238887546528">"Qo‘shimcha Chapga tugmasi turi"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 5db23247dc62..7c95e4e995a6 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G trở lên"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> @@ -362,7 +363,7 @@ <string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Giới hạn <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Cảnh báo <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string> <string name="quick_settings_work_mode_label" msgid="7608026833638817218">"Hồ sơ công việc"</string> - <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Chế độ ánh sáng ban đêm"</string> + <string name="quick_settings_night_display_label" msgid="3577098011487644395">"Ánh sáng đêm"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Bật khi trời tối"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Cho đến khi trời sáng"</string> <string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"Bật vào lúc <xliff:g id="TIME">%s</xliff:g>"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index ea35df9cc07f..8f014d2a3f2c 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 0245e56e0cc0..cdbea3d44b38 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5GE"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 01b5c7af7fc9..e0a4a50e1ad7 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 1f465ccb8539..c587f314b351 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -172,6 +172,7 @@ <string name="data_connection_4g_plus" msgid="1148687201877800700">"4G+"</string> <string name="data_connection_lte" msgid="2694876797724028614">"I-LTE"</string> <string name="data_connection_lte_plus" msgid="3423013208570937424">"I-LTE+"</string> + <string name="data_connection_5ge" msgid="4699478963278829331">"5Ge"</string> <string name="data_connection_5g" msgid="6357743323196864504">"5G"</string> <string name="data_connection_5g_plus" msgid="3284146603743732965">"5G+"</string> <string name="data_connection_cdma" msgid="8176597308239086780">"1X"</string> diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml index 41e078699647..ced26c95dfd9 100644 --- a/packages/SystemUI/res/values/attrs_car.xml +++ b/packages/SystemUI/res/values/attrs_car.xml @@ -15,16 +15,23 @@ --> <resources> + <attr name="icon" format="reference"/> + <attr name="selectedIcon" format="reference"/> + <attr name="intent" format="string"/> + <attr name="longIntent" format="string"/> + <attr name="selectedAlpha" format="float" /> + <attr name="unselectedAlpha" format="float" /> + <!-- Allow for custom attribs to be added to a facet button --> <declare-styleable name="CarFacetButton"> <!-- icon to be rendered (drawable) --> - <attr name="icon" format="reference"/> + <attr name="icon"/> <!-- icon to be rendered when in selected state --> - <attr name="selectedIcon" format="reference"/> + <attr name="selectedIcon"/> <!-- intent to start when button is click --> - <attr name="intent" format="string"/> + <attr name="intent"/> <!-- intent to start when a long press has happened --> - <attr name="longIntent" format="string"/> + <attr name="longIntent"/> <!-- categories that will be added as extras to the fired intents --> <attr name="categories" format="string"/> <!-- package names that will be added as extras to the fired intents --> @@ -32,9 +39,9 @@ <!-- componentName names that will be used for detecting selected state --> <attr name="componentNames" format="string" /> <!-- Alpha value to used when in selected state. Defaults 1f --> - <attr name="selectedAlpha" format="float" /> + <attr name="selectedAlpha" /> <!-- Alpha value to used when in un-selected state. Defaults 0.7f --> - <attr name="unselectedAlpha" format="float" /> + <attr name="unselectedAlpha" /> <!-- Render a "more" icon. Defaults true --> <attr name="useMoreIcon" format="boolean" /> @@ -44,17 +51,17 @@ <!-- Allow for custom attribs to be added to a nav button --> <declare-styleable name="CarNavigationButton"> <!-- intent to start when button is click --> - <attr name="intent" format="string"/> + <attr name="intent" /> <!-- intent to start when a long press has happened --> - <attr name="longIntent" format="string"/> + <attr name="longIntent" /> <!-- start the intent as a broad cast instead of an activity if true--> <attr name="broadcast" format="boolean"/> <!-- Alpha value to used when in selected state. Defaults 1f --> - <attr name="selectedAlpha" format="float" /> + <attr name="selectedAlpha" /> <!-- Alpha value to used when in un-selected state. Defaults 0.7f --> - <attr name="unselectedAlpha" format="float" /> + <attr name="unselectedAlpha" /> <!-- icon to be rendered when in selected state --> - <attr name="selectedIcon" format="reference"/> + <attr name="selectedIcon" /> </declare-styleable> <!-- Custom attributes to configure hvac values --> @@ -89,6 +96,6 @@ </attr> <!-- Icon resource ids to render on UI --> - <attr name="icon" format="reference"/> + <attr name="icon" /> </declare-styleable> </resources> diff --git a/packages/SystemUI/res/values/internal.xml b/packages/SystemUI/res/values/internal.xml index 930cfce65b61..c29a51f6178e 100644 --- a/packages/SystemUI/res/values/internal.xml +++ b/packages/SystemUI/res/values/internal.xml @@ -17,6 +17,7 @@ <resources> <dimen name="status_bar_height">@*android:dimen/status_bar_height</dimen> <dimen name="navigation_bar_height">@*android:dimen/navigation_bar_height</dimen> + <dimen name="navigation_bar_frame_height">@*android:dimen/navigation_bar_frame_height</dimen> <dimen name="navigation_bar_height_car_mode">@*android:dimen/navigation_bar_height_car_mode</dimen> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index f47d4b5aac6d..e098bc5acd5c 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -424,6 +424,9 @@ <!-- Content description of the data connection type LTE+. [CHAR LIMIT=NONE] --> <string name="data_connection_lte_plus">LTE+</string> + <!-- Content description of the data connection type 5Ge. [CHAR LIMIT=NONE] --> + <string name="data_connection_5ge" translate="false">5Ge</string> + <!-- Content description of the data connection type 5G. [CHAR LIMIT=NONE] --> <string name="data_connection_5g" translate="false">5G</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl index 04701bcf7c77..577e3bbefdad 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl @@ -124,9 +124,14 @@ oneway interface IOverviewProxy { */ void onAssistantVisibilityChanged(float visibility) = 14; - /* + /** * Sent when back is triggered. */ void onBackAction(boolean completed, int downX, int downY, boolean isButton, boolean gestureSwipeLeft) = 15; + + /** + * Sent when some system ui state changes. + */ + void onSystemUiStateChanged(int stateFlags) = 16; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 1076e73d1072..b36a88b909b3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -20,12 +20,16 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; +import android.annotation.IntDef; import android.content.Context; import android.content.res.Resources; import android.view.WindowManagerPolicyConstants; import com.android.internal.policy.ScreenDecorationsUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Various shared constants between Launcher and SysUI as part of quickstep */ @@ -44,6 +48,17 @@ public class QuickStepContract { public static final String NAV_BAR_MODE_GESTURAL_OVERLAY = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY; + public static final int SYSUI_STATE_SCREEN_PINNING = 1 << 0; + public static final int SYSUI_STATE_NAV_BAR_HIDDEN = 1 << 1; + public static final int SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED = 1 << 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SYSUI_STATE_SCREEN_PINNING, + SYSUI_STATE_NAV_BAR_HIDDEN, + SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED + }) + public @interface SystemUiStateFlags {} + /** * Touch slopes and thresholds for quick step operations. Drag slop is the point where the * home button press/long press over are ignored and will start to drag when exceeded and the diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index 1d9105c35ac5..d2fe5cd9ef64 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -77,9 +77,15 @@ public class RecentsAnimationControllerCompat { } } - public void finish(boolean toHome) { + /** + * Finish the current recents animation. + * @param toHome Going to home or back to the previous app. + * @param sendUserLeaveHint determines whether userLeaveHint will be set true to the previous + * app. + */ + public void finish(boolean toHome, boolean sendUserLeaveHint) { try { - mAnimationController.finish(toHome); + mAnimationController.finish(toHome, sendUserLeaveHint); } catch (RemoteException e) { Log.e(TAG, "Failed to finish recents animation", e); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index b738b576ef2f..70366a839da5 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -138,6 +138,7 @@ public class KeyguardClockSwitch extends RelativeLayout { ClockManager clockManager) { super(context, attrs); mStatusBarStateController = statusBarStateController; + mStatusBarState = mStatusBarStateController.getState(); mSysuiColorExtractor = colorExtractor; mClockManager = clockManager; mTransition = new ClockBoundsTransition(); diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java index 59ee2679ca3d..7ffee5d5232d 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java @@ -37,7 +37,6 @@ public class ClockLayout extends FrameLayout { */ private View mDigitalClock; private View mAnalogClock; - private View mTypeClock; /** * Pixel shifting amplitidues used to prevent screen burn-in. @@ -62,7 +61,6 @@ public class ClockLayout extends FrameLayout { super.onFinishInflate(); mDigitalClock = findViewById(R.id.digital_clock); mAnalogClock = findViewById(R.id.analog_clock); - mTypeClock = findViewById(R.id.type_clock); // Get pixel shifting X, Y amplitudes from resources. Resources resources = getResources(); @@ -95,11 +93,5 @@ public class ClockLayout extends FrameLayout { mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight())) + offsetY); } - - // Put the typographic clock part way down the screen. - if (mTypeClock != null) { - mTypeClock.setX(offsetX); - mTypeClock.setY(0.2f * getHeight() + offsetY); - } } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 64e56f955058..e373ca1d955c 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -20,8 +20,10 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; +import android.net.Uri; import android.os.Handler; import android.os.Looper; +import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; import android.util.DisplayMetrics; @@ -35,6 +37,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManager.DockEventListener; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.PluginListener; +import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.InjectionInflationController; @@ -61,6 +64,7 @@ public final class ClockManager { private final ContentResolver mContentResolver; private final SettingsWrapper mSettingsWrapper; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); + private final CurrentUserTracker mCurrentUserTracker; /** * Observe settings changes to know when to switch the clock face. @@ -68,9 +72,11 @@ public final class ClockManager { private final ContentObserver mContentObserver = new ContentObserver(mMainHandler) { @Override - public void onChange(boolean selfChange) { - super.onChange(selfChange); - reload(); + public void onChange(boolean selfChange, Uri uri, int userId) { + super.onChange(selfChange, uri, userId); + if (userId == mCurrentUserTracker.getCurrentUserId()) { + reload(); + } } }; @@ -123,6 +129,12 @@ public final class ClockManager { mPluginManager = pluginManager; mContentResolver = contentResolver; mSettingsWrapper = settingsWrapper; + mCurrentUserTracker = new CurrentUserTracker(context) { + @Override + public void onUserSwitched(int newUserId) { + reload(); + } + }; mPreviewClocks = new AvailableClocks(); Resources res = context.getResources(); @@ -132,7 +144,6 @@ public final class ClockManager { addBuiltinClock(() -> new BubbleClockController(res, layoutInflater, colorExtractor)); addBuiltinClock(() -> new StretchAnalogClockController(res, layoutInflater, colorExtractor)); - addBuiltinClock(() -> new TypeClockController(res, layoutInflater, colorExtractor)); // Store the size of the display for generation of clock preview. DisplayMetrics dm = res.getDisplayMetrics(); @@ -203,10 +214,11 @@ public final class ClockManager { mPluginManager.addPluginListener(mPreviewClocks, ClockPlugin.class, true); mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), - false, mContentObserver); + false, mContentObserver, UserHandle.USER_ALL); mContentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE), - false, mContentObserver); + false, mContentObserver, UserHandle.USER_ALL); + mCurrentUserTracker.startTracking(); if (mDockManager == null) { mDockManager = SysUiServiceProvider.getComponent(mContext, DockManager.class); } @@ -218,6 +230,7 @@ public final class ClockManager { private void unregister() { mPluginManager.removePluginListener(mPreviewClocks); mContentResolver.unregisterContentObserver(mContentObserver); + mCurrentUserTracker.stopTracking(); if (mDockManager != null) { mDockManager.removeListener(mDockEventListener); } @@ -334,7 +347,8 @@ public final class ClockManager { private ClockPlugin getClockPlugin() { ClockPlugin plugin = null; if (ClockManager.this.isDocked()) { - final String name = mSettingsWrapper.getDockedClockFace(); + final String name = mSettingsWrapper.getDockedClockFace( + mCurrentUserTracker.getCurrentUserId()); if (name != null) { plugin = mClocks.get(name); if (plugin != null) { @@ -342,7 +356,8 @@ public final class ClockManager { } } } - final String name = mSettingsWrapper.getLockScreenCustomClockFace(); + final String name = mSettingsWrapper.getLockScreenCustomClockFace( + mCurrentUserTracker.getCurrentUserId()); if (name != null) { plugin = mClocks.get(name); } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java index 58e11553af9d..e1c658be4c26 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java @@ -34,15 +34,19 @@ public class SettingsWrapper { /** * Gets the value stored in settings for the custom clock face. + * + * @param userId ID of the user. */ - public String getLockScreenCustomClockFace() { - return Settings.Secure.getString(mContentResolver, CUSTOM_CLOCK_FACE); + public String getLockScreenCustomClockFace(int userId) { + return Settings.Secure.getStringForUser(mContentResolver, CUSTOM_CLOCK_FACE, userId); } /** * Gets the value stored in settings for the clock face to use when docked. + * + * @param userId ID of the user. */ - public String getDockedClockFace() { - return Settings.Secure.getString(mContentResolver, DOCKED_CLOCK_FACE); + public String getDockedClockFace(int userId) { + return Settings.Secure.getStringForUser(mContentResolver, DOCKED_CLOCK_FACE, userId); } } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java deleted file mode 100644 index 1c6b38b18b05..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/TypeClockController.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.app.WallpaperManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.graphics.Paint.Style; -import android.view.LayoutInflater; -import android.view.View; - -import com.android.internal.colorextraction.ColorExtractor; -import com.android.keyguard.R; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.plugins.ClockPlugin; - -import java.util.TimeZone; - -/** - * Plugin for a custom Typographic clock face that displays the time in words. - */ -public class TypeClockController implements ClockPlugin { - - /** - * Resources used to get title and thumbnail. - */ - private final Resources mResources; - - /** - * LayoutInflater used to inflate custom clock views. - */ - private final LayoutInflater mLayoutInflater; - - /** - * Extracts accent color from wallpaper. - */ - private final SysuiColorExtractor mColorExtractor; - - /** - * Renders preview from clock view. - */ - private final ViewPreviewer mRenderer = new ViewPreviewer(); - - /** - * Custom clock shown on AOD screen and behind stack scroller on lock. - */ - private View mView; - private TypographicClock mTypeClock; - - /** - * Small clock shown on lock screen above stack scroller. - */ - private TypographicClock mLockClock; - - /** - * Controller for transition into dark state. - */ - private CrossFadeDarkController mDarkController; - - /** - * Create a TypeClockController instance. - * - * @param res Resources contains title and thumbnail. - * @param inflater Inflater used to inflate custom clock views. - * @param colorExtractor Extracts accent color from wallpaper. - */ - TypeClockController(Resources res, LayoutInflater inflater, - SysuiColorExtractor colorExtractor) { - mResources = res; - mLayoutInflater = inflater; - mColorExtractor = colorExtractor; - } - - private void createViews() { - mView = mLayoutInflater.inflate(R.layout.type_aod_clock, null); - mTypeClock = mView.findViewById(R.id.type_clock); - - // For now, this view is used to hide the default digital clock. - // Need better transition to lock screen. - mLockClock = (TypographicClock) mLayoutInflater.inflate(R.layout.typographic_clock, null); - mLockClock.setVisibility(View.GONE); - - mDarkController = new CrossFadeDarkController(mView, mLockClock); - } - - @Override - public void onDestroyView() { - mView = null; - mTypeClock = null; - mLockClock = null; - mDarkController = null; - } - - @Override - public String getName() { - return "type"; - } - - @Override - public String getTitle() { - return mResources.getString(R.string.clock_title_type); - } - - @Override - public Bitmap getThumbnail() { - return BitmapFactory.decodeResource(mResources, R.drawable.type_thumbnail); - } - - @Override - public Bitmap getPreview(int width, int height) { - - // Use the big clock view for the preview - View view = getBigClockView(); - - // Initialize state of plugin before generating preview. - setDarkAmount(1f); - setTextColor(Color.WHITE); - ColorExtractor.GradientColors colors = mColorExtractor.getColors( - WallpaperManager.FLAG_LOCK, true); - setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); - onTimeTick(); - - return mRenderer.createPreview(view, width, height); - } - - @Override - public View getView() { - if (mLockClock == null) { - createViews(); - } - return mLockClock; - } - - @Override - public View getBigClockView() { - if (mView == null) { - createViews(); - } - return mView; - } - - @Override - public void setStyle(Style style) {} - - @Override - public void setTextColor(int color) { - mTypeClock.setTextColor(color); - mLockClock.setTextColor(color); - } - - @Override - public void setColorPalette(boolean supportsDarkText, int[] colorPalette) { - if (colorPalette == null || colorPalette.length == 0) { - return; - } - final int color = colorPalette[Math.max(0, colorPalette.length - 5)]; - mTypeClock.setClockColor(color); - mLockClock.setClockColor(color); - } - - @Override - public void onTimeTick() { - mTypeClock.onTimeChanged(); - mLockClock.onTimeChanged(); - } - - @Override - public void setDarkAmount(float darkAmount) { - if (mDarkController != null) { - mDarkController.setDarkAmount(darkAmount); - } - } - - @Override - public void onTimeZoneChanged(TimeZone timeZone) { - mTypeClock.onTimeZoneChanged(timeZone); - mLockClock.onTimeZoneChanged(timeZone); - } - - @Override - public boolean shouldShowStatusArea() { - return false; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java b/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java deleted file mode 100644 index 572ab30d7019..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/TypographicClock.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.content.Context; -import android.content.res.Resources; -import android.text.Annotation; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.SpannedString; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.style.ForegroundColorSpan; -import android.util.AttributeSet; -import android.widget.TextView; - -import com.android.keyguard.R; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.TimeZone; - -/** - * Clock that presents the time in words. - */ -public class TypographicClock extends TextView { - - private static final String ANNOTATION_COLOR = "color"; - - private final Resources mResources; - private final String[] mHours; - private final String[] mMinutes; - private int mAccentColor; - private final Calendar mTime = Calendar.getInstance(TimeZone.getDefault()); - private String mDescFormat; - private TimeZone mTimeZone; - - public TypographicClock(Context context) { - this(context, null); - } - - public TypographicClock(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public TypographicClock(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern(); - mResources = context.getResources(); - mHours = mResources.getStringArray(R.array.type_clock_hours); - mMinutes = mResources.getStringArray(R.array.type_clock_minutes); - mAccentColor = mResources.getColor(R.color.typeClockAccentColor, null); - } - - /** - * Call when the time changes to update the text of the time. - */ - public void onTimeChanged() { - mTime.setTimeInMillis(System.currentTimeMillis()); - setContentDescription(DateFormat.format(mDescFormat, mTime)); - final int hour = mTime.get(Calendar.HOUR) % 12; - final int minute = mTime.get(Calendar.MINUTE) % 60; - - // Get the quantity based on the hour for languages like Portuguese and Czech. - SpannedString typeTemplate = (SpannedString) mResources.getQuantityText( - R.plurals.type_clock_header, hour); - - // Find the "color" annotation and set the foreground color to the accent color. - Annotation[] annotations = typeTemplate.getSpans(0, typeTemplate.length(), - Annotation.class); - SpannableString spanType = new SpannableString(typeTemplate); - for (int i = 0; i < annotations.length; i++) { - Annotation annotation = annotations[i]; - String key = annotation.getValue(); - if (ANNOTATION_COLOR.equals(key)) { - spanType.setSpan(new ForegroundColorSpan(mAccentColor), - spanType.getSpanStart(annotation), spanType.getSpanEnd(annotation), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - - setText(TextUtils.expandTemplate(spanType, mHours[hour], mMinutes[minute])); - } - - /** - * Call when the time zone has changed to update clock time. - * - * @param timeZone The updated time zone that will be used. - */ - public void onTimeZoneChanged(TimeZone timeZone) { - mTimeZone = timeZone; - mTime.setTimeZone(timeZone); - } - - /** - * Sets the accent color used on the clock face. - */ - public void setClockColor(int color) { - mAccentColor = color; - onTimeChanged(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); - onTimeChanged(); - } - - /** - * Overriding hasOverlappingRendering as false to improve performance of crossfading. - */ - @Override - public boolean hasOverlappingRendering() { - return false; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index dfa9c95b022b..93effed5958e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -399,11 +399,8 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe return; } if (shouldAutoBubbleForFlags(mContext, entry) || shouldBubble(entry)) { - // TODO: handle group summaries - boolean suppressNotification = entry.getBubbleMetadata() != null - && entry.getBubbleMetadata().getSuppressInitialNotification() - && isForegroundApp(entry.notification.getPackageName()); - entry.setShowInShadeWhenBubble(!suppressNotification); + // TODO: handle group summaries? + updateShowInShadeForSuppressNotification(entry); } } @@ -424,7 +421,7 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe } if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry) && alertAgain(entry, entry.notification.getNotification())) { - entry.setShowInShadeWhenBubble(true); + updateShowInShadeForSuppressNotification(entry); entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed updateBubble(entry); mStackView.updateDotVisibility(entry.key); @@ -591,6 +588,13 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe && isForegroundApp(entry.notification.getPackageName()); } + private void updateShowInShadeForSuppressNotification(NotificationEntry entry) { + boolean suppressNotification = entry.getBubbleMetadata() != null + && entry.getBubbleMetadata().getSuppressNotification() + && isForegroundApp(entry.notification.getPackageName()); + entry.setShowInShadeWhenBubble(!suppressNotification); + } + /** * Return true if the applications with the package name is running in foreground. * diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 6b21526a70c9..285d4aab4f66 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -229,6 +229,9 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList true /* singleTaskInstance */); addView(mActivityView); + // Make sure pointer is below activity view + bringChildToFront(mPointerView); + setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> { // Keep track of IME displaying because we should not make any adjustments that might // cause a config change while the IME is displayed otherwise it'll loose focus. diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java index a74c3287e1f1..05665b5ae4a2 100644 --- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java +++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java @@ -34,6 +34,7 @@ import com.android.internal.colorextraction.types.ExtractionType; import com.android.internal.colorextraction.types.Tonal; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; +import com.android.systemui.statusbar.policy.ConfigurationController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -46,7 +47,8 @@ import javax.inject.Singleton; * ColorExtractor aware of wallpaper visibility */ @Singleton -public class SysuiColorExtractor extends ColorExtractor implements Dumpable { +public class SysuiColorExtractor extends ColorExtractor implements Dumpable, + ConfigurationController.ConfigurationListener { private static final String TAG = "SysuiColorExtractor"; private final Tonal mTonal; private boolean mWallpaperVisible; @@ -55,15 +57,17 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable { private final GradientColors mWpHiddenColors; @Inject - public SysuiColorExtractor(Context context) { - this(context, new Tonal(context), true); + public SysuiColorExtractor(Context context, ConfigurationController configurationController) { + this(context, new Tonal(context), configurationController, true); } @VisibleForTesting - public SysuiColorExtractor(Context context, ExtractionType type, boolean registerVisibility) { + public SysuiColorExtractor(Context context, ExtractionType type, + ConfigurationController configurationController, boolean registerVisibility) { super(context, type, false /* immediately */); mTonal = type instanceof Tonal ? (Tonal) type : new Tonal(context); mWpHiddenColors = new GradientColors(); + configurationController.addCallback(this); WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM); updateDefaultGradients(systemColors); @@ -113,8 +117,21 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable { } } - @VisibleForTesting - GradientColors getFallbackColors() { + @Override + public void onUiModeChanged() { + WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM); + updateDefaultGradients(systemColors); + } + + /** + * Colors the should be using for scrims. + * + * They will be: + * - A light gray if the wallpaper is light + * - A dark gray if the wallpaper is very dark or we're in night mode. + * - Black otherwise + */ + public GradientColors getNeutralColors() { return mWpHiddenColors; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 77180f8cc07a..831d07446e7c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -228,9 +228,9 @@ public class DozeSensors { /** Dump current state */ public void dump(PrintWriter pw) { for (TriggerSensor s : mSensors) { - pw.print("Sensor: "); pw.println(s.toString()); + pw.print(" Sensor: "); pw.println(s.toString()); } - pw.print("ProxSensor: "); pw.println(mProxSensor.toString()); + pw.print(" ProxSensor: "); pw.println(mProxSensor.toString()); } /** diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 7a3f3bef8f5e..411536c2ddbb 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -35,7 +35,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.UserInfo; import android.database.ContentObserver; -import android.graphics.Point; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.net.ConnectivityManager; @@ -73,7 +72,7 @@ import android.widget.TextView; import com.android.internal.R; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.colorextraction.ColorExtractor.GradientColors; -import com.android.internal.colorextraction.drawable.GradientDrawable; +import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.telephony.TelephonyIntents; @@ -1503,7 +1502,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final MyAdapter mAdapter; private MultiListLayout mGlobalActionsLayout; private Drawable mBackgroundDrawable; - private final ColorExtractor mColorExtractor; + private final SysuiColorExtractor mColorExtractor; private final GlobalActionsPanelPlugin.PanelViewController mPanelController; private boolean mKeyguardShowing; private boolean mShowing; @@ -1582,7 +1581,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (!shouldUsePanel()) { if (mBackgroundDrawable == null) { - mBackgroundDrawable = new GradientDrawable(mContext); + mBackgroundDrawable = new ScrimDrawable(); } mScrimAlpha = ScrimController.GRADIENT_SCRIM_ALPHA; } else { @@ -1610,16 +1609,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, super.onStart(); mGlobalActionsLayout.updateList(); - if (mBackgroundDrawable instanceof GradientDrawable) { - Point displaySize = new Point(); - mContext.getDisplay().getRealSize(displaySize); + if (mBackgroundDrawable instanceof ScrimDrawable) { mColorExtractor.addOnColorsChangedListener(this); - ((GradientDrawable) mBackgroundDrawable) - .setScreenSize(displaySize.x, displaySize.y); - GradientColors colors = mColorExtractor.getColors( - mKeyguardShowing - ? WallpaperManager.FLAG_LOCK - : WallpaperManager.FLAG_SYSTEM); + GradientColors colors = mColorExtractor.getNeutralColors(); updateColors(colors, false /* animate */); } } @@ -1630,10 +1622,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, * @param animate Interpolates gradient if true, just sets otherwise. */ private void updateColors(GradientColors colors, boolean animate) { - if (!(mBackgroundDrawable instanceof GradientDrawable)) { + if (!(mBackgroundDrawable instanceof ScrimDrawable)) { return; } - ((GradientDrawable) mBackgroundDrawable).setColors(colors, animate); + ((ScrimDrawable) mBackgroundDrawable).setColor(colors.getMainColor(), animate); View decorView = getWindow().getDecorView(); if (colors.supportsDarkText()) { decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR | diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 4cf58b736eb9..4065d5b5dd8d 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -19,9 +19,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import android.app.Dialog; import android.app.KeyguardManager; -import android.app.WallpaperManager; import android.content.Context; -import android.graphics.Point; import android.view.View; import android.view.ViewGroup; import android.view.Window; @@ -31,7 +29,7 @@ import android.widget.TextView; import com.android.internal.R; import com.android.internal.colorextraction.ColorExtractor.GradientColors; -import com.android.internal.colorextraction.drawable.GradientDrawable; +import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.SysUiServiceProvider; @@ -87,7 +85,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks @Override public void showShutdownUi(boolean isReboot, String reason) { - GradientDrawable background = new GradientDrawable(mContext); + ScrimDrawable background = new ScrimDrawable(); background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255)); Dialog d = new Dialog(mContext, @@ -129,12 +127,8 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks message.setTextColor(color); if (isReboot) message.setText(R.string.reboot_to_reset_message); - Point displaySize = new Point(); - mContext.getDisplay().getRealSize(displaySize); - GradientColors colors = Dependency.get(SysuiColorExtractor.class).getColors( - onKeyguard ? WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM); - background.setColors(colors, false); - background.setScreenSize(displaySize.x, displaySize.y); + GradientColors colors = Dependency.get(SysuiColorExtractor.class).getNeutralColors(); + background.setColor(colors.getMainColor(), false); d.show(); } diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java index d1939d0fddb0..477e7d7ebf72 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java @@ -85,9 +85,7 @@ class ImageProcessHelper { Bitmap bitmap = bitmaps[0]; if (bitmap != null) { int[] histogram = processHistogram(bitmap); - Float val = computePercentile85(bitmap, histogram); - bitmaps[0] = null; - return val; + return computePercentile85(bitmap, histogram); } Log.e(TAG, "Per85ComputeTask: Can't get bitmap"); return DEFAULT_PER85; diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index 10f727bc7189..e92aa519b9b7 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -22,15 +22,19 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioAttributes; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.os.UserHandle; +import android.provider.Settings; +import android.provider.Settings.Global; import android.provider.Settings.Secure; import android.text.Annotation; import android.text.Layout; @@ -547,9 +551,15 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { updateNotification(); } - private void showStartSaverConfirmation(boolean confirmOnly) { + private void showStartSaverConfirmation(Bundle extras) { if (mSaverConfirmation != null) return; final SystemUIDialog d = new SystemUIDialog(mContext); + final boolean confirmOnly = extras.getBoolean(BatterySaverUtils.EXTRA_CONFIRM_TEXT_ONLY); + final int batterySaverTriggerMode = + extras.getInt(BatterySaverUtils.EXTRA_POWER_SAVE_MODE_TRIGGER, + PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE); + final int batterySaverTriggerLevel = + extras.getInt(BatterySaverUtils.EXTRA_POWER_SAVE_MODE_TRIGGER_LEVEL, 0); d.setMessage(getBatterySaverDescription()); // Sad hack for http://b/78261259 and http://b/78298335. Otherwise "Battery" may be split @@ -563,14 +573,25 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { if (confirmOnly) { d.setTitle(R.string.battery_saver_confirmation_title_generic); d.setPositiveButton(com.android.internal.R.string.confirm_battery_saver, - (dialog, which) -> Secure.putInt( - mContext.getContentResolver(), - Secure.LOW_POWER_WARNING_ACKNOWLEDGED, - 1)); + (dialog, which) -> { + final ContentResolver resolver = mContext.getContentResolver(); + Secure.putInt( + resolver, + Secure.LOW_POWER_WARNING_ACKNOWLEDGED, + 1); + Settings.Global.putInt( + resolver, + Global.AUTOMATIC_POWER_SAVE_MODE, + batterySaverTriggerMode); + Settings.Global.putInt( + resolver, + Global.LOW_POWER_MODE_TRIGGER_LEVEL, + batterySaverTriggerLevel); + }); } else { d.setTitle(R.string.battery_saver_confirmation_title); d.setPositiveButton(R.string.battery_saver_confirmation_ok, - (dialog, which) -> setSaverMode(true, false)); + (dialog, which) -> setSaverMode(true, false)); d.setNegativeButton(android.R.string.cancel, null); } d.setShowForAllUsers(true); @@ -731,7 +752,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { dismissLowBatteryNotification(); } else if (action.equals(ACTION_SHOW_START_SAVER_CONFIRMATION)) { dismissLowBatteryNotification(); - showStartSaverConfirmation(intent.getBooleanExtra(EXTRA_CONFIRM_ONLY, false)); + showStartSaverConfirmation(intent.getExtras()); } else if (action.equals(ACTION_DISMISSED_WARNING)) { dismissLowBatteryWarning(); } else if (ACTION_CLICKED_TEMP_WARNING.equals(action)) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java index d40973bfdad7..a732a253f5a3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java @@ -40,7 +40,6 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.ImageView.ScaleType; import android.widget.Switch; import com.android.settingslib.Utils; @@ -63,6 +62,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { private boolean mTileState; private boolean mCollapsedView; private boolean mClicked; + private boolean mShowRippleEffect = true; private final ImageView mBg; private final int mColorActive; @@ -209,6 +209,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { mCircleColor = circleColor; } + mShowRippleEffect = state.showRippleEffect; setClickable(state.state != Tile.STATE_UNAVAILABLE); setLongClickable(state.handlesLongClick); mIcon.setIcon(state, allowAnimations); @@ -254,7 +255,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { @Override public void setClickable(boolean clickable) { super.setClickable(clickable); - setBackground(clickable ? mRipple : null); + setBackground(clickable && mShowRippleEffect ? mRipple : null); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index c664a2090c04..d62f10d312d7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles; import android.content.Intent; +import android.provider.Settings.Secure; import android.service.quicksettings.Tile; import android.widget.Switch; @@ -23,6 +24,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.SecureSetting; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.BatteryController; @@ -32,6 +34,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements BatteryController.BatteryStateChangeCallback { private final BatteryController mBatteryController; + private final SecureSetting mSetting; private int mLevel; private boolean mPowerSave; @@ -45,6 +48,12 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements super(host); mBatteryController = batteryController; mBatteryController.observe(getLifecycle(), this); + mSetting = new SecureSetting(mContext, mHandler, Secure.LOW_POWER_WARNING_ACKNOWLEDGED) { + @Override + protected void handleValueChanged(int value, boolean observedChange) { + handleRefreshState(null); + } + }; } @Override @@ -53,12 +62,19 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements } @Override + protected void handleDestroy() { + super.handleDestroy(); + mSetting.setListening(false); + } + + @Override public int getMetricsCategory() { return MetricsEvent.QS_BATTERY_TILE; } @Override public void handleSetListening(boolean listening) { + mSetting.setListening(listening); } @Override @@ -88,6 +104,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements state.contentDescription = state.label; state.value = mPowerSave; state.expandedAccessibilityClassName = Switch.class.getName(); + state.showRippleEffect = mSetting.getValue() == 0; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 4d6693ff6613..00aef9aed85d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -28,6 +28,9 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INP import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import android.annotation.FloatRange; import android.content.BroadcastReceiver; @@ -52,6 +55,7 @@ import android.view.InputMonitor; import android.view.MotionEvent; import com.android.internal.policy.ScreenDecorationsUtils; +import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.Prefs; import com.android.systemui.SysUiServiceProvider; @@ -60,7 +64,10 @@ import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; import com.android.systemui.stackdivider.Divider; +import com.android.systemui.statusbar.NavigationBarController; +import com.android.systemui.statusbar.phone.NavigationBarFragment; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.CallbackController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -107,6 +114,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private IOverviewProxy mOverviewProxy; private int mConnectionBackoffAttempts; private @InteractionType int mInteractionFlags; + private @SystemUiStateFlags int mSysUiStateFlags; private boolean mBound; private boolean mIsEnabled; private int mCurrentBoundedUserId = -1; @@ -368,6 +376,9 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } dispatchNavButtonBounds(); + // Update the systemui state flags + updateSystemUiStateFlags(); + notifyConnectionChanged(); } @@ -394,19 +405,29 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private final DeviceProvisionedListener mDeviceProvisionedCallback = new DeviceProvisionedListener() { - @Override - public void onUserSetupChanged() { - if (mDeviceProvisionedController.isCurrentUserSetup()) { - internalConnectToCurrentUser(); - } - } - @Override - public void onUserSwitched() { - mConnectionBackoffAttempts = 0; + @Override + public void onDeviceProvisionedChanged() { + /* + on initialize, keep track of the previous gestural state (nothing is enabled by default) + restore to a non gestural state if device is not provisioned + once the device is provisioned, restore to the original state + */ + } + + @Override + public void onUserSetupChanged() { + if (mDeviceProvisionedController.isCurrentUserSetup()) { internalConnectToCurrentUser(); } - }; + } + + @Override + public void onUserSwitched() { + mConnectionBackoffAttempts = 0; + internalConnectToCurrentUser(); + } + }; // This is the death handler for the binder from the launcher service private final IBinder.DeathRecipient mOverviewServiceDeathRcpt @@ -455,6 +476,45 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + public void setSystemUiStateFlag(int flag, boolean enabled) { + int newState = mSysUiStateFlags; + if (enabled) { + newState |= flag; + } else { + newState &= ~flag; + } + if (mSysUiStateFlags != newState) { + mSysUiStateFlags = newState; + notifySystemUiStateFlags(mSysUiStateFlags); + } + } + + private void updateSystemUiStateFlags() { + final NavigationBarController navBar = Dependency.get(NavigationBarController.class); + final NavigationBarFragment navBarFragment = navBar.getDefaultNavigationBarFragment(); + final StatusBar statusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class); + final boolean panelExpanded = statusBar != null && statusBar.getPanel() != null + && statusBar.getPanel().isFullyExpanded(); + mSysUiStateFlags = 0; + mSysUiStateFlags |= ActivityManagerWrapper.getInstance().isScreenPinningActive() + ? SYSUI_STATE_SCREEN_PINNING : 0; + mSysUiStateFlags |= (navBarFragment == null || !navBarFragment.isNavBarWindowVisible()) + ? SYSUI_STATE_NAV_BAR_HIDDEN : 0; + mSysUiStateFlags |= panelExpanded + ? SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED : 0; + notifySystemUiStateFlags(mSysUiStateFlags); + } + + private void notifySystemUiStateFlags(int flags) { + try { + if (mOverviewProxy != null) { + mOverviewProxy.onSystemUiStateChanged(flags); + } + } catch (RemoteException e) { + Log.e(TAG_OPS, "Failed to notify sysui state change", e); + } + } + /** * Sets the navbar region which can receive touch inputs */ @@ -631,7 +691,9 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis public void notifyAssistantVisibilityChanged(float visibility) { try { - mOverviewProxy.onAssistantVisibilityChanged(visibility); + if (mOverviewProxy != null) { + mOverviewProxy.onAssistantVisibilityChanged(visibility); + } } catch (RemoteException e) { Log.e(TAG_OPS, "Failed to call onAssistantVisibilityChanged()", e); } @@ -657,6 +719,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis pw.print(" quickStepIntentResolved="); pw.println(isEnabled()); pw.print(" navBarMode="); pw.println(QuickStepContract.getCurrentInteractionMode(mContext)); + pw.print(" mSysUiStateFlags="); pw.println(mSysUiStateFlags); } public interface OverviewProxyListener { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java index 34f3c606be62..33a2acfe9521 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java @@ -64,6 +64,7 @@ import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.TaskStackChangeListener; import java.io.PrintWriter; @@ -339,7 +340,7 @@ public class RecentsOnboarding { } public void onConnectedToLauncher() { - if (!ONBOARDING_ENABLED) { + if (!ONBOARDING_ENABLED || QuickStepContract.isGesturalMode(mContext)) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index f796793d908c..07391ed5867f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -16,6 +16,7 @@ package com.android.systemui.recents; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; @@ -44,6 +45,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -59,6 +61,7 @@ public class ScreenPinningRequest implements View.OnClickListener { private final AccessibilityManager mAccessibilityService; private final WindowManager mWindowManager; + private final OverviewProxyService mOverviewProxyService; private RequestWindowView mRequestWindow; @@ -71,6 +74,7 @@ public class ScreenPinningRequest implements View.OnClickListener { mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + mOverviewProxyService = Dependency.get(OverviewProxyService.class); } public void clearPrompt() { @@ -125,6 +129,7 @@ public class ScreenPinningRequest implements View.OnClickListener { if (v.getId() == R.id.screen_pinning_ok_button || mRequestWindow == v) { try { ActivityTaskManager.getService().startSystemLockTaskMode(taskId); + mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING, true); } catch (RemoteException e) {} } clearPrompt(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index a87e50c50f51..3441591e03ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -83,7 +83,7 @@ public class KeyguardIndicationController implements StateListener { private final int mSlowThreshold; private final int mFastThreshold; - private LockIcon mLockIcon; + private final LockIcon mLockIcon; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private String mRestingIndication; @@ -539,7 +539,6 @@ public class KeyguardIndicationController implements StateListener { protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback { public static final int HIDE_DELAY_MS = 5000; - private int mLastSuccessiveErrorMessage = -1; @Override public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) { @@ -577,20 +576,14 @@ public class KeyguardIndicationController implements StateListener { if (!updateMonitor.isUnlockingWithBiometricAllowed()) { return; } + animatePadlockError(); if (mStatusBarKeyguardViewManager.isBouncerShowing()) { mStatusBarKeyguardViewManager.showBouncerMessage(helpString, mInitialTextColorState); } else if (updateMonitor.isScreenOn()) { - mLockIcon.setTransientBiometricsError(true); showTransientIndication(helpString); hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT); - mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG); - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG), - TRANSIENT_BIOMETRIC_ERROR_TIMEOUT); } - // Help messages indicate that there was actually a try since the last error, so those - // are not two successive error messages anymore. - mLastSuccessiveErrorMessage = -1; } @Override @@ -600,15 +593,9 @@ public class KeyguardIndicationController implements StateListener { if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) { return; } + animatePadlockError(); if (mStatusBarKeyguardViewManager.isBouncerShowing()) { - // When swiping up right after receiving a biometric error, the bouncer calls - // authenticate leading to the same message being shown again on the bouncer. - // We want to avoid this, as it may confuse the user when the message is too - // generic. - if (mLastSuccessiveErrorMessage != msgId) { - mStatusBarKeyguardViewManager.showBouncerMessage(errString, - mInitialTextColorState); - } + mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState); } else if (updateMonitor.isScreenOn()) { showTransientIndication(errString); // We want to keep this message around in case the screen was off @@ -616,7 +603,13 @@ public class KeyguardIndicationController implements StateListener { } else { mMessageToShowOnScreenOn = errString; } - mLastSuccessiveErrorMessage = msgId; + } + + private void animatePadlockError() { + mLockIcon.setTransientBiometricsError(true); + mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG), + TRANSIENT_BIOMETRIC_ERROR_TIMEOUT); } private boolean shouldSuppressBiometricError(int msgId, @@ -670,21 +663,19 @@ public class KeyguardIndicationController implements StateListener { @Override public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { super.onBiometricAuthenticated(userId, biometricSourceType); - mLastSuccessiveErrorMessage = -1; mHandler.sendEmptyMessage(MSG_HIDE_TRANSIENT); } @Override - public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { - super.onBiometricAuthFailed(biometricSourceType); - mLastSuccessiveErrorMessage = -1; - } - - @Override public void onUserUnlocked() { if (mVisible) { updateIndication(false); } } + + @Override + public void onKeyguardBouncerChanged(boolean bouncer) { + mLockIcon.setBouncerVisible(bouncer); + } }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index 2bb6e3e32dc7..c833ded6a85f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -67,7 +67,10 @@ public class NavigationBarController implements Callbacks { mContext = context; mHandler = handler; mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); - getComponent(mContext, CommandQueue.class).addCallback(this); + CommandQueue commandQueue = getComponent(mContext, CommandQueue.class); + if (commandQueue != null) { + commandQueue.addCallback(this); + } } @Override @@ -206,4 +209,9 @@ public class NavigationBarController implements Callbacks { NavigationBarFragment navBar = mNavigationBars.get(DEFAULT_DISPLAY); return (navBar == null) ? null : (NavigationBarView) navBar.getView(); } + + /** @return {@link NavigationBarFragment} on the default display. */ + public NavigationBarFragment getDefaultNavigationBarFragment() { + return mNavigationBars.get(DEFAULT_DISPLAY); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java index cf6e64cef365..04f1c3248a6f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java @@ -16,61 +16,33 @@ package com.android.systemui.statusbar; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; import android.annotation.NonNull; import android.content.Context; -import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.util.Log; -import android.view.Display; import android.view.View; -import android.view.WindowManager; import androidx.core.graphics.ColorUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; -import com.android.internal.colorextraction.drawable.GradientDrawable; -import com.android.settingslib.Utils; -import com.android.systemui.Dependency; -import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.internal.colorextraction.drawable.ScrimDrawable; /** * A view which can draw a scrim */ -public class ScrimView extends View implements ConfigurationController.ConfigurationListener { - private static final String TAG = "ScrimView"; +public class ScrimView extends View { private final ColorExtractor.GradientColors mColors; - private int mDensity; private float mViewAlpha = 1.0f; - private ValueAnimator mAlphaAnimator; private Drawable mDrawable; private PorterDuffColorFilter mColorFilter; private int mTintColor; - private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener = animation -> { - if (mDrawable == null) { - Log.w(TAG, "Trying to animate null drawable"); - return; - } - mDrawable.setAlpha((int) (255 * (float) animation.getAnimatedValue())); - }; - private AnimatorListenerAdapter mClearAnimatorListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mAlphaAnimator = null; - } - }; private Runnable mChangeRunnable; - private int mCornerRadius; public ScrimView(Context context) { this(context, null); @@ -87,47 +59,10 @@ public class ScrimView extends View implements ConfigurationController.Configura public ScrimView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mDrawable = new GradientDrawable(context); + mDrawable = new ScrimDrawable(); mDrawable.setCallback(this); mColors = new ColorExtractor.GradientColors(); - updateScreenSize(); updateColorWithTint(false); - initView(); - final Configuration currentConfig = mContext.getResources().getConfiguration(); - mDensity = currentConfig.densityDpi; - } - - private void initView() { - mCornerRadius = getResources().getDimensionPixelSize( - Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius)); - } - - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - int densityDpi = newConfig.densityDpi; - if (mDensity != densityDpi) { - mDensity = densityDpi; - initView(); - } - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - // We need to know about configuration changes to update the gradient size - // since it's independent from view bounds. - ConfigurationController config = Dependency.get(ConfigurationController.class); - config.addCallback(this); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - ConfigurationController config = Dependency.get(ConfigurationController.class); - config.removeCallback(this); } @Override @@ -142,7 +77,6 @@ public class ScrimView extends View implements ConfigurationController.Configura mDrawable.setCallback(this); mDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom()); mDrawable.setAlpha((int) (255 * mViewAlpha)); - updateScreenSize(); invalidate(); } @@ -200,15 +134,13 @@ public class ScrimView extends View implements ConfigurationController.Configura } private void updateColorWithTint(boolean animated) { - if (mDrawable instanceof GradientDrawable) { + if (mDrawable instanceof ScrimDrawable) { // Optimization to blend colors and avoid a color filter - GradientDrawable drawable = (GradientDrawable) mDrawable; + ScrimDrawable drawable = (ScrimDrawable) mDrawable; float tintAmount = Color.alpha(mTintColor) / 255f; int mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor, tintAmount); - int secondaryTinted = ColorUtils.blendARGB(mColors.getSecondaryColor(), mTintColor, - tintAmount); - drawable.setColors(mainTinted, secondaryTinted, animated); + drawable.setColor(mainTinted, animated); } else { boolean hasAlpha = Color.alpha(mTintColor) != 0; if (hasAlpha) { @@ -250,10 +182,6 @@ public class ScrimView extends View implements ConfigurationController.Configura if (alpha != mViewAlpha) { mViewAlpha = alpha; - if (mAlphaAnimator != null) { - mAlphaAnimator.cancel(); - } - mDrawable.setAlpha((int) (255 * alpha)); if (mChangeRunnable != null) { mChangeRunnable.run(); @@ -270,27 +198,6 @@ public class ScrimView extends View implements ConfigurationController.Configura } @Override - public void onConfigChanged(Configuration newConfig) { - updateScreenSize(); - } - - private void updateScreenSize() { - if (mDrawable instanceof GradientDrawable) { - WindowManager wm = mContext.getSystemService(WindowManager.class); - if (wm == null) { - Log.w(TAG, "Can't resize gradient drawable to fit the screen"); - return; - } - Display display = wm.getDefaultDisplay(); - if (display != null) { - Point size = new Point(); - display.getRealSize(size); - ((GradientDrawable) mDrawable).setScreenSize(size.x, size.y); - } - } - } - - @Override protected boolean canReceivePointerEvents() { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 2e85fea4615f..ce8463e70099 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -367,16 +367,11 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { @Override public void onFinishedGoingToSleep(int why) { Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep"); - if (mPendingAuthenticatedUserId != -1) { - + BiometricSourceType pendingType = mPendingAuthenticatedBioSourceType; + int pendingUserId = mPendingAuthenticatedUserId; + if (pendingUserId != -1 && pendingType != null) { // Post this to make sure it's executed after the device is fully locked. - mHandler.post(new Runnable() { - @Override - public void run() { - onBiometricAuthenticated(mPendingAuthenticatedUserId, - mPendingAuthenticatedBioSourceType); - } - }); + mHandler.post(() -> onBiometricAuthenticated(pendingUserId, pendingType)); } mPendingAuthenticatedUserId = -1; mPendingAuthenticatedBioSourceType = null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index 3a6756bb677f..79bf6b3e49f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import android.content.Context; import android.content.pm.ParceledListSlice; +import android.content.res.Resources; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PointF; @@ -140,6 +141,7 @@ public class EdgeBackGestureHandler implements DisplayListener { private WindowManager.LayoutParams mEdgePanelLp; public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService) { + final Resources res = context.getResources(); mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); @@ -148,10 +150,9 @@ public class EdgeBackGestureHandler implements DisplayListener { mEdgeWidth = QuickStepContract.getEdgeSensitivityWidth(context); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); - mSwipeThreshold = context.getResources() - .getDimension(R.dimen.navigation_edge_action_drag_threshold); + mSwipeThreshold = res.getDimension(R.dimen.navigation_edge_action_drag_threshold); - mNavBarHeight = context.getResources().getDimensionPixelSize(R.dimen.navigation_bar_height); + mNavBarHeight = res.getDimensionPixelSize(R.dimen.navigation_bar_frame_height); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 6ebd6b3f04cd..3cc4a7bfa1eb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -57,8 +57,10 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private int mDensity; private boolean mPulsing; private boolean mDozing; + private boolean mBouncerVisible; private boolean mLastDozing; private boolean mLastPulsing; + private boolean mLastBouncerVisible; private final Runnable mDrawOffTimeout = () -> update(true /* forceUpdate */); private float mDarkAmount; @@ -109,9 +111,9 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange int state = getState(); mIsFaceUnlockState = state == STATE_SCANNING_FACE; if (state != mLastState || mLastDozing != mDozing || mLastPulsing != mPulsing - || mLastScreenOn != mScreenOn || force) { + || mLastScreenOn != mScreenOn || mLastBouncerVisible != mBouncerVisible || force) { int iconAnimRes = getAnimationResForTransition(mLastState, state, mLastPulsing, - mPulsing, mLastDozing, mDozing); + mPulsing, mLastDozing, mDozing, mBouncerVisible); boolean isAnim = iconAnimRes != -1; Drawable icon; @@ -159,6 +161,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mLastScreenOn = mScreenOn; mLastDozing = mDozing; mLastPulsing = mPulsing; + mLastBouncerVisible = mBouncerVisible; } setVisibility(mDozing && !mPulsing ? GONE : VISIBLE); @@ -231,8 +234,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange } private static int getAnimationResForTransition(int oldState, int newState, - boolean wasPulsing, boolean pulsing, - boolean wasDozing, boolean dozing) { + boolean wasPulsing, boolean pulsing, boolean wasDozing, boolean dozing, + boolean bouncerVisible) { // Never animate when screen is off if (dozing && !pulsing) { @@ -249,7 +252,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange return com.android.internal.R.anim.lock_unlock; } else if (justLocked) { return com.android.internal.R.anim.lock_lock; - } else if (newState == STATE_SCANNING_FACE) { + } else if (newState == STATE_SCANNING_FACE && bouncerVisible) { return com.android.internal.R.anim.lock_scanning; } else if (!wasPulsing && pulsing && newState != STATE_LOCK_OPEN) { return com.android.internal.R.anim.lock_in; @@ -298,4 +301,15 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange int color = ColorUtils.blendARGB(Color.TRANSPARENT, Color.WHITE, mDarkAmount); drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); } + + /** + * If bouncer is visible or not. + */ + public void setBouncerVisible(boolean bouncerVisible) { + if (mBouncerVisible == bouncerVisible) { + return; + } + mBouncerVisible = bouncerVisible; + update(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java index 1d87a8b439e8..443cc4397bd3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java @@ -98,8 +98,10 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener // Short-circuiting from UserManager. Needs to be extracted because of SystemUI boolean flag // qs_show_user_switcher_for_single_user + // The default in UserManager is to show the switcher. We want to not show it unless the + // user explicitly requests it in Settings final boolean userSwitcherEnabled = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.USER_SWITCHER_ENABLED, 1) != 0; + Settings.Global.USER_SWITCHER_ENABLED, 0) != 0; if (!UserManager.supportsMultipleUsers() || mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 6729f9fa5787..591b1b48ddc2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -24,6 +24,8 @@ import static android.app.StatusBarManager.windowStateToString; import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; @@ -457,8 +459,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback mNavigationBarWindowState = state; if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); + mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NAV_BAR_HIDDEN, + !isNavBarWindowVisible()); mNavigationBarView.getRotateSuggestionButton() - .onNavigationBarWindowVisibilityChange(state == WINDOW_STATE_SHOWING); + .onNavigationBarWindowVisibilityChange(isNavBarWindowVisible()); } } @@ -776,44 +780,52 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback IActivityTaskManager activityManager = ActivityTaskManager.getService(); boolean touchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled(); boolean inLockTaskMode = activityManager.isInLockTaskMode(); - if (inLockTaskMode && !touchExplorationEnabled) { - long time = System.currentTimeMillis(); - - // If we recently long-pressed the other button then they were - // long-pressed 'together' - if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) { - activityManager.stopSystemLockTaskMode(); - // When exiting refresh disabled flags. - mNavigationBarView.updateNavButtonIcons(); - return true; - } else if (v.getId() == btnId1) { - ButtonDispatcher button = btnId2 == R.id.recent_apps - ? mNavigationBarView.getRecentsButton() - : mNavigationBarView.getHomeButton(); - if (!button.getCurrentView().isPressed()) { - // If we aren't pressing recents/home right now then they presses - // won't be together, so send the standard long-press action. + boolean stopLockTaskMode = false; + try { + if (inLockTaskMode && !touchExplorationEnabled) { + long time = System.currentTimeMillis(); + + // If we recently long-pressed the other button then they were + // long-pressed 'together' + if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) { + stopLockTaskMode = true; + return true; + } else if (v.getId() == btnId1) { + ButtonDispatcher button = btnId2 == R.id.recent_apps + ? mNavigationBarView.getRecentsButton() + : mNavigationBarView.getHomeButton(); + if (!button.getCurrentView().isPressed()) { + // If we aren't pressing recents/home right now then they presses + // won't be together, so send the standard long-press action. + sendBackLongPress = true; + } + } + mLastLockToAppLongPress = time; + } else { + // If this is back still need to handle sending the long-press event. + if (v.getId() == btnId1) { sendBackLongPress = true; + } else if (touchExplorationEnabled && inLockTaskMode) { + // When in accessibility mode a long press that is recents/home (not back) + // should stop lock task. + stopLockTaskMode = true; + return true; + } else if (v.getId() == btnId2) { + return btnId2 == R.id.recent_apps + ? onLongPressRecents() + : onHomeLongClick( + mNavigationBarView.getHomeButton().getCurrentView()); } } - mLastLockToAppLongPress = time; - } else { - // If this is back still need to handle sending the long-press event. - if (v.getId() == btnId1) { - sendBackLongPress = true; - } else if (touchExplorationEnabled && inLockTaskMode) { - // When in accessibility mode a long press that is recents/home (not back) - // should stop lock task. + } finally { + if (stopLockTaskMode) { activityManager.stopSystemLockTaskMode(); // When exiting refresh disabled flags. mNavigationBarView.updateNavButtonIcons(); - return true; - } else if (v.getId() == btnId2) { - return btnId2 == R.id.recent_apps - ? onLongPressRecents() - : onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView()); + mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SCREEN_PINNING, false); } } + if (sendBackLongPress) { KeyButtonView keyButtonView = (KeyButtonView) v; keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); @@ -925,6 +937,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } } + public boolean isNavBarWindowVisible() { + return mNavigationBarWindowState == WINDOW_STATE_SHOWING; + } + /** * Checks current navigation bar mode and make transitions. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index e2a63cc00d9e..a45d86ef0dfe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -20,6 +20,7 @@ import static android.content.Intent.ACTION_OVERLAY_CHANGED; import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; import android.animation.LayoutTransition; @@ -34,7 +35,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ParceledListSlice; import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Point; @@ -48,8 +48,6 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.view.Display; -import android.view.IPinnedStackController; -import android.view.IPinnedStackListener; import android.view.MotionEvent; import android.view.Surface; import android.view.View; @@ -237,45 +235,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } }; - private final IPinnedStackListener.Stub mImeChangedListener = new IPinnedStackListener.Stub() { - @Override - public void onListenerRegistered(IPinnedStackController controller) { - } - - @Override - public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - post(() -> { - // TODO remove this and do below when mNavigationIconHints changes - if (imeVisible) { - getBackButton().setVisibility(VISIBLE); - reloadNavIcons(); - } else { - getImeSwitchButton().setVisibility(GONE); - } - mImeVisible = imeVisible; - updateWindowTouchable(); - }); - } - - @Override - public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) { - } - - @Override - public void onMinimizedStateChanged(boolean isMinimized) { - } - - @Override - public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, - Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment, - int displayRotation) { - } - - @Override - public void onActionsChanged(ParceledListSlice actions) { - } - }; - private BroadcastReceiver mOverlaysChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -507,9 +466,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav final boolean useAltBack = (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; final boolean isRtl = mConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; - float degrees = useAltBack - ? (isRtl ? 270 : -90) - : (isRtl ? 180 : 0); + float degrees = useAltBack ? (isRtl ? 90 : -90) : 0; if (drawable.getRotation() == degrees) { return; } @@ -559,10 +516,13 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav public void setNavigationIconHints(int hints) { if (hints == mNavigationIconHints) return; - final boolean backAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; - if ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0 && !backAlt) { - mTransitionListener.onBackAltCleared(); + final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; + final boolean oldBackAlt = + (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; + if (newBackAlt != oldBackAlt) { + onImeVisibilityChanged(newBackAlt); } + if (DEBUG) { android.widget.Toast.makeText(getContext(), "Navigation icon hints = " + hints, @@ -572,6 +532,14 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav updateNavButtonIcons(); } + private void onImeVisibilityChanged(boolean visible) { + if (!visible) { + mTransitionListener.onBackAltCleared(); + } + mImeVisible = visible; + updateWindowTouchable(); + } + public void setDisabledFlags(int disabledFlags) { if (mDisabledFlags == disabledFlags) return; @@ -665,10 +633,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav return getContext().getDisplay(); } - public boolean inScreenPinning() { - return ActivityManagerWrapper.getInstance().isScreenPinningActive(); - } - public void setLayoutTransitionsEnabled(boolean enabled) { mLayoutTransitionsEnabled = enabled; updateLayoutTransitionsEnabled(); @@ -724,6 +688,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav public void onPanelExpandedChange(boolean expanded) { updateSlippery(); + mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED, + expanded); } public void updateStates() { @@ -743,10 +709,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav showSwipeUpUI ? mQuickStepAccessibilityDelegate : null); } - public boolean isNotificationsFullyCollapsed() { - return mPanelView.isFullyCollapsed(); - } - /** * Updates the {@link WindowManager.LayoutParams.FLAG_SLIPPERY} state dependent on if swipe up * is enabled, or the notifications is fully opened without being in an animated state. If @@ -1114,14 +1076,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav filter.addDataScheme("package"); getContext().registerReceiver(mOverlaysChangedReceiver, filter); mEdgeBackGestureHandler.onNavBarAttached(); - - if (QuickStepContract.isGesturalMode(getContext())) { - try { - WindowManagerWrapper.getInstance().addPinnedStackListener(mImeChangedListener); - } catch (RemoteException e) { - Log.e(TAG, "Failed to register pinned stack listener", e); - } - } updateWindowTouchable(); } @@ -1139,8 +1093,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav getContext().unregisterReceiver(mOverlaysChangedReceiver); mEdgeBackGestureHandler.onNavBarDetached(); - - WindowManagerWrapper.getInstance().removePinnedStackListener(mImeChangedListener); } private void setUpSwipeUpOnboarding(boolean connectedToOverviewProxy) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 0d2fe13b9a09..ed7947690e50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -20,7 +20,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.app.AlarmManager; -import android.app.WallpaperManager; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.Drawable; @@ -121,8 +120,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private final Handler mHandler; private final SysuiColorExtractor mColorExtractor; - private GradientColors mLockColors; - private GradientColors mSystemColors; + private GradientColors mColors; private boolean mNeedsDrawableColorUpdate; protected float mScrimBehindAlpha; @@ -190,10 +188,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mColorExtractor = Dependency.get(SysuiColorExtractor.class); mColorExtractor.addOnColorsChangedListener(this); - mLockColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, - ColorExtractor.TYPE_DARK, true /* ignoreVisibility */); - mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM, - ColorExtractor.TYPE_DARK, true /* ignoreVisibility */); + mColors = mColorExtractor.getNeutralColors(); mNeedsDrawableColorUpdate = true; final ScrimState[] states = ScrimState.values(); @@ -201,7 +196,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo states[i].init(mScrimInFront, mScrimBehind, mDozeParameters); states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard); } - mState = ScrimState.UNINITIALIZED; mScrimBehind.setDefaultFocusHighlightEnabled(false); mScrimInFront.setDefaultFocusHighlightEnabled(false); @@ -488,17 +482,15 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo // Make sure we have the right gradients and their opacities will satisfy GAR. if (mNeedsDrawableColorUpdate) { mNeedsDrawableColorUpdate = false; - boolean isKeyguard = mKeyguardUpdateMonitor.isKeyguardVisible() && !mKeyguardOccluded; - GradientColors currentScrimColors = isKeyguard ? mLockColors : mSystemColors; // Only animate scrim color if the scrim view is actually visible boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0 && !mBlankScreen; boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen; - mScrimInFront.setColors(currentScrimColors, animateScrimInFront); - mScrimBehind.setColors(currentScrimColors, animateScrimBehind); + mScrimInFront.setColors(mColors, animateScrimInFront); + mScrimBehind.setColors(mColors, animateScrimBehind); // Calculate minimum scrim opacity for white or black text. - int textColor = currentScrimColors.supportsDarkText() ? Color.BLACK : Color.WHITE; - int mainColor = currentScrimColors.getMainColor(); + int textColor = mColors.supportsDarkText() ? Color.BLACK : Color.WHITE; + int mainColor = mColors.getMainColor(); float minOpacity = ColorUtils.calculateMinimumBackgroundAlpha(textColor, mainColor, 4.5f /* minimumContrast */) / 255f; mScrimBehindAlpha = Math.max(mScrimBehindAlphaResValue, minOpacity); @@ -815,7 +807,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } public int getBackgroundColor() { - int color = mLockColors.getMainColor(); + int color = mColors.getMainColor(); return Color.argb((int) (mScrimBehind.getViewAlpha() * Color.alpha(color)), Color.red(color), Color.green(color), Color.blue(color)); } @@ -830,18 +822,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo @Override public void onColorsChanged(ColorExtractor colorExtractor, int which) { - if ((which & WallpaperManager.FLAG_LOCK) != 0) { - mLockColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, - ColorExtractor.TYPE_DARK, true /* ignoreVisibility */); - mNeedsDrawableColorUpdate = true; - scheduleUpdate(); - } - if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { - mSystemColors = mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM, - ColorExtractor.TYPE_DARK, mState != ScrimState.UNLOCKED); - mNeedsDrawableColorUpdate = true; - scheduleUpdate(); - } + mColors = mColorExtractor.getNeutralColors(); + mNeedsDrawableColorUpdate = true; + scheduleUpdate(); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index b34e24eee7fe..aaaf3edfba68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1152,7 +1152,6 @@ public class StatusBar extends SystemUI implements DemoMode, if (mBrightnessMirrorController != null) { mBrightnessMirrorController.onDensityOrFontScaleChanged(); } - mStatusBarKeyguardViewManager.onDensityOrFontScaleChanged(); // TODO: Bring these out of StatusBar. ((UserInfoControllerImpl) Dependency.get(UserInfoController.class)) .onDensityOrFontScaleChanged(); @@ -3949,6 +3948,7 @@ public class StatusBar extends SystemUI implements DemoMode, } private void setPulsing(boolean pulsing) { + mStatusBarKeyguardViewManager.setPulsing(pulsing); mKeyguardViewMediator.setPulsing(pulsing); mNotificationPanel.setPulsing(pulsing); mVisualStabilityManager.setPulsing(pulsing); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 92cd28009386..e3cc3d46aa11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -40,14 +40,18 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.DejankUtils; import com.android.systemui.Dependency; +import com.android.systemui.SysUiServiceProvider; import com.android.systemui.SystemUIFactory; +import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitorImpl; @@ -61,7 +65,7 @@ import java.util.ArrayList; * {@link com.android.keyguard.KeyguardViewBase}. */ public class StatusBarKeyguardViewManager implements RemoteInputController.Callback, - StatusBarStateController.StateListener { + StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener { // When hiding the Keyguard with timing supplied from WindowManager, better be early than late. private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3; @@ -105,6 +109,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mNotificationPanelView.updateLockIcon(); } }; + private final DockManager.DockEventListener mDockEventListener = + new DockManager.DockEventListener() { + @Override + public void onEvent(int event) { + boolean isDocked = mDockManager.isDocked(); + if (isDocked == mIsDocked) { + return; + } + mIsDocked = isDocked; + updateStates(); + } + }; protected LockPatternUtils mLockPatternUtils; protected ViewMediatorCallback mViewMediatorCallback; @@ -119,6 +135,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb protected boolean mOccluded; protected boolean mRemoteInputActive; private boolean mDozing; + private boolean mPulsing; + private boolean mGesturalNav; + private boolean mIsDocked; protected boolean mFirstUpdate = true; protected boolean mLastShowing; @@ -127,6 +146,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean mLastBouncerDismissible; protected boolean mLastRemoteInputActive; private boolean mLastDozing; + private boolean mLastGesturalNav; + private boolean mLastIsDocked; + private boolean mLastPulsing; private int mLastBiometricMode; private boolean mGoingToSleepVisibleNotOccluded; @@ -139,6 +161,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class); private final NotificationMediaManager mMediaManager = Dependency.get(NotificationMediaManager.class); + private final DockManager mDockManager; private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @@ -159,8 +182,15 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mViewMediatorCallback = callback; mLockPatternUtils = lockPatternUtils; mStatusBarWindowController = Dependency.get(StatusBarWindowController.class); + mGesturalNav = QuickStepContract.isGesturalMode(context); KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback); Dependency.get(StatusBarStateController.class).addCallback(this); + Dependency.get(ConfigurationController.class).addCallback(this); + mDockManager = SysUiServiceProvider.getComponent(context, DockManager.class); + if (mDockManager != null) { + mDockManager.addListener(mDockEventListener); + mIsDocked = mDockManager.isDocked(); + } } public void registerStatusBar(StatusBar statusBar, @@ -241,6 +271,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } private void hideBouncer(boolean destroyView) { + if (mBouncer == null) { + return; + } mBouncer.hide(destroyView); cancelPendingWakeupAction(); } @@ -354,6 +387,16 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } + /** + * If {@link StatusBar} is pulsing. + */ + public void setPulsing(boolean pulsing) { + if (mPulsing != pulsing) { + mPulsing = pulsing; + updateStates(); + } + } + public void setNeedsInput(boolean needsInput) { mStatusBarWindowController.setKeyguardNeedsInput(needsInput); } @@ -492,10 +535,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb StatsLog.KEYGUARD_STATE_CHANGED__STATE__HIDDEN); } + @Override public void onDensityOrFontScaleChanged() { hideBouncer(true /* destroyView */); } + @Override + public void onOverlayChanged() { + boolean gesturalNav = QuickStepContract.isGesturalMode(mContext); + if (gesturalNav != mGesturalNav) { + mGesturalNav = gesturalNav; + updateStates(); + } + } + public void onThemeChanged() { hideBouncer(true /* destroyView */); mBouncer.prepare(); @@ -643,7 +696,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mLastBouncerDismissible = bouncerDismissible; mLastRemoteInputActive = remoteInputActive; mLastDozing = mDozing; + mLastPulsing = mPulsing; mLastBiometricMode = mBiometricUnlockController.getMode(); + mLastGesturalNav = mGesturalNav; + mLastIsDocked = mIsDocked; mStatusBar.onKeyguardViewManagerStatesUpdated(); } @@ -671,8 +727,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb int biometricMode = mBiometricUnlockController.getMode(); boolean keyguardShowing = mShowing && !mOccluded; boolean hideWhileDozing = mDozing && biometricMode != MODE_WAKE_AND_UNLOCK_PULSING; + boolean keyguardWithGestureNav = (keyguardShowing && !mDozing || mPulsing && !mIsDocked) + && mGesturalNav; return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing() - || mRemoteInputActive); + || mRemoteInputActive || keyguardWithGestureNav); } /** @@ -681,8 +739,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb protected boolean getLastNavBarVisible() { boolean keyguardShowing = mLastShowing && !mLastOccluded; boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING; + boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing + || mLastPulsing && !mLastIsDocked) && mLastGesturalNav; return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing - || mLastRemoteInputActive); + || mLastRemoteInputActive || keyguardWithGestureNav); } public boolean shouldDismissOnMenuPressed() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java index 03c89c60360f..dd0c3443f2ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java @@ -37,6 +37,7 @@ import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.util.FloatProperty; import android.view.ContextThemeWrapper; +import android.view.View; import com.android.settingslib.Utils; import com.android.systemui.R; @@ -79,9 +80,10 @@ public class KeyButtonDrawable extends Drawable { private final ShadowDrawableState mState; private AnimatedVectorDrawable mAnimatedDrawable; - public KeyButtonDrawable(Drawable d, @ColorInt int lightColor, @ColorInt int darkColor) { + public KeyButtonDrawable(Drawable d, @ColorInt int lightColor, @ColorInt int darkColor, + boolean horizontalFlip) { this(d, new ShadowDrawableState(lightColor, darkColor, - d instanceof AnimatedVectorDrawable)); + d instanceof AnimatedVectorDrawable, horizontalFlip)); } private KeyButtonDrawable(Drawable d, ShadowDrawableState state) { @@ -282,7 +284,12 @@ public class KeyButtonDrawable extends Drawable { // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared. final Drawable d = mState.mChildState.newDrawable().mutate(); setDrawableBounds(d); + canvas.save(); + if (mState.mHorizontalFlip) { + canvas.scale(-1f, 1f, width * 0.5f, height * 0.5f); + } d.draw(canvas); + canvas.restore(); if (mState.mIsHardwareBitmap) { bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false); @@ -305,7 +312,12 @@ public class KeyButtonDrawable extends Drawable { // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared. final Drawable d = mState.mChildState.newDrawable().mutate(); setDrawableBounds(d); + canvas.save(); + if (mState.mHorizontalFlip) { + canvas.scale(-1f, 1f, width * 0.5f, height * 0.5f); + } d.draw(canvas); + canvas.restore(); // Draws the shadow from original drawable Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); @@ -357,6 +369,7 @@ public class KeyButtonDrawable extends Drawable { int mShadowColor; float mDarkIntensity; int mAlpha; + boolean mHorizontalFlip; boolean mIsHardwareBitmap; Bitmap mLastDrawnIcon; @@ -368,11 +381,12 @@ public class KeyButtonDrawable extends Drawable { final boolean mSupportsAnimation; public ShadowDrawableState(@ColorInt int lightColor, @ColorInt int darkColor, - boolean animated) { + boolean animated, boolean horizontalFlip) { mLightColor = lightColor; mDarkColor = darkColor; mSupportsAnimation = animated; mAlpha = 255; + mHorizontalFlip = horizontalFlip; } @Override @@ -400,7 +414,7 @@ public class KeyButtonDrawable extends Drawable { * @return KeyButtonDrawable */ public static KeyButtonDrawable create(@NonNull Context ctx, @DrawableRes int icon, - boolean hasShadow) { + boolean hasShadow) { final int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme); final int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme); Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme); @@ -409,7 +423,7 @@ public class KeyButtonDrawable extends Drawable { } public static KeyButtonDrawable create(Context lightContext, Context darkContext, - @DrawableRes int iconResId, boolean hasShadow) { + @DrawableRes int iconResId, boolean hasShadow) { return create(lightContext, Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor), Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor), @@ -418,10 +432,12 @@ public class KeyButtonDrawable extends Drawable { public static KeyButtonDrawable create(Context context, @ColorInt int lightColor, @ColorInt int darkColor, @DrawableRes int iconResId, boolean hasShadow) { - final KeyButtonDrawable drawable = new KeyButtonDrawable(context.getDrawable(iconResId), - lightColor, darkColor); + final Resources res = context.getResources(); + boolean isRtl = res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + Drawable d = context.getDrawable(iconResId); + final KeyButtonDrawable drawable = new KeyButtonDrawable(d, lightColor, darkColor, + isRtl && d.isAutoMirrored()); if (hasShadow) { - final Resources res = context.getResources(); int offsetX = res.getDimensionPixelSize(R.dimen.nav_key_button_shadow_offset_x); int offsetY = res.getDimensionPixelSize(R.dimen.nav_key_button_shadow_offset_y); int radius = res.getDimensionPixelSize(R.dimen.nav_key_button_shadow_radius); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index c5996a1e1b00..8f135c80a1d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -47,6 +47,8 @@ import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionD import java.io.PrintWriter; import java.util.BitSet; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class MobileSignalController extends SignalController< @@ -73,6 +75,9 @@ public class MobileSignalController extends SignalController< private SignalStrength mSignalStrength; private MobileIconGroup mDefaultIcons; private Config mConfig; + private boolean mInflateSignalStrengths = false; + // Some specific carriers have 5GE network which is special LTE CA network. + private static final int NETWORK_TYPE_LTE_CA_5GE = TelephonyManager.MAX_NETWORK_TYPE + 1; // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't // need listener lists anymore. @@ -114,6 +119,7 @@ public class MobileSignalController extends SignalController< public void setConfiguration(Config config) { mConfig = config; + updateInflateSignalStrength(); mapIconSets(); updateTelephony(); } @@ -236,11 +242,19 @@ public class MobileSignalController extends SignalController< TelephonyIcons.LTE_PLUS); } } + mNetworkToIconLookup.put(NETWORK_TYPE_LTE_CA_5GE, + TelephonyIcons.LTE_CA_5G_E); mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_IWLAN, TelephonyIcons.WFC); } + private void updateInflateSignalStrength() { + mInflateSignalStrengths = SubscriptionManager.getResourcesForSubId(mContext, + mSubscriptionInfo.getSubscriptionId()) + .getBoolean(R.bool.config_inflateSignalStrength); + } + private int getNumLevels() { - if (mConfig.inflateSignalStrengths) { + if (mInflateSignalStrengths) { return SignalStrength.NUM_SIGNAL_STRENGTH_BINS + 1; } return SignalStrength.NUM_SIGNAL_STRENGTH_BINS; @@ -252,7 +266,7 @@ public class MobileSignalController extends SignalController< return SignalDrawable.getCarrierChangeState(getNumLevels()); } else if (mCurrentState.connected) { int level = mCurrentState.level; - if (mConfig.inflateSignalStrengths) { + if (mInflateSignalStrengths) { level++; } boolean dataDisabled = mCurrentState.userSetup @@ -381,6 +395,26 @@ public class MobileSignalController extends SignalController< } } + private boolean isCarrierSpecificDataIcon() { + if (mConfig.patternOfCarrierSpecificDataIcon == null + || mConfig.patternOfCarrierSpecificDataIcon.length() == 0) { + return false; + } + + Pattern stringPattern = Pattern.compile(mConfig.patternOfCarrierSpecificDataIcon); + String[] operatorNames = new String[]{mServiceState.getOperatorAlphaLongRaw(), + mServiceState.getOperatorAlphaShortRaw()}; + for (String opName : operatorNames) { + if (!TextUtils.isEmpty(opName)) { + Matcher matcher = stringPattern.matcher(opName); + if (matcher.find()) { + return true; + } + } + } + return false; + } + /** * Updates the network's name based on incoming spn and plmn. */ @@ -535,6 +569,7 @@ public class MobileSignalController extends SignalController< pw.println(" mSignalStrength=" + mSignalStrength + ","); pw.println(" mDataState=" + mDataState + ","); pw.println(" mDataNetType=" + mDataNetType + ","); + pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ","); } class MobilePhoneStateListener extends PhoneStateListener { @@ -559,12 +594,8 @@ public class MobileSignalController extends SignalController< + " dataState=" + state.getDataRegState()); } mServiceState = state; - if (state != null) { - mDataNetType = state.getDataNetworkType(); - if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null && - mServiceState.isUsingCarrierAggregation()) { - mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA; - } + if (mServiceState != null) { + updateDataNetType(mServiceState.getDataNetworkType()); } updateTelephony(); } @@ -576,12 +607,19 @@ public class MobileSignalController extends SignalController< + " type=" + networkType); } mDataState = state; + updateDataNetType(networkType); + updateTelephony(); + } + + private void updateDataNetType(int networkType) { mDataNetType = networkType; - if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null && - mServiceState.isUsingCarrierAggregation()) { - mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA; + if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE) { + if (isCarrierSpecificDataIcon()) { + mDataNetType = NETWORK_TYPE_LTE_CA_5GE; + } else if (mServiceState != null && mServiceState.isUsingCarrierAggregation()) { + mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA; + } } - updateTelephony(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index d01430a97783..faf63c838259 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -55,6 +55,7 @@ import android.util.Log; import android.util.MathUtils; import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; @@ -108,16 +109,10 @@ public class NetworkControllerImpl extends BroadcastReceiver private final SubscriptionDefaults mSubDefaults; private final DataSaverController mDataSaverController; private final CurrentUserTracker mUserTracker; + private final Object mLock = new Object(); private Config mConfig; - private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { - @Override - public void onActiveDataSubscriptionIdChanged(int subId) { - mActiveMobileDataSubscription = subId; - doUpdateMobileControllers(); - } - }; - + private PhoneStateListener mPhoneStateListener; private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; // Subcontrollers. @@ -279,6 +274,14 @@ public class NetworkControllerImpl extends BroadcastReceiver // TODO: Move off of the deprecated CONNECTIVITY_ACTION broadcast and rely on callbacks // exclusively for status bar icons. mConnectivityManager.registerDefaultNetworkCallback(callback, mReceiverHandler); + // Register the listener on our bg looper + mPhoneStateListener = new PhoneStateListener(bgLooper) { + @Override + public void onActiveDataSubscriptionIdChanged(int subId) { + mActiveMobileDataSubscription = subId; + doUpdateMobileControllers(); + } + }; } public DataSaverController getDataSaverController() { @@ -600,7 +603,9 @@ public class NetworkControllerImpl extends BroadcastReceiver updateNoSims(); return; } - setCurrentSubscriptions(subscriptions); + synchronized (mLock) { + setCurrentSubscriptionsLocked(subscriptions); + } updateNoSims(); recalculateEmergency(); } @@ -628,8 +633,9 @@ public class NetworkControllerImpl extends BroadcastReceiver return false; } + @GuardedBy("mLock") @VisibleForTesting - void setCurrentSubscriptions(List<SubscriptionInfo> subscriptions) { + public void setCurrentSubscriptionsLocked(List<SubscriptionInfo> subscriptions) { Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() { @Override public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) { @@ -1102,6 +1108,7 @@ public class NetworkControllerImpl extends BroadcastReceiver boolean hspaDataDistinguishable; boolean inflateSignalStrengths = false; boolean alwaysShowDataRatIcon = false; + public String patternOfCarrierSpecificDataIcon = ""; /** * Mapping from NR 5G status string to an integer. The NR 5G status string should match @@ -1140,6 +1147,8 @@ public class NetworkControllerImpl extends BroadcastReceiver CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL); config.hideLtePlus = b.getBoolean( CarrierConfigManager.KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL); + config.patternOfCarrierSpecificDataIcon = b.getString( + CarrierConfigManager.KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING); String nr5GIconConfiguration = b.getString(CarrierConfigManager.KEY_5G_ICON_CONFIGURATION_STRING); if (!TextUtils.isEmpty(nr5GIconConfiguration)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java index e151ca3e23f3..c22ff8ba594b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java @@ -35,6 +35,7 @@ class TelephonyIcons { static final int ICON_3G = R.drawable.ic_3g_mobiledata; static final int ICON_4G = R.drawable.ic_4g_mobiledata; static final int ICON_4G_PLUS = R.drawable.ic_4g_plus_mobiledata; + static final int ICON_5G_E = R.drawable.ic_5g_e_mobiledata; static final int ICON_1X = R.drawable.ic_1x_mobiledata; static final int ICON_5G = R.drawable.ic_5g_mobiledata; static final int ICON_5G_PLUS = R.drawable.ic_5g_plus_mobiledata; @@ -204,6 +205,19 @@ class TelephonyIcons { TelephonyIcons.ICON_LTE_PLUS, true); + static final MobileIconGroup LTE_CA_5G_E = new MobileIconGroup( + "5Ge", + null, + null, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH, + 0, 0, + 0, + 0, + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], + R.string.data_connection_5ge, + TelephonyIcons.ICON_5G_E, + true); + static final MobileIconGroup NR_5G = new MobileIconGroup( "5G", null, @@ -276,6 +290,7 @@ class TelephonyIcons { ICON_NAME_TO_ICON.put("h+", H_PLUS); ICON_NAME_TO_ICON.put("4g", FOUR_G); ICON_NAME_TO_ICON.put("4g+", FOUR_G_PLUS); + ICON_NAME_TO_ICON.put("5ge", LTE_CA_5G_E); ICON_NAME_TO_ICON.put("lte", LTE); ICON_NAME_TO_ICON.put("lte+", LTE_PLUS); ICON_NAME_TO_ICON.put("5g", NR_5G); diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java index 2a84c5d4d44d..9bbfd224079c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/Events.java +++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java @@ -94,7 +94,6 @@ public class Events { public static final int DISMISS_STREAM_GONE = 7; public static final int DISMISS_REASON_OUTPUT_CHOOSER = 8; public static final int DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED = 9; - public static final int DISMISS_REASON_ODI_CAPTIONS_CLICKED = 10; public static final String[] DISMISS_REASONS = { "unknown", "touch_outside", @@ -105,8 +104,7 @@ public class Events { "done_clicked", "a11y_stream_changed", "output_chooser", - "usb_temperature_below_threshold", - "odi_captions_clicked" + "usb_temperature_below_threshold" }; public static final int SHOW_REASON_UNKNOWN = 0; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 2094b36d8294..509537089bf8 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -30,7 +30,6 @@ import static android.view.View.GONE; import static android.view.View.VISIBLE; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static com.android.systemui.volume.Events.DISMISS_REASON_ODI_CAPTIONS_CLICKED; import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED; import android.animation.ObjectAnimator; @@ -519,7 +518,6 @@ public class VolumeDialogImpl implements VolumeDialog, mODICaptionsIcon.setOnConfirmedTapListener(() -> { onCaptionIconClicked(); Events.writeEvent(mContext, Events.EVENT_ODI_CAPTIONS_CLICK); - dismissH(DISMISS_REASON_ODI_CAPTIONS_CLICKED); }, mHandler); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java index f2ad958c57ab..17fbe09e07b3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java @@ -18,12 +18,14 @@ package com.android.keyguard.clock; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.database.ContentObserver; +import android.net.Uri; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -52,6 +54,8 @@ public final class ClockManagerTest extends SysuiTestCase { private static final String BUBBLE_CLOCK = BubbleClockController.class.getName(); private static final Class<?> BUBBLE_CLOCK_CLASS = BubbleClockController.class; + private static final int USER_ID = 0; + private static final Uri SETTINGS_URI = null; private ClockManager mClockManager; private ContentObserver mContentObserver; @@ -106,10 +110,10 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void getCurrentClock_default() { // GIVEN that settings doesn't contain any values - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(null); - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(null); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(null); + when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(null); // WHEN settings change event is fired - mContentObserver.onChange(false); + mContentObserver.onChange(false, SETTINGS_URI, USER_ID); // THEN the result is null, indicated the default clock face should be used. assertThat(mClockManager.getCurrentClock()).isNull(); } @@ -117,9 +121,9 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void getCurrentClock_customClock() { // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false); + mContentObserver.onChange(false, SETTINGS_URI, USER_ID); // THEN the plugin is the bubble clock face. assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); } @@ -127,9 +131,9 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void onClockChanged_customClock() { // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false); + mContentObserver.onChange(false, SETTINGS_URI, USER_ID); // THEN the plugin is the bubble clock face. ArgumentCaptor<ClockPlugin> captor = ArgumentCaptor.forClass(ClockPlugin.class); verify(mMockListener1).onClockChanged(captor.capture()); @@ -139,9 +143,9 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void onClockChanged_uniqueInstances() { // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN settings change event is fired - mContentObserver.onChange(false); + mContentObserver.onChange(false, SETTINGS_URI, USER_ID); // THEN the listeners receive separate instances of the Bubble clock plugin. ArgumentCaptor<ClockPlugin> captor1 = ArgumentCaptor.forClass(ClockPlugin.class); ArgumentCaptor<ClockPlugin> captor2 = ArgumentCaptor.forClass(ClockPlugin.class); @@ -156,9 +160,9 @@ public final class ClockManagerTest extends SysuiTestCase { public void getCurrentClock_badSettingsValue() { // GIVEN that settings contains a value that doesn't correspond to a // custom clock face. - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn("bad value"); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn("bad value"); // WHEN settings change event is fired - mContentObserver.onChange(false); + mContentObserver.onChange(false, SETTINGS_URI, USER_ID); // THEN the result is null. assertThat(mClockManager.getCurrentClock()).isNull(); } @@ -174,7 +178,7 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void getCurrentClock_dockedCustomClock() { // GIVEN settings is set to the bubble clock face - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn(BUBBLE_CLOCK); + when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN dock event fires mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); // THEN the plugin is the bubble clock face. @@ -184,7 +188,7 @@ public final class ClockManagerTest extends SysuiTestCase { @Test public void getCurrentClock_badDockedSettingsValue() { // GIVEN settings contains a value that doesn't correspond to an available clock face. - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value"); + when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn("bad value"); // WHEN dock event fires mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); // THEN the result is null. @@ -195,8 +199,8 @@ public final class ClockManagerTest extends SysuiTestCase { public void getCurrentClock_badDockedSettingsFallback() { // GIVEN settings contains a value that doesn't correspond to an available clock face, but // locked screen settings is set to bubble clock. - when(mMockSettingsWrapper.getDockedClockFace()).thenReturn("bad value"); - when(mMockSettingsWrapper.getLockScreenCustomClockFace()).thenReturn(BUBBLE_CLOCK); + when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn("bad value"); + when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); // WHEN dock event is fired mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); // THEN the plugin is the bubble clock face. diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java index 1649f9845661..67df60a3dcfc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java +++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java @@ -18,6 +18,11 @@ package com.android.systemui.colorextraction; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; import android.app.WallpaperColors; import android.app.WallpaperManager; @@ -27,7 +32,9 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.colorextraction.ColorExtractor; +import com.android.internal.colorextraction.types.Tonal; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.ConfigurationController; import org.junit.Test; import org.junit.runner.RunWith; @@ -57,7 +64,7 @@ public class SysuiColorExtractorTests extends SysuiTestCase { simulateEvent(extractor); extractor.setWallpaperVisible(false); - ColorExtractor.GradientColors fallbackColors = extractor.getFallbackColors(); + ColorExtractor.GradientColors fallbackColors = extractor.getNeutralColors(); for (int type : sTypes) { assertEquals("Not using fallback!", @@ -96,7 +103,7 @@ public class SysuiColorExtractorTests extends SysuiTestCase { extractor.setWallpaperVisible(true); extractor.setHasBackdrop(true); - ColorExtractor.GradientColors fallbackColors = extractor.getFallbackColors(); + ColorExtractor.GradientColors fallbackColors = extractor.getNeutralColors(); for (int type : sTypes) { assertEquals("Not using fallback!", @@ -106,6 +113,19 @@ public class SysuiColorExtractorTests extends SysuiTestCase { } } + @Test + public void onUiModeChanged_reloadsColors() { + Tonal tonal = mock(Tonal.class); + ConfigurationController configurationController = mock(ConfigurationController.class); + SysuiColorExtractor sysuiColorExtractor = new SysuiColorExtractor(getContext(), + tonal, configurationController, false /* registerVisibility */); + verify(configurationController).addCallback(eq(sysuiColorExtractor)); + + reset(tonal); + sysuiColorExtractor.onUiModeChanged(); + verify(tonal).applyFallback(any(), any()); + } + private SysuiColorExtractor getTestableExtractor(ColorExtractor.GradientColors colors) { return new SysuiColorExtractor(getContext(), (inWallpaperColors, outGradientColorsNormal, outGradientColorsDark, @@ -113,7 +133,7 @@ public class SysuiColorExtractorTests extends SysuiTestCase { outGradientColorsNormal.set(colors); outGradientColorsDark.set(colors); outGradientColorsExtraDark.set(colors); - }, false); + }, mock(ConfigurationController.class), false); } private void simulateEvent(SysuiColorExtractor extractor) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java index 2020d4b9562f..87a77577841a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java @@ -30,7 +30,7 @@ import android.view.View; import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; -import com.android.internal.colorextraction.drawable.GradientDrawable; +import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.utils.leaks.LeakCheckedTest; @@ -70,12 +70,10 @@ public class ScrimViewTest extends LeakCheckedTest { @Test public void testCreation_initialColor() { - GradientDrawable drawable = (GradientDrawable) mView.getDrawable(); + ScrimDrawable drawable = (ScrimDrawable) mView.getDrawable(); ColorExtractor.GradientColors colors = mView.getColors(); assertEquals("Main color should be set upon creation", drawable.getMainColor(), colors.getMainColor()); - assertEquals("Secondary color should be set upon creation", - drawable.getSecondaryColor(), colors.getSecondaryColor()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 057f752b5ad1..d2d294bf8d37 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; @@ -71,6 +72,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { private UnlockMethodCache mUnlockMethodCache; @Mock private TunerService mTunerService; + @Mock + private Handler mHandler; private BiometricUnlockController mBiometricUnlockController; @Before @@ -172,12 +175,24 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat()); } + @Test + public void onFinishedGoingToSleep_authenticatesWhenPending() { + when(mUpdateMonitor.isGoingToSleep()).thenReturn(true); + mBiometricUnlockController.onFinishedGoingToSleep(-1); + verify(mHandler, never()).post(any()); + + mBiometricUnlockController.onBiometricAuthenticated(1 /* userId */, + BiometricSourceType.FACE); + mBiometricUnlockController.onFinishedGoingToSleep(-1); + verify(mHandler).post(any()); + } + private class TestableBiometricUnlockController extends BiometricUnlockController { TestableBiometricUnlockController(boolean faceDismissesKeyguard) { super(mContext, mDozeScrimController, mKeyguardViewMediator, mScrimController, mStatusBar, mUnlockMethodCache, - new Handler(), mUpdateMonitor, mTunerService, 0 /* wakeUpDelay */, + mHandler, mUpdateMonitor, mTunerService, 0 /* wakeUpDelay */, faceDismissesKeyguard); mFaceDismissesKeyguard = faceDismissesKeyguard; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java index e3c081ea2a67..c837c9ccea95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java @@ -179,8 +179,10 @@ public class NavigationBarContextTest extends SysuiTestCase { final int unusedColor = 0; final Drawable d = mock(Drawable.class); final ContextualButton button = spy(mBtn0); - final KeyButtonDrawable kbd1 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor)); - final KeyButtonDrawable kbd2 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor)); + final KeyButtonDrawable kbd1 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor, + false /* horizontalFlip */)); + final KeyButtonDrawable kbd2 = spy(new KeyButtonDrawable(d, unusedColor, unusedColor, + false /* horizontalFlip */)); kbd1.setDarkIntensity(TEST_DARK_INTENSITY); kbd2.setDarkIntensity(0f); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java index ac6544e129b4..0b53c486356f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java @@ -300,7 +300,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { // We can only test whether unregister gets called if it thinks its in a listening // state. mNetworkController.mListening = true; - mNetworkController.setCurrentSubscriptions(subscriptions); + mNetworkController.setCurrentSubscriptionsLocked(subscriptions); for (int i = 0; i < testSubscriptions.length; i++) { if (i == indexToSkipController) { diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index c57d4e9f1395..b9b3a615650f 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -18,6 +18,10 @@ include $(CLEAR_VARS) LOCAL_MODULE := frameworks-base-overlays LOCAL_REQUIRED_MODULES := \ AccentColorBlackOverlay \ + AccentColorCinnamonOverlay \ + AccentColorOceanOverlay \ + AccentColorOrchidOverlay \ + AccentColorSpaceOverlay \ AccentColorGreenOverlay \ AccentColorPurpleOverlay \ DisplayCutoutEmulationCornerOverlay \ diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml index 380ff346c6f2..4d844a1c4e0f 100644 --- a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml @@ -16,7 +16,7 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" - android:tint="@*android:color/accent_device_default" + android:tint="@*android:color/accent_device_default_light" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" > diff --git a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_settings_bluetooth.xml b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_settings_bluetooth.xml index 452a032c49e9..19731249bfc7 100644 --- a/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_settings_bluetooth.xml +++ b/packages/overlays/IconPackCircularAndroidOverlay/res/drawable/ic_settings_bluetooth.xml @@ -16,6 +16,7 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" > diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml index 8719f155f94a..df79827ce624 100644 --- a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml @@ -16,7 +16,7 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" - android:tint="@*android:color/accent_device_default" + android:tint="@*android:color/accent_device_default_light" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" > diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_settings_bluetooth.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_settings_bluetooth.xml index 09643e606350..58800c8d29be 100644 --- a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_settings_bluetooth.xml +++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_settings_bluetooth.xml @@ -16,6 +16,7 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" > diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml index d0f9d9b567f3..feed70c49138 100644 --- a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_bluetooth_share_icon.xml @@ -16,7 +16,7 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" - android:tint="@*android:color/accent_device_default" + android:tint="@*android:color/accent_device_default_light" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" > diff --git a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_settings_bluetooth.xml b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_settings_bluetooth.xml index 3d270b3fce42..5e1a5f20c6d6 100644 --- a/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_settings_bluetooth.xml +++ b/packages/overlays/IconPackRoundedAndroidOverlay/res/drawable/ic_settings_bluetooth.xml @@ -16,6 +16,7 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" + android:tint="?android:attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" > diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml index 704ff2ee2aa3..86fd47bfc63f 100644 --- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml +++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/config.xml @@ -33,4 +33,8 @@ <!-- Controls the size of the back gesture inset. --> <dimen name="config_backGestureInset">20dp</dimen> + <!-- Controls whether the navbar needs a scrim with + {@link Window#setEnsureNavigationBarContrastWhenTransparent}. --> + <bool name="config_navBarNeedsScrim">false</bool> + </resources> diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 0402b8fb9285..fdc01e0c1093 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -704,6 +704,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mClient.asBinder().linkToDeath(mClientVulture, 0); } catch (RemoteException e) { Slog.w(TAG, "could not set binder death listener on autofill client: " + e); + mClientVulture = null; } } @@ -714,6 +715,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (!unlinked) { Slog.w(TAG, "unlinking vulture from death failed for " + mActivityToken); } + mClientVulture = null; } } @@ -1243,18 +1245,55 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * when necessary. */ public void logContextCommitted() { - mHandler.sendMessage(obtainMessage( - Session::doLogContextCommitted, this)); + mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this)); + } + + private void handleLogContextCommitted() { + final FillResponse lastResponse; + synchronized (mLock) { + lastResponse = getLastResponseLocked("logContextCommited()"); + } + + if (lastResponse == null) { + Slog.w(TAG, "handleLogContextCommitted(): last response is null"); + return; + } + + // Merge UserData if necessary. + // Fields in packageUserData will override corresponding fields in genericUserData. + final UserData genericUserData = mService.getUserData(); + final UserData packageUserData = lastResponse.getUserData(); + final FieldClassificationUserData userData; + if (packageUserData == null && genericUserData == null) { + userData = null; + } else if (packageUserData != null && genericUserData != null) { + userData = new CompositeUserData(genericUserData, packageUserData); + } else if (packageUserData != null) { + userData = packageUserData; + } else { + userData = mService.getUserData(); + } + + final FieldClassificationStrategy fcStrategy = mService.getFieldClassificationStrategy(); + + // Sets field classification scores + if (userData != null && fcStrategy != null) { + logFieldClassificationScore(fcStrategy, userData); + } else { + logContextCommitted(null, null); + } } - private void doLogContextCommitted() { + private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds, + @Nullable ArrayList<FieldClassification> detectedFieldClassifications) { synchronized (mLock) { - logContextCommittedLocked(); + logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications); } } @GuardedBy("mLock") - private void logContextCommittedLocked() { + private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds, + @Nullable ArrayList<FieldClassification> detectedFieldClassifications) { final FillResponse lastResponse = getLastResponseLocked("logContextCommited()"); if (lastResponse == null) return; @@ -1308,21 +1347,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - // Merge UserData if necessary. - // Fields in packageUserData will override corresponding fields in genericUserData. - final UserData genericUserData = mService.getUserData(); - final UserData packageUserData = lastResponse.getUserData(); - final FieldClassificationUserData userData; - if (packageUserData == null && genericUserData == null) { - userData = null; - } else if (packageUserData != null && genericUserData != null) { - userData = new CompositeUserData(genericUserData, packageUserData); - } else if (packageUserData != null) { - userData = packageUserData; - } else { - userData = mService.getUserData(); - } - for (int i = 0; i < mViewStates.size(); i++) { final ViewState viewState = mViewStates.valueAt(i); final int state = viewState.getState(); @@ -1447,33 +1471,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - // Sets field classification scores - final FieldClassificationStrategy fcStrategy = mService.getFieldClassificationStrategy(); - if (userData != null && fcStrategy != null) { - logFieldClassificationScoreLocked(fcStrategy, ignoredDatasets, changedFieldIds, - changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds, - userData, mViewStates.values()); - } else { - mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, - ignoredDatasets, changedFieldIds, changedDatasetIds, - manuallyFilledFieldIds, manuallyFilledDatasetIds, - mComponentName, mCompatMode); - } + mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, + ignoredDatasets, changedFieldIds, changedDatasetIds, + manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds, + detectedFieldClassifications, mComponentName, mCompatMode); } /** * Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for * {@code fieldId} based on its {@code currentValue} and {@code userData}. */ - private void logFieldClassificationScoreLocked( - @NonNull FieldClassificationStrategy fcStrategy, - @NonNull ArraySet<String> ignoredDatasets, - @NonNull ArrayList<AutofillId> changedFieldIds, - @NonNull ArrayList<String> changedDatasetIds, - @NonNull ArrayList<AutofillId> manuallyFilledFieldIds, - @NonNull ArrayList<ArrayList<String>> manuallyFilledDatasetIds, - @NonNull FieldClassificationUserData userData, - @NonNull Collection<ViewState> viewStates) { + private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy, + @NonNull FieldClassificationUserData userData) { final String[] userValues = userData.getValues(); final String[] categoryIds = userData.getCategoryIds(); @@ -1499,6 +1508,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<FieldClassification> detectedFieldClassifications = new ArrayList<>( maxFieldsSize); + final Collection<ViewState> viewStates; + synchronized (mLock) { + viewStates = mViewStates.values(); + } + final int viewsSize = viewStates.size(); // First, we get all scores. @@ -1514,10 +1528,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final RemoteCallback callback = new RemoteCallback((result) -> { if (result == null) { if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results"); - mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, - ignoredDatasets, changedFieldIds, changedDatasetIds, - manuallyFilledFieldIds, manuallyFilledDatasetIds, - mComponentName, mCompatMode); + logContextCommitted(null, null); return; } final Scores scores = result.getParcelable(EXTRA_SCORES); @@ -1544,7 +1555,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final Float currentScore = scoresByField.get(categoryId); if (currentScore != null && currentScore > score) { if (sVerbose) { - Slog.v(TAG, "skipping score " + score + Slog.v(TAG, "skipping score " + score + " because it's less than " + currentScore); } continue; @@ -1554,8 +1565,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + autofillId); } scoresByField.put(categoryId, score); - } - else if (sVerbose) { + } else if (sVerbose) { Slog.v(TAG, "skipping score 0 at index " + j + " and id " + autofillId); } } @@ -1579,10 +1589,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, - ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, - manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, - mComponentName, mCompatMode); + logContextCommitted(detectedFieldIds, detectedFieldClassifications); }); fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds, diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index a3e7d3685100..54a3ecb22687 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -166,6 +166,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind @Override public void onUnlockUser(int userHandle) { Set<Association> associations = readAllAssociations(userHandle); + if (associations == null || associations.isEmpty()) { + return; + } Set<String> companionAppPackages = new HashSet<>(); for (Association association : associations) { companionAppPackages.add(association.companionAppPackage); diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 47c85683b4e6..1bd367cdfa94 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1764,7 +1764,8 @@ class AlarmManagerService extends SystemService { + ", callingPackage: " + callingPackage; // STOPSHIP (b/128866264): Just to catch breakages. Remove before final release. Slog.wtf(TAG, errorMsg); - throw new UnsupportedOperationException(errorMsg); + // TODO b/129995049: Resume throwing once issue is resolved. + // throw new UnsupportedOperationException(errorMsg); } setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, interval, operation, directReceiver, listenerTag, flags, true, workSource, diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 57de67e1a38c..e4c39ccd629c 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -108,7 +108,6 @@ import android.net.VpnService; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.netlink.InetDiagMessage; -import android.net.shared.NetworkMonitorUtils; import android.net.shared.PrivateDnsConfig; import android.net.util.MultinetworkPolicyTracker; import android.net.util.NetdService; @@ -238,6 +237,16 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final boolean LOGD_BLOCKED_NETWORKINFO = true; + /** + * Default URL to use for {@link #getCaptivePortalServerUrl()}. This should not be changed + * by OEMs for configuration purposes, as this value is overridden by + * Settings.Global.CAPTIVE_PORTAL_HTTP_URL. + * R.string.config_networkCaptivePortalServerUrl should be overridden instead for this purpose + * (preferably via runtime resource overlays). + */ + private static final String DEFAULT_CAPTIVE_PORTAL_HTTP_URL = + "http://connectivitycheck.gstatic.com/generate_204"; + // TODO: create better separation between radio types and network types // how long to wait before switching back to a radio's default network @@ -6543,7 +6552,7 @@ public class ConnectivityService extends IConnectivityManager.Stub uid, newRules, metered, mRestrictBackground); } if (oldBlocked == newBlocked) { - return; + continue; } final int arg = encodeBool(newBlocked); for (int i = 0; i < nai.numNetworkRequests(); i++) { @@ -6701,9 +6710,20 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public String getCaptivePortalServerUrl() { enforceConnectivityInternalPermission(); - final String defaultUrl = mContext.getResources().getString( - R.string.config_networkDefaultCaptivePortalServerUrl); - return NetworkMonitorUtils.getCaptivePortalServerHttpUrl(mContext, defaultUrl); + String settingUrl = mContext.getResources().getString( + R.string.config_networkCaptivePortalServerUrl); + + if (!TextUtils.isEmpty(settingUrl)) { + return settingUrl; + } + + settingUrl = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.CAPTIVE_PORTAL_HTTP_URL); + if (!TextUtils.isEmpty(settingUrl)) { + return settingUrl; + } + + return DEFAULT_CAPTIVE_PORTAL_HTTP_URL; } @Override diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index 99bbcf890a0a..f1882c5f641e 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -47,7 +47,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements private static IGsiService connect(DeathRecipient recipient) throws RemoteException { IBinder binder = ServiceManager.getService("gsiservice"); if (binder == null) { - throw new RemoteException(NO_SERVICE_ERROR); + return null; } /** * The init will restart gsiservice if it crashed and the proxy object will need to be @@ -68,26 +68,31 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements private IGsiService getGsiService() throws RemoteException { checkPermission(); + if (!"running".equals(SystemProperties.get("init.svc.gsid"))) { SystemProperties.set("ctl.start", "gsid"); - for (int sleepMs = 64; sleepMs <= (GSID_ROUGH_TIMEOUT_MS << 1); sleepMs <<= 1) { - try { - Thread.sleep(sleepMs); - } catch (InterruptedException e) { - Slog.e(TAG, "Interrupted when waiting for GSID"); - break; + } + + for (int sleepMs = 64; sleepMs <= (GSID_ROUGH_TIMEOUT_MS << 1); sleepMs <<= 1) { + synchronized (this) { + if (mGsiService == null) { + mGsiService = connect(this); } - if ("running".equals(SystemProperties.get("init.svc.gsid"))) { - break; + if (mGsiService != null) { + return mGsiService; } } - } - synchronized (this) { - if (mGsiService == null) { - mGsiService = connect(this); + + try { + Slog.d(TAG, "GsiService is not ready, wait for " + sleepMs + "ms"); + Thread.sleep(sleepMs); + } catch (InterruptedException e) { + Slog.e(TAG, "Interrupted when waiting for GSID"); + return null; } - return mGsiService; } + + throw new RemoteException(NO_SERVICE_ERROR); } private void checkPermission() { diff --git a/services/core/java/com/android/server/ExplicitHealthCheckController.java b/services/core/java/com/android/server/ExplicitHealthCheckController.java index 27ad208ef8a1..19ab33e85f5e 100644 --- a/services/core/java/com/android/server/ExplicitHealthCheckController.java +++ b/services/core/java/com/android/server/ExplicitHealthCheckController.java @@ -35,7 +35,9 @@ import android.os.RemoteException; import android.os.UserHandle; import android.service.watchdog.ExplicitHealthCheckService; import android.service.watchdog.IExplicitHealthCheckService; +import android.service.watchdog.PackageInfo; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -69,7 +71,7 @@ class ExplicitHealthCheckController { // To prevent deadlocks between the controller and watchdog threads, we have // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class. // It's easier to just NOT hold #mLock when calling into watchdog code on this consumer. - @GuardedBy("mLock") @Nullable private Consumer<List<String>> mSupportedConsumer; + @GuardedBy("mLock") @Nullable private Consumer<List<PackageInfo>> mSupportedConsumer; // Called everytime we need to notify the watchdog to sync requests between itself and the // health check service. In practice, should never be null after it has been #setEnabled. // To prevent deadlocks between the controller and watchdog threads, we have @@ -104,7 +106,7 @@ class ExplicitHealthCheckController { * ensure a happens-before relationship of the set parameters and visibility on other threads. */ public void setCallbacks(Consumer<String> passedConsumer, - Consumer<List<String>> supportedConsumer, Runnable notifySyncRunnable) { + Consumer<List<PackageInfo>> supportedConsumer, Runnable notifySyncRunnable) { synchronized (mLock) { if (mPassedConsumer != null || mSupportedConsumer != null || mNotifySyncRunnable != null) { @@ -144,14 +146,18 @@ class ExplicitHealthCheckController { return; } - getSupportedPackages(supportedPackages -> { + getSupportedPackages(supportedPackageInfos -> { // Notify the watchdog without lock held - mSupportedConsumer.accept(supportedPackages); + mSupportedConsumer.accept(supportedPackageInfos); getRequestedPackages(previousRequestedPackages -> { synchronized (mLock) { // Hold lock so requests and cancellations are sent atomically. // It is important we don't mix requests from multiple threads. + Set<String> supportedPackages = new ArraySet<>(); + for (PackageInfo info : supportedPackageInfos) { + supportedPackages.add(info.getPackageName()); + } // Note, this may modify newRequestedPackages newRequestedPackages.retainAll(supportedPackages); @@ -229,7 +235,7 @@ class ExplicitHealthCheckController { * Returns the packages that we can request explicit health checks for. * The packages will be returned to the {@code consumer}. */ - private void getSupportedPackages(Consumer<List<String>> consumer) { + private void getSupportedPackages(Consumer<List<PackageInfo>> consumer) { synchronized (mLock) { if (!prepareServiceLocked("get health check supported packages")) { return; @@ -238,7 +244,8 @@ class ExplicitHealthCheckController { Slog.d(TAG, "Getting health check supported packages"); try { mRemoteService.getSupportedPackages(new RemoteCallback(result -> { - List<String> packages = result.getStringArrayList(EXTRA_SUPPORTED_PACKAGES); + List<PackageInfo> packages = + result.getParcelableArrayList(EXTRA_SUPPORTED_PACKAGES); Slog.i(TAG, "Explicit health check supported packages " + packages); consumer.accept(packages); })); diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 2cfcecca5f99..2055b64483d9 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -208,6 +208,7 @@ public class IpSecService extends IIpSecService.Stub { mBinder.linkToDeath(this, 0); } catch (RemoteException e) { binderDied(); + e.rethrowFromSystemServer(); } } } diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 0c681df036b4..7d0d8344a566 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -27,6 +27,7 @@ import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; +import android.service.watchdog.PackageInfo; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -57,6 +58,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -102,16 +104,10 @@ public class PackageWatchdog { private boolean mIsHealthCheckEnabled = true; @GuardedBy("mLock") private boolean mIsPackagesReady; - // SystemClock#uptimeMillis when we last executed #pruneObservers. + // SystemClock#uptimeMillis when we last executed #syncState // 0 if no prune is scheduled. @GuardedBy("mLock") - private long mUptimeAtLastPruneMs; - // Duration in millis that the last prune was scheduled for. - // Used along with #mUptimeAtLastPruneMs after scheduling a prune to determine the remaining - // duration before #pruneObservers will be executed. - // 0 if no prune is scheduled. - @GuardedBy("mLock") - private long mDurationAtLastPrune; + private long mUptimeAtLastStateSync; private PackageWatchdog(Context context) { // Needs to be constructed inline @@ -156,7 +152,7 @@ public class PackageWatchdog { mHealthCheckController.setCallbacks(packageName -> onHealthCheckPassed(packageName), packages -> onSupportedPackages(packages), () -> syncRequestsAsync()); - // Controller is initially disabled until here where we may enable it and sync requests + // Controller is initially disabled until here where we may enable it and sync our state setExplicitHealthCheckEnabled(mIsHealthCheckEnabled); } } @@ -173,10 +169,6 @@ public class PackageWatchdog { if (internalObserver != null) { internalObserver.mRegisteredObserver = observer; } - if (mDurationAtLastPrune == 0) { - // Nothing running, prune - pruneAndSchedule(); - } } } @@ -214,6 +206,12 @@ public class PackageWatchdog { packages.add(new MonitoredPackage(packageNames.get(i), durationMs, false)); } + // Sync before we add the new packages to the observers. This will #pruneObservers, + // causing any elapsed time to be deducted from all existing packages before we add new + // packages. This maintains the invariant that the elapsed time for ALL (new and existing) + // packages is the same. + syncState("observing new packages"); + synchronized (mLock) { ObserverInternal oldObserver = mAllObservers.get(observer.getName()); if (oldObserver == null) { @@ -224,16 +222,16 @@ public class PackageWatchdog { } else { Slog.d(TAG, observer.getName() + " added the following " + "packages to monitor " + packageNames); - oldObserver.updatePackages(packages); + oldObserver.updatePackagesLocked(packages); } } + + // Register observer in case not already registered registerHealthObserver(observer); - // Always prune because we may have received packges requiring an earlier - // schedule than we are currently scheduled for. - pruneAndSchedule(); - Slog.i(TAG, "Syncing health check requests, observing packages " + packageNames); - syncRequestsAsync(); - saveToFileAsync(); + + // Sync after we add the new packages to the observers. We may have received packges + // requiring an earlier schedule than we are currently scheduled for. + syncState("updated observers"); } /** @@ -245,7 +243,7 @@ public class PackageWatchdog { synchronized (mLock) { mAllObservers.remove(observer.getName()); } - saveToFileAsync(); + syncState("unregistering observer: " + observer.getName()); } /** @@ -296,7 +294,8 @@ public class PackageWatchdog { ObserverInternal observer = mAllObservers.valueAt(oIndex); PackageHealthObserver registeredObserver = observer.mRegisteredObserver; if (registeredObserver != null - && observer.onPackageFailure(versionedPackage.getPackageName())) { + && observer.onPackageFailureLocked( + versionedPackage.getPackageName())) { int impact = registeredObserver.onHealthCheckFailed(versionedPackage); if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE && impact < currentObserverImpact) { @@ -321,9 +320,11 @@ public class PackageWatchdog { /** Writes the package information to file during shutdown. */ public void writeNow() { synchronized (mLock) { + // Must only run synchronous tasks as this runs on the ShutdownThread and no other + // thread is guaranteed to run during shutdown. if (!mAllObservers.isEmpty()) { - mLongTaskHandler.removeCallbacks(this::saveToFile); - pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastPruneMs); + mLongTaskHandler.removeCallbacks(this::saveToFileAsync); + pruneObserversLocked(); saveToFile(); Slog.i(TAG, "Last write to update package durations"); } @@ -341,9 +342,8 @@ public class PackageWatchdog { synchronized (mLock) { mIsHealthCheckEnabled = enabled; mHealthCheckController.setEnabled(enabled); - Slog.i(TAG, "Syncing health check requests, explicit health check is " - + (enabled ? "enabled" : "disabled")); - syncRequestsAsync(); + // Prune to update internal state whenever health check is enabled/disabled + syncState("health check state " + (enabled ? "enabled" : "disabled")); } } @@ -393,9 +393,8 @@ public class PackageWatchdog { * Serializes and syncs health check requests with the {@link ExplicitHealthCheckController}. */ private void syncRequestsAsync() { - if (!mShortTaskHandler.hasCallbacks(this::syncRequests)) { - mShortTaskHandler.post(this::syncRequests); - } + mShortTaskHandler.removeCallbacks(this::syncRequests); + mShortTaskHandler.post(this::syncRequests); } /** @@ -414,6 +413,7 @@ public class PackageWatchdog { // Call outside lock to avoid holding lock when calling into the controller. if (packages != null) { + Slog.i(TAG, "Syncing health check requests for packages: " + packages); mHealthCheckController.syncRequests(packages); } } @@ -426,86 +426,73 @@ public class PackageWatchdog { * effectively behave as if the explicit health check hasn't passed for {@code packageName}. * * <p> {@code packageName} can still be considered failed if reported by - * {@link #onPackageFailure} before the package expires. + * {@link #onPackageFailureLocked} before the package expires. * * <p> Triggered by components outside the system server when they are fully functional after an * update. */ private void onHealthCheckPassed(String packageName) { Slog.i(TAG, "Health check passed for package: " + packageName); - boolean shouldUpdateFile = false; + boolean isStateChanged = false; + synchronized (mLock) { for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) { ObserverInternal observer = mAllObservers.valueAt(observerIdx); MonitoredPackage monitoredPackage = observer.mPackages.get(packageName); - if (monitoredPackage != null && !monitoredPackage.mHasPassedHealthCheck) { - monitoredPackage.mHasPassedHealthCheck = true; - shouldUpdateFile = true; + + if (monitoredPackage != null) { + int oldState = monitoredPackage.getHealthCheckStateLocked(); + int newState = monitoredPackage.tryPassHealthCheckLocked(); + isStateChanged |= oldState != newState; } } } - // So we can unbind from the service if this was the last result we expected - Slog.i(TAG, "Syncing health check requests, health check passed for " + packageName); - syncRequestsAsync(); - - if (shouldUpdateFile) { - saveToFileAsync(); + if (isStateChanged) { + syncState("health check passed for " + packageName); } } - private void onSupportedPackages(List<String> supportedPackages) { - boolean shouldUpdateFile = false; - boolean shouldPrune = false; + private void onSupportedPackages(List<PackageInfo> supportedPackages) { + boolean isStateChanged = false; + + Map<String, Long> supportedPackageTimeouts = new ArrayMap<>(); + Iterator<PackageInfo> it = supportedPackages.iterator(); + while (it.hasNext()) { + PackageInfo info = it.next(); + supportedPackageTimeouts.put(info.getPackageName(), info.getHealthCheckTimeoutMillis()); + } synchronized (mLock) { Slog.d(TAG, "Received supported packages " + supportedPackages); Iterator<ObserverInternal> oit = mAllObservers.values().iterator(); while (oit.hasNext()) { - ObserverInternal observer = oit.next(); - Iterator<MonitoredPackage> pit = - observer.mPackages.values().iterator(); + Iterator<MonitoredPackage> pit = oit.next().mPackages.values().iterator(); while (pit.hasNext()) { MonitoredPackage monitoredPackage = pit.next(); - String packageName = monitoredPackage.mName; - int healthCheckState = monitoredPackage.getHealthCheckState(); - - if (healthCheckState != MonitoredPackage.STATE_PASSED) { - // Have to update file, we will either transition state or reduce - // health check duration - shouldUpdateFile = true; - - if (supportedPackages.contains(packageName)) { - // Supports health check, transition to ACTIVE if not already. - // We need to prune packages earlier than already scheduled. - shouldPrune = true; - - // TODO: Get healthCheckDuration from supportedPackages - long healthCheckDuration = monitoredPackage.mDurationMs; - monitoredPackage.mHealthCheckDurationMs = Math.min(healthCheckDuration, - monitoredPackage.mDurationMs); - Slog.i(TAG, packageName + " health check state is now: ACTIVE(" - + monitoredPackage.mHealthCheckDurationMs + "ms)"); - } else { - // Does not support health check, transistion to PASSED - monitoredPackage.mHasPassedHealthCheck = true; - Slog.i(TAG, packageName + " health check state is now: PASSED"); - } + String packageName = monitoredPackage.getName(); + int oldState = monitoredPackage.getHealthCheckStateLocked(); + int newState; + + if (supportedPackageTimeouts.containsKey(packageName)) { + // Supported packages become ACTIVE if currently INACTIVE + newState = monitoredPackage.setHealthCheckActiveLocked( + supportedPackageTimeouts.get(packageName)); } else { - Slog.i(TAG, packageName + " does not support health check, state: PASSED"); + // Unsupported packages are marked as PASSED unless already FAILED + newState = monitoredPackage.tryPassHealthCheckLocked(); } + isStateChanged |= oldState != newState; } } } - if (shouldUpdateFile) { - saveToFileAsync(); - } - if (shouldPrune) { - pruneAndSchedule(); + if (isStateChanged) { + syncState("updated health check supported packages " + supportedPackages); } } + @GuardedBy("mLock") private Set<String> getPackagesPendingHealthChecksLocked() { Slog.d(TAG, "Getting all observed packages pending health checks"); Set<String> packages = new ArraySet<>(); @@ -516,8 +503,9 @@ public class PackageWatchdog { observer.mPackages.values().iterator(); while (pit.hasNext()) { MonitoredPackage monitoredPackage = pit.next(); - String packageName = monitoredPackage.mName; - if (!monitoredPackage.mHasPassedHealthCheck) { + String packageName = monitoredPackage.getName(); + if (monitoredPackage.getHealthCheckStateLocked() + != MonitoredPackage.STATE_PASSED) { packages.add(packageName); } } @@ -525,88 +513,91 @@ public class PackageWatchdog { return packages; } - /** Executes {@link #pruneObservers} and schedules the next execution. */ - private void pruneAndSchedule() { + /** + * Syncs the state of the observers. + * + * <p> Prunes all observers, saves new state to disk, syncs health check requests with the + * health check service and schedules the next state sync. + */ + private void syncState(String reason) { synchronized (mLock) { - long nextDurationToScheduleMs = getNextPruneScheduleMillisLocked(); - if (nextDurationToScheduleMs == Long.MAX_VALUE) { - Slog.i(TAG, "No monitored packages, ending prune"); - mDurationAtLastPrune = 0; - mUptimeAtLastPruneMs = 0; - return; - } - long uptimeMs = SystemClock.uptimeMillis(); - // O if not running - long elapsedDurationMs = mUptimeAtLastPruneMs == 0 - ? 0 : uptimeMs - mUptimeAtLastPruneMs; - // Less than O if unexpectedly didn't run yet even though - // we are past the last duration scheduled to run - long remainingDurationMs = mDurationAtLastPrune - elapsedDurationMs; - if (mUptimeAtLastPruneMs == 0 - || remainingDurationMs <= 0 - || nextDurationToScheduleMs < remainingDurationMs) { - // First schedule or an earlier reschedule - pruneObservers(elapsedDurationMs); - // We don't use Handler#hasCallbacks because we want to update the schedule delay - mShortTaskHandler.removeCallbacks(this::pruneAndSchedule); - mShortTaskHandler.postDelayed(this::pruneAndSchedule, nextDurationToScheduleMs); - mDurationAtLastPrune = nextDurationToScheduleMs; - mUptimeAtLastPruneMs = uptimeMs; - } + Slog.i(TAG, "Syncing state, reason: " + reason); + pruneObserversLocked(); + + saveToFileAsync(); + syncRequestsAsync(); + + // Done syncing state, schedule the next state sync + scheduleNextSyncStateLocked(); + } + } + + private void syncStateWithScheduledReason() { + syncState("scheduled"); + } + + @GuardedBy("mLock") + private void scheduleNextSyncStateLocked() { + long durationMs = getNextStateSyncMillisLocked(); + mShortTaskHandler.removeCallbacks(this::syncStateWithScheduledReason); + if (durationMs == Long.MAX_VALUE) { + Slog.i(TAG, "Cancelling state sync, nothing to sync"); + mUptimeAtLastStateSync = 0; + } else { + Slog.i(TAG, "Scheduling next state sync in " + durationMs + "ms"); + mUptimeAtLastStateSync = SystemClock.uptimeMillis(); + mShortTaskHandler.postDelayed(this::syncStateWithScheduledReason, durationMs); } } /** - * Returns the next time in millis to schedule a prune. + * Returns the next duration in millis to sync the watchdog state. * * @returns Long#MAX_VALUE if there are no observed packages. */ - private long getNextPruneScheduleMillisLocked() { + @GuardedBy("mLock") + private long getNextStateSyncMillisLocked() { long shortestDurationMs = Long.MAX_VALUE; for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex).mPackages; for (int pIndex = 0; pIndex < packages.size(); pIndex++) { MonitoredPackage mp = packages.valueAt(pIndex); - long duration = Math.min(mp.mDurationMs, mp.mHealthCheckDurationMs); + long duration = mp.getShortestScheduleDurationMsLocked(); if (duration < shortestDurationMs) { shortestDurationMs = duration; } } } - Slog.i(TAG, "Next prune will be scheduled in " + shortestDurationMs + "ms"); - return shortestDurationMs; } /** - * Removes {@code elapsedMs} milliseconds from all durations on monitored packages. - * - * <p> Prunes all observers with {@link ObserverInternal#prunePackages} and discards observers - * without any packages left. + * Removes {@code elapsedMs} milliseconds from all durations on monitored packages + * and updates other internal state. */ - private void pruneObservers(long elapsedMs) { - if (elapsedMs == 0) { + @GuardedBy("mLock") + private void pruneObserversLocked() { + long elapsedMs = mUptimeAtLastStateSync == 0 + ? 0 : SystemClock.uptimeMillis() - mUptimeAtLastStateSync; + if (elapsedMs <= 0) { + Slog.i(TAG, "Not pruning observers, elapsed time: " + elapsedMs + "ms"); return; } - synchronized (mLock) { - Slog.d(TAG, "Removing expired packages after " + elapsedMs + "ms"); - Iterator<ObserverInternal> it = mAllObservers.values().iterator(); - while (it.hasNext()) { - ObserverInternal observer = it.next(); - Set<MonitoredPackage> failedPackages = - observer.prunePackages(elapsedMs); - if (!failedPackages.isEmpty()) { - onHealthCheckFailed(observer, failedPackages); - } - if (observer.mPackages.isEmpty()) { - Slog.i(TAG, "Discarding observer " + observer.mName + ". All packages expired"); - it.remove(); - } + + Slog.i(TAG, "Removing " + elapsedMs + "ms from all packages on all observers"); + Iterator<ObserverInternal> it = mAllObservers.values().iterator(); + while (it.hasNext()) { + ObserverInternal observer = it.next(); + Set<MonitoredPackage> failedPackages = + observer.prunePackagesLocked(elapsedMs); + if (!failedPackages.isEmpty()) { + onHealthCheckFailed(observer, failedPackages); + } + if (observer.mPackages.isEmpty()) { + Slog.i(TAG, "Discarding observer " + observer.mName + ". All packages expired"); + it.remove(); } } - Slog.i(TAG, "Syncing health check requests, pruned observers"); - syncRequestsAsync(); - saveToFileAsync(); } private void onHealthCheckFailed(ObserverInternal observer, @@ -618,7 +609,7 @@ public class PackageWatchdog { PackageManager pm = mContext.getPackageManager(); Iterator<MonitoredPackage> it = failedPackages.iterator(); while (it.hasNext()) { - String failedPackage = it.next().mName; + String failedPackage = it.next().getName(); long versionCode = 0; Slog.i(TAG, "Explicit health check failed for package " + failedPackage); try { @@ -673,6 +664,7 @@ public class PackageWatchdog { * Persists mAllObservers to file. Threshold information is ignored. */ private boolean saveToFile() { + Slog.i(TAG, "Saving observer state to file"); synchronized (mLock) { FileOutputStream stream; try { @@ -689,7 +681,7 @@ public class PackageWatchdog { out.startTag(null, TAG_PACKAGE_WATCHDOG); out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { - mAllObservers.valueAt(oIndex).write(out); + mAllObservers.valueAt(oIndex).writeLocked(out); } out.endTag(null, TAG_PACKAGE_WATCHDOG); out.endDocument(); @@ -730,7 +722,7 @@ public class PackageWatchdog { ObserverInternal(String name, List<MonitoredPackage> packages) { mName = name; - updatePackages(packages); + updatePackagesLocked(packages); } /** @@ -738,20 +730,13 @@ public class PackageWatchdog { * Does not persist any package failure thresholds. */ @GuardedBy("mLock") - public boolean write(XmlSerializer out) { + public boolean writeLocked(XmlSerializer out) { try { out.startTag(null, TAG_OBSERVER); out.attribute(null, ATTR_NAME, mName); for (int i = 0; i < mPackages.size(); i++) { MonitoredPackage p = mPackages.valueAt(i); - out.startTag(null, TAG_PACKAGE); - out.attribute(null, ATTR_NAME, p.mName); - out.attribute(null, ATTR_DURATION, String.valueOf(p.mDurationMs)); - out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, - String.valueOf(p.mHealthCheckDurationMs)); - out.attribute(null, ATTR_PASSED_HEALTH_CHECK, - String.valueOf(p.mHasPassedHealthCheck)); - out.endTag(null, TAG_PACKAGE); + p.writeLocked(out); } out.endTag(null, TAG_OBSERVER); return true; @@ -762,7 +747,7 @@ public class PackageWatchdog { } @GuardedBy("mLock") - public void updatePackages(List<MonitoredPackage> packages) { + public void updatePackagesLocked(List<MonitoredPackage> packages) { for (int pIndex = 0; pIndex < packages.size(); pIndex++) { MonitoredPackage p = packages.get(pIndex); mPackages.put(p.mName, p); @@ -775,37 +760,24 @@ public class PackageWatchdog { * observation. If any health check duration is less than 0, the health check result * is evaluated. * - * @returns a {@link Set} of packages that were removed from the observer without explicit + * @return a {@link Set} of packages that were removed from the observer without explicit * health check passing, or an empty list if no package expired for which an explicit health * check was still pending */ @GuardedBy("mLock") - private Set<MonitoredPackage> prunePackages(long elapsedMs) { + private Set<MonitoredPackage> prunePackagesLocked(long elapsedMs) { Set<MonitoredPackage> failedPackages = new ArraySet<>(); Iterator<MonitoredPackage> it = mPackages.values().iterator(); while (it.hasNext()) { MonitoredPackage p = it.next(); - int healthCheckState = p.getHealthCheckState(); - - // Handle health check timeouts - if (healthCheckState == MonitoredPackage.STATE_ACTIVE) { - // Only reduce duration if state is active - p.mHealthCheckDurationMs -= elapsedMs; - // Check duration after reducing duration - if (p.mHealthCheckDurationMs <= 0) { - failedPackages.add(p); - } + int oldState = p.getHealthCheckStateLocked(); + int newState = p.handleElapsedTimeLocked(elapsedMs); + if (oldState != MonitoredPackage.STATE_FAILED + && newState == MonitoredPackage.STATE_FAILED) { + Slog.i(TAG, "Package " + p.mName + " failed health check"); + failedPackages.add(p); } - - // Handle package expiry - p.mDurationMs -= elapsedMs; - // Check duration after reducing duration - if (p.mDurationMs <= 0) { - if (healthCheckState == MonitoredPackage.STATE_INACTIVE) { - Slog.w(TAG, "Package " + p.mName - + " expiring without starting health check, failing"); - failedPackages.add(p); - } + if (p.isExpiredLocked()) { it.remove(); } } @@ -817,10 +789,10 @@ public class PackageWatchdog { * @returns {@code true} if failure threshold is exceeded, {@code false} otherwise */ @GuardedBy("mLock") - public boolean onPackageFailure(String packageName) { + public boolean onPackageFailureLocked(String packageName) { MonitoredPackage p = mPackages.get(packageName); if (p != null) { - return p.onFailure(); + return p.onFailureLocked(); } return false; } @@ -877,33 +849,45 @@ public class PackageWatchdog { } /** - * Represents a package along with the time it should be monitored for. + * Represents a package and its health check state along with the time + * it should be monitored for. * * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing * instances of this class. */ - //TODO(b/120598832): Remove 'm' from non-private fields - private static class MonitoredPackage { + static class MonitoredPackage { // Health check states + // TODO(b/120598832): Prefix with HEALTH_CHECK // mName has not passed health check but has requested a health check - public static int STATE_ACTIVE = 0; + public static final int STATE_ACTIVE = 0; // mName has not passed health check and has not requested a health check - public static int STATE_INACTIVE = 1; + public static final int STATE_INACTIVE = 1; // mName has passed health check - public static int STATE_PASSED = 2; - - public final String mName; - // Whether an explicit health check has passed + public static final int STATE_PASSED = 2; + // mName has failed health check + public static final int STATE_FAILED = 3; + + //TODO(b/120598832): VersionedPackage? + private final String mName; + // One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after + // methods that could change the health check state: handleElapsedTimeLocked and + // tryPassHealthCheckLocked + private int mHealthCheckState = STATE_INACTIVE; + // Whether an explicit health check has passed. + // This value in addition with mHealthCheckDurationMs determines the health check state + // of the package, see #getHealthCheckStateLocked @GuardedBy("mLock") - public boolean mHasPassedHealthCheck; - // System uptime duration to monitor package + private boolean mHasPassedHealthCheck; + // System uptime duration to monitor package. @GuardedBy("mLock") - public long mDurationMs; + private long mDurationMs; // System uptime duration to check the result of an explicit health check // Initially, MAX_VALUE until we get a value from the health check service // and request health checks. + // This value in addition with mHasPassedHealthCheck determines the health check state + // of the package, see #getHealthCheckStateLocked @GuardedBy("mLock") - public long mHealthCheckDurationMs = Long.MAX_VALUE; + private long mHealthCheckDurationMs = Long.MAX_VALUE; // System uptime of first package failure @GuardedBy("mLock") private long mUptimeStartMs; @@ -921,6 +905,20 @@ public class PackageWatchdog { mDurationMs = durationMs; mHealthCheckDurationMs = healthCheckDurationMs; mHasPassedHealthCheck = hasPassedHealthCheck; + updateHealthCheckStateLocked(); + } + + /** Writes the salient fields to disk using {@code out}. */ + @GuardedBy("mLock") + public void writeLocked(XmlSerializer out) throws IOException { + out.startTag(null, TAG_PACKAGE); + out.attribute(null, ATTR_NAME, mName); + out.attribute(null, ATTR_DURATION, String.valueOf(mDurationMs)); + out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, + String.valueOf(mHealthCheckDurationMs)); + out.attribute(null, ATTR_PASSED_HEALTH_CHECK, + String.valueOf(mHasPassedHealthCheck)); + out.endTag(null, TAG_PACKAGE); } /** @@ -929,7 +927,7 @@ public class PackageWatchdog { * @return {@code true} if failure count exceeds a threshold, {@code false} otherwise */ @GuardedBy("mLock") - public boolean onFailure() { + public boolean onFailureLocked() { final long now = SystemClock.uptimeMillis(); final long duration = now - mUptimeStartMs; if (duration > TRIGGER_DURATION_MS) { @@ -949,18 +947,141 @@ public class PackageWatchdog { } /** - * Returns any of the health check states of {@link #STATE_ACTIVE}, + * Sets the initial health check duration. + * + * @return the new health check state + */ + @GuardedBy("mLock") + public int setHealthCheckActiveLocked(long initialHealthCheckDurationMs) { + if (initialHealthCheckDurationMs <= 0) { + Slog.wtf(TAG, "Cannot set non-positive health check duration " + + initialHealthCheckDurationMs + "ms for package " + mName + + ". Using total duration " + mDurationMs + "ms instead"); + initialHealthCheckDurationMs = mDurationMs; + } + if (mHealthCheckState == STATE_INACTIVE) { + // Transitions to ACTIVE + mHealthCheckDurationMs = initialHealthCheckDurationMs; + } + return updateHealthCheckStateLocked(); + } + + /** + * Updates the monitoring durations of the package. + * + * @return the new health check state + */ + @GuardedBy("mLock") + public int handleElapsedTimeLocked(long elapsedMs) { + if (elapsedMs <= 0) { + Slog.w(TAG, "Cannot handle non-positive elapsed time for package " + mName); + return mHealthCheckState; + } + // Transitions to FAILED if now <= 0 and health check not passed + mDurationMs -= elapsedMs; + if (mHealthCheckState == STATE_ACTIVE) { + // We only update health check durations if we have #setHealthCheckActiveLocked + // This ensures we don't leave the INACTIVE state for an unexpected elapsed time + // Transitions to FAILED if now <= 0 and health check not passed + mHealthCheckDurationMs -= elapsedMs; + } + return updateHealthCheckStateLocked(); + } + + /** + * Marks the health check as passed and transitions to {@link #STATE_PASSED} + * if not yet {@link #STATE_FAILED}. + * + * @return the new health check state + */ + @GuardedBy("mLock") + public int tryPassHealthCheckLocked() { + if (mHealthCheckState != STATE_FAILED) { + // FAILED is a final state so only pass if we haven't failed + // Transition to PASSED + mHasPassedHealthCheck = true; + } + return updateHealthCheckStateLocked(); + } + + /** Returns the monitored package name. */ + private String getName() { + return mName; + } + + //TODO(b/120598832): IntDef + /** + * Returns the current health check state, any of {@link #STATE_ACTIVE}, * {@link #STATE_INACTIVE} or {@link #STATE_PASSED} */ @GuardedBy("mLock") - public int getHealthCheckState() { + public int getHealthCheckStateLocked() { + return mHealthCheckState; + } + + /** + * Returns the shortest duration before the package should be scheduled for a prune. + * + * @return the duration or {@link Long#MAX_VALUE} if the package should not be scheduled + */ + @GuardedBy("mLock") + public long getShortestScheduleDurationMsLocked() { + return Math.min(toPositive(mDurationMs), toPositive(mHealthCheckDurationMs)); + } + + /** + * Returns {@code true} if the total duration left to monitor the package is less than or + * equal to 0 {@code false} otherwise. + */ + @GuardedBy("mLock") + public boolean isExpiredLocked() { + return mDurationMs <= 0; + } + + /** + * Updates the health check state based on {@link #mHasPassedHealthCheck} + * and {@link #mHealthCheckDurationMs}. + * + * @return the new health check state + */ + @GuardedBy("mLock") + private int updateHealthCheckStateLocked() { + int oldState = mHealthCheckState; if (mHasPassedHealthCheck) { - return STATE_PASSED; + // Set final state first to avoid ambiguity + mHealthCheckState = STATE_PASSED; + } else if (mHealthCheckDurationMs <= 0 || mDurationMs <= 0) { + // Set final state first to avoid ambiguity + mHealthCheckState = STATE_FAILED; } else if (mHealthCheckDurationMs == Long.MAX_VALUE) { - return STATE_INACTIVE; + mHealthCheckState = STATE_INACTIVE; } else { - return STATE_ACTIVE; + mHealthCheckState = STATE_ACTIVE; + } + Slog.i(TAG, "Updated health check state for package " + mName + ": " + + toString(oldState) + " -> " + toString(mHealthCheckState)); + return mHealthCheckState; + } + + /** Returns a {@link String} representation of the current health check state. */ + private static String toString(int state) { + switch (state) { + case STATE_ACTIVE: + return "ACTIVE"; + case STATE_INACTIVE: + return "INACTIVE"; + case STATE_PASSED: + return "PASSED"; + case STATE_FAILED: + return "FAILED"; + default: + return "UNKNOWN"; } } + + /** Returns {@code value} if it is greater than 0 or {@link Long#MAX_VALUE} otherwise. */ + private static long toPositive(long value) { + return value > 0 ? value : Long.MAX_VALUE; + } } } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index ac584e9571fc..af78b769ce8c 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -1172,7 +1172,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(i)) .findFirst().getAsInt(); } catch (NoSuchElementException ex) { - log("notifyCarrierNetworkChange without carrier privilege"); + loge("notifyCarrierNetworkChange without carrier privilege"); + } + // the active subId does not have carrier privilege. + if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + throw new SecurityException("notifyCarrierNetworkChange without carrier privilege"); } int phoneId = SubscriptionManager.getPhoneId(subId); @@ -2272,6 +2276,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { Rlog.d(TAG, s); } + private static void loge(String s) { + Rlog.e(TAG, s); + } + boolean idMatch(int rSubId, int subId, int phoneId) { if(subId < 0) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1757c9816355..0b9e3bb1b99e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6147,8 +6147,9 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void moveTaskToFront(int taskId, int flags, Bundle bOptions) { - mActivityTaskManager.moveTaskToFront(taskId, flags, bOptions); + public void moveTaskToFront(IApplicationThread appThread, String callingPackage, int taskId, + int flags, Bundle bOptions) { + mActivityTaskManager.moveTaskToFront(appThread, callingPackage, taskId, flags, bOptions); } /** diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 8847e32d0fb5..01a3a6fb25d1 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -1020,11 +1020,7 @@ class UserController implements Handler.Callback { if (state.state == STATE_RUNNING_UNLOCKED) { // We'll skip all later code, so we must tell listener it's already // unlocked. - try { - unlockListener.onFinished(userId, null); - } catch (RemoteException ignore) { - // Ignore. - } + notifyFinished(userId, unlockListener); } return true; } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index d073bc60c766..4c3bb8c07728 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -1840,11 +1840,14 @@ public class AppOpsService extends IAppOpsService.Stub { } private boolean isPackageSuspendedForUser(String pkg, int uid) { + final long identity = Binder.clearCallingIdentity(); try { return AppGlobals.getPackageManager().isPackageSuspendedForUser( pkg, UserHandle.getUserId(uid)); } catch (RemoteException re) { throw new SecurityException("Could not talk to package manager service"); + } finally { + Binder.restoreCallingIdentity(identity); } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 3fc2d3712fed..d58888a7c67b 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1960,7 +1960,7 @@ public class AudioService extends IAudioService.Stub return; } for (final int groupedStream : avg.getLegacyStreamTypes()) { - setStreamVolume(stream, index, flags, callingPackage, callingPackage, + setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage, Binder.getCallingUid()); } } @@ -3368,8 +3368,14 @@ public class AudioService extends IAudioService.Stub .append(Binder.getCallingPid()).toString(); final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(on, eventSource); if (stateChanged) { - mContext.sendBroadcast(new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED) - .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)); + final long ident = Binder.clearCallingIdentity(); + try { + mContext.sendBroadcastAsUser( + new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL); + } finally { + Binder.restoreCallingIdentity(ident); + } } } diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java index 7d4ac592a9df..f7278d2601c3 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java @@ -129,13 +129,16 @@ public abstract class AuthenticationClient extends ClientMonitor { boolean result = false; try { + if (DEBUG) Slog.v(getLogTag(), "onAuthenticated(" + authenticated + ")" + + ", ID:" + identifier.getBiometricId() + + ", Owner: " + getOwnerString() + + ", isBP: " + isBiometricPrompt() + + ", listener: " + listener + + ", requireConfirmation: " + mRequireConfirmation); + if (authenticated) { mAlreadyDone = true; - if (DEBUG) Slog.v(getLogTag(), "onAuthenticated(" + getOwnerString() - + ", ID:" + identifier.getBiometricId() - + ", isBP: " + isBiometricPrompt() - + ", listener: " + listener - + ", requireConfirmation: " + mRequireConfirmation); + if (listener != null) { vibrateSuccess(); } diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 843ecac14b62..153133a6c669 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -41,6 +41,7 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; @@ -85,6 +86,7 @@ import java.util.Random; public class BiometricService extends SystemService { private static final String TAG = "BiometricService"; + private static final boolean DEBUG = true; private static final int MSG_ON_TASK_STACK_CHANGED = 1; private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2; @@ -96,6 +98,9 @@ public class BiometricService extends SystemService { private static final int MSG_ON_READY_FOR_AUTHENTICATION = 8; private static final int MSG_AUTHENTICATE = 9; private static final int MSG_CANCEL_AUTHENTICATION = 10; + private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS = 11; + private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR = 12; + private static final int MSG_REGISTER_CANCELLATION_CALLBACK = 13; private static final int[] FEATURE_ID = { TYPE_FINGERPRINT, @@ -128,8 +133,12 @@ public class BiometricService extends SystemService { * Authentication is successful, but we're waiting for the user to press "confirm" button. */ private static final int STATE_AUTH_PENDING_CONFIRM = 5; + /** + * Biometric authentication was canceled, but the device is now showing ConfirmDeviceCredential + */ + private static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6; - private final class AuthSession { + private final class AuthSession implements IBinder.DeathRecipient { // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from // <Biometric>Services before we can start authenticating. Pairs that have been returned // are moved to mModalitiesMatched. @@ -164,10 +173,14 @@ public class BiometricService extends SystemService { // Timestamp when hardware authentication occurred private long mAuthenticatedTimeMs; + // TODO(b/123378871): Remove when moved. + private IBiometricConfirmDeviceCredentialCallback mConfirmDeviceCredentialCallback; + AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, int callingUid, int callingPid, int callingUserId, - int modality, boolean requireConfirmation) { + int modality, boolean requireConfirmation, + IBiometricConfirmDeviceCredentialCallback callback) { mModalitiesWaiting = modalities; mToken = token; mSessionId = sessionId; @@ -180,12 +193,25 @@ public class BiometricService extends SystemService { mCallingUserId = callingUserId; mModality = modality; mRequireConfirmation = requireConfirmation; + mConfirmDeviceCredentialCallback = callback; + + if (isFromConfirmDeviceCredential()) { + try { + token.linkToDeath(this, 0 /* flags */); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to link to death", e); + } + } } boolean isCrypto() { return mSessionId != 0; } + boolean isFromConfirmDeviceCredential() { + return mBundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false); + } + boolean containsCookie(int cookie) { if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) { return true; @@ -195,6 +221,25 @@ public class BiometricService extends SystemService { } return false; } + + // TODO(b/123378871): Remove when moved. + @Override + public void binderDied() { + mHandler.post(() -> { + Slog.e(TAG, "Binder died, killing ConfirmDeviceCredential"); + if (mConfirmDeviceCredentialCallback == null) { + Slog.e(TAG, "Callback is null"); + return; + } + + try { + mConfirmDeviceCredentialCallback.cancel(); + mConfirmDeviceCredentialCallback = null; + } catch (RemoteException e) { + Slog.e(TAG, "Unable to send cancel", e); + } + }); + } } private final class BiometricTaskStackListener extends TaskStackListener { @@ -234,6 +279,14 @@ public class BiometricService extends SystemService { private AuthSession mCurrentAuthSession; private AuthSession mPendingAuthSession; + // TODO(b/123378871): Remove when moved. + // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the + // client (app) receiver. BiometricService internally launches CDCA which invokes + // BiometricService to start authentication (normal path). When auth is success/rejected, + // CDCA will use an aidl method to poke BiometricService - the result will then be forwarded + // to this receiver. + private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver; + private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { @@ -311,7 +364,8 @@ public class BiometricService extends SystemService { (Bundle) args.arg5 /* bundle */, args.argi2 /* callingUid */, args.argi3 /* callingPid */, - args.argi4 /* callingUserId */); + args.argi4 /* callingUserId */, + (IBiometricConfirmDeviceCredentialCallback) args.arg6 /* callback */); args.recycle(); break; } @@ -325,7 +379,28 @@ public class BiometricService extends SystemService { break; } + case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS: { + handleOnConfirmDeviceCredentialSuccess(); + break; + } + + case MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR: { + SomeArgs args = (SomeArgs) msg.obj; + handleOnConfirmDeviceCredentialError( + args.argi1 /* error */, + (String) args.arg1 /* errorMsg */); + args.recycle(); + break; + } + + case MSG_REGISTER_CANCELLATION_CALLBACK: { + handleRegisterCancellationCallback( + (IBiometricConfirmDeviceCredentialCallback) msg.obj /* callback */); + break; + } + default: + Slog.e(TAG, "Unknown message: " + msg); break; } } @@ -533,14 +608,6 @@ public class BiometricService extends SystemService { * cancelAuthentication() can go to the right place. */ private final class BiometricServiceWrapper extends IBiometricService.Stub { - // TODO(b/123378871): Remove when moved. - // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the - // client (app) receiver. BiometricService internally launches CDCA which invokes - // BiometricService to start authentication (normal path). When auth is success/rejected, - // CDCA will use an aidl method to poke BiometricService - the result will then be forwarded - // to this receiver. - private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver; - @Override // Binder call public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) { checkInternalPermission(); @@ -554,12 +621,18 @@ public class BiometricService extends SystemService { @Override // Binder call public void authenticate(IBinder token, long sessionId, int userId, - IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle) + IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, + IBiometricConfirmDeviceCredentialCallback callback) throws RemoteException { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); + // TODO(b/123378871): Remove when moved. + if (callback != null) { + checkInternalPermission(); + } + // In the BiometricServiceBase, check do the AppOps and foreground check. if (userId == callingUserId) { // Check the USE_BIOMETRIC permission here. @@ -576,6 +649,12 @@ public class BiometricService extends SystemService { return; } + final boolean isFromConfirmDeviceCredential = + bundle.getBoolean(BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false); + if (isFromConfirmDeviceCredential) { + checkInternalPermission(); + } + // Check the usage of this in system server. Need to remove this check if it becomes // a public API. final boolean useDefaultTitle = @@ -632,6 +711,7 @@ public class BiometricService extends SystemService { args.argi2 = callingUid; args.argi3 = callingPid; args.argi4 = callingUserId; + args.arg6 = callback; mHandler.obtainMessage(MSG_AUTHENTICATE, args).sendToTarget(); } @@ -639,35 +719,30 @@ public class BiometricService extends SystemService { @Override // Binder call public void onConfirmDeviceCredentialSuccess() { checkInternalPermission(); - mHandler.post(() -> { - if (mConfirmDeviceCredentialReceiver == null) { - Slog.w(TAG, "onCDCASuccess null!"); - return; - } - try { - mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded(); - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException", e); - } - mConfirmDeviceCredentialReceiver = null; - }); + + mHandler.sendEmptyMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS); } @Override // Binder call public void onConfirmDeviceCredentialError(int error, String message) { checkInternalPermission(); - mHandler.post(() -> { - if (mConfirmDeviceCredentialReceiver == null) { - Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message); - return; - } - try { - mConfirmDeviceCredentialReceiver.onError(error, message); - } catch (RemoteException e) { - Slog.e(TAG, "RemoteException", e); - } - mConfirmDeviceCredentialReceiver = null; - }); + + SomeArgs args = SomeArgs.obtain(); + args.argi1 = error; + args.arg1 = message; + mHandler.obtainMessage(MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR, args).sendToTarget(); + } + + @Override // Binder call + public void registerCancellationCallback( + IBiometricConfirmDeviceCredentialCallback callback) { + // TODO(b/123378871): Remove when moved. + // This callback replaces the one stored in the current session. If the session is null + // we can ignore this, since it means ConfirmDeviceCredential was launched by something + // else (not BiometricPrompt) + checkInternalPermission(); + + mHandler.obtainMessage(MSG_REGISTER_CANCELLATION_CALLBACK, callback).sendToTarget(); } @Override // Binder call @@ -1104,6 +1179,52 @@ public class BiometricService extends SystemService { } } + private void handleOnConfirmDeviceCredentialSuccess() { + if (mConfirmDeviceCredentialReceiver == null) { + Slog.w(TAG, "onCDCASuccess null!"); + return; + } + try { + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded(); + if (mCurrentAuthSession != null) { + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + } + mConfirmDeviceCredentialReceiver = null; + } + + private void handleOnConfirmDeviceCredentialError(int error, String message) { + if (mConfirmDeviceCredentialReceiver == null) { + Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message); + return; + } + try { + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + mConfirmDeviceCredentialReceiver.onError(error, message); + if (mCurrentAuthSession != null) { + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException", e); + } + mConfirmDeviceCredentialReceiver = null; + } + + private void handleRegisterCancellationCallback( + IBiometricConfirmDeviceCredentialCallback callback) { + if (mCurrentAuthSession == null) { + Slog.d(TAG, "Current auth session null"); + return; + } + Slog.d(TAG, "Updating cancel callback"); + mCurrentAuthSession.mConfirmDeviceCredentialCallback = callback; + } + private void handleOnError(int cookie, int error, String message) { Slog.d(TAG, "Error: " + error + " cookie: " + cookie); // Errors can either be from the current auth session or the pending auth session. @@ -1114,7 +1235,18 @@ public class BiometricService extends SystemService { // of their intended receivers. try { if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) { - if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) { + + if (mCurrentAuthSession.isFromConfirmDeviceCredential()) { + // If we were invoked by ConfirmDeviceCredential, do not delete the current + // auth session since we still need to respond to cancel signal while + if (DEBUG) Slog.d(TAG, "From CDC, transition to CANCELED_SHOWING_CDC state"); + + // Send the error to ConfirmDeviceCredential so that it goes to Pin/Pattern/Pass + // screen + mCurrentAuthSession.mClientReceiver.onError(error, message); + mCurrentAuthSession.mState = STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC; + mStatusBarService.hideBiometricDialog(); + } else if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) { mStatusBarService.onBiometricError(message); if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { mActivityTaskManager.unregisterTaskStackListener( @@ -1214,9 +1346,16 @@ public class BiometricService extends SystemService { KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow); mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); } - mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); - mCurrentAuthSession.mState = STATE_AUTH_IDLE; - mCurrentAuthSession = null; + + // Do not clean up yet if we are from ConfirmDeviceCredential. We should be in the + // STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC. The session should only be removed when + // ConfirmDeviceCredential is confirmed or canceled. + // TODO(b/123378871): Remove when moved + if (!mCurrentAuthSession.isFromConfirmDeviceCredential()) { + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; + } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } @@ -1235,7 +1374,8 @@ public class BiometricService extends SystemService { mCurrentAuthSession.mCallingUid, mCurrentAuthSession.mCallingPid, mCurrentAuthSession.mCallingUserId, - mCurrentAuthSession.mModality); + mCurrentAuthSession.mModality, + mCurrentAuthSession.mConfirmDeviceCredentialCallback); } private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation, @@ -1290,7 +1430,8 @@ public class BiometricService extends SystemService { private void handleAuthenticate(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, - int callingUid, int callingPid, int callingUserId) { + int callingUid, int callingPid, int callingUserId, + IBiometricConfirmDeviceCredentialCallback callback) { mHandler.post(() -> { final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId); @@ -1328,7 +1469,7 @@ public class BiometricService extends SystemService { // Start preparing for authentication. Authentication starts when // all modalities requested have invoked onReadyForAuthentication. authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle, - callingUid, callingPid, callingUserId, modality); + callingUid, callingPid, callingUserId, modality, callback); }); } @@ -1343,7 +1484,8 @@ public class BiometricService extends SystemService { */ private void authenticateInternal(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle, - int callingUid, int callingPid, int callingUserId, int modality) { + int callingUid, int callingPid, int callingUserId, int modality, + IBiometricConfirmDeviceCredentialCallback callback) { try { boolean requireConfirmation = bundle.getBoolean( BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */); @@ -1363,7 +1505,7 @@ public class BiometricService extends SystemService { authenticators.put(modality, cookie); mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId, receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, - modality, requireConfirmation); + modality, requireConfirmation, callback); mPendingAuthSession.mState = STATE_AUTH_CALLED; // No polymorphism :( if ((modality & TYPE_FINGERPRINT) != 0) { @@ -1390,10 +1532,23 @@ public class BiometricService extends SystemService { return; } - // We need to check the current authenticators state. If we're pending confirm - // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client, - // since we won't be getting an onError from the driver. - if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) { + if (mCurrentAuthSession != null + && mCurrentAuthSession.mState == STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC) { + if (DEBUG) Slog.d(TAG, "Cancel received while ConfirmDeviceCredential showing"); + try { + mCurrentAuthSession.mConfirmDeviceCredentialCallback.cancel(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to cancel ConfirmDeviceCredential", e); + } + + // TODO(b/123378871): Remove when moved. Piggy back on this for now to clean up. + handleOnConfirmDeviceCredentialError(BiometricConstants.BIOMETRIC_ERROR_CANCELED, + getContext().getString(R.string.biometric_error_canceled)); + } else if (mCurrentAuthSession != null + && mCurrentAuthSession.mState != STATE_AUTH_STARTED) { + // We need to check the current authenticators state. If we're pending confirm + // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client, + // since we won't be getting an onError from the driver. try { // Send error to client mCurrentAuthSession.mClientReceiver.onError( @@ -1409,11 +1564,22 @@ public class BiometricService extends SystemService { Slog.e(TAG, "Remote exception", e); } } else { - cancelInternal(token, opPackageName, true /* fromClient */); + boolean fromCDC = false; + if (mCurrentAuthSession != null) { + fromCDC = mCurrentAuthSession.mBundle.getBoolean( + BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL, false); + } + + if (fromCDC) { + if (DEBUG) Slog.d(TAG, "Cancelling from CDC"); + cancelInternal(token, opPackageName, false /* fromClient */); + } else { + cancelInternal(token, opPackageName, true /* fromClient */); + } + } } - void cancelInternal(IBinder token, String opPackageName, boolean fromClient) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 527539d8ce0d..7733d6779e48 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -50,7 +50,7 @@ import java.util.List; import java.util.Set; /** - * CameraServiceProxy is the system_server analog to the camera service running in mediaserver. + * CameraServiceProxy is the system_server analog to the camera service running in cameraserver. * * @hide */ @@ -74,6 +74,7 @@ public class CameraServiceProxy extends SystemService private static final int MSG_SWITCH_USER = 1; private static final int RETRY_DELAY_TIME = 20; //ms + private static final int RETRY_TIMES = 30; // Maximum entries to keep in usage history before dumping out private static final int MAX_USAGE_HISTORY = 100; @@ -171,7 +172,7 @@ public class CameraServiceProxy extends SystemService " camera service UID!"); return; } - notifySwitchWithRetries(30); + notifySwitchWithRetries(RETRY_TIMES); } @Override @@ -242,7 +243,8 @@ public class CameraServiceProxy extends SystemService public void onStartUser(int userHandle) { synchronized(mLock) { if (mEnabledCameraUsers == null) { - // Initialize mediaserver, or update mediaserver if we are recovering from a crash. + // Initialize cameraserver, or update cameraserver if we are recovering + // from a crash. switchUserLocked(userHandle); } } @@ -324,9 +326,9 @@ public class CameraServiceProxy extends SystemService Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle); mLastUser = userHandle; if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) { - // Some user handles have been added or removed, update mediaserver. + // Some user handles have been added or removed, update cameraserver. mEnabledCameraUsers = currentUserHandles; - notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, currentUserHandles); + notifySwitchWithRetriesLocked(RETRY_TIMES); } } @@ -343,12 +345,16 @@ public class CameraServiceProxy extends SystemService private void notifySwitchWithRetries(int retries) { synchronized(mLock) { - if (mEnabledCameraUsers == null) { - return; - } - if (notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, mEnabledCameraUsers)) { - retries = 0; - } + notifySwitchWithRetriesLocked(retries); + } + } + + private void notifySwitchWithRetriesLocked(int retries) { + if (mEnabledCameraUsers == null) { + return; + } + if (notifyCameraserverLocked(ICameraService.EVENT_USER_SWITCHED, mEnabledCameraUsers)) { + retries = 0; } if (retries <= 0) { return; @@ -358,13 +364,13 @@ public class CameraServiceProxy extends SystemService RETRY_DELAY_TIME); } - private boolean notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles) { - // Forward the user switch event to the native camera service running in the mediaserver + private boolean notifyCameraserverLocked(int eventType, Set<Integer> updatedUserHandles) { + // Forward the user switch event to the native camera service running in the cameraserver // process. if (mCameraServiceRaw == null) { IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME); if (cameraServiceBinder == null) { - Slog.w(TAG, "Could not notify mediaserver, camera service not available."); + Slog.w(TAG, "Could not notify cameraserver, camera service not available."); return false; // Camera service not active, cannot evict user clients. } try { @@ -380,7 +386,7 @@ public class CameraServiceProxy extends SystemService try { mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles)); } catch (RemoteException e) { - Slog.w(TAG, "Could not notify mediaserver, remote exception: " + e); + Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e); // Not much we can do if camera service is dead. return false; } 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 9590f8143713..a7d0a5c706b6 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -461,7 +461,9 @@ public final class ColorDisplayService extends SystemService { .setMatrix(mNightDisplayTintController.getColorTemperatureSetting()); } - updateDisplayWhiteBalanceStatus(); + if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) { + updateDisplayWhiteBalanceStatus(); + } final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); dtm.setColorMode(mode, mNightDisplayTintController.getMatrix()); @@ -624,7 +626,11 @@ public final class ColorDisplayService extends SystemService { return false; } return Secure.getIntForUser(getContext().getContentResolver(), - Secure.DISPLAY_WHITE_BALANCE_ENABLED, 0, mCurrentUser) == 1; + Secure.DISPLAY_WHITE_BALANCE_ENABLED, + getContext().getResources() + .getBoolean(R.bool.config_displayWhiteBalanceEnabledDefault) ? 1 + : 0, + mCurrentUser) == 1; } private boolean isDeviceColorManagedInternal() { diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java index 026837f07356..d6aa2ba02f1f 100644 --- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java +++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java @@ -28,6 +28,7 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import java.util.Arrays; @@ -73,15 +74,27 @@ public class DisplayTransformManager { private static final int SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR = 1023; private static final int SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED = 1030; - private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation"; - private static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode"; + @VisibleForTesting + static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation"; + @VisibleForTesting + static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode"; private static final float COLOR_SATURATION_NATURAL = 1.0f; private static final float COLOR_SATURATION_BOOSTED = 1.1f; + /** + * Display color modes defined by DisplayColorSetting in + * frameworks/native/services/surfaceflinger/SurfaceFlinger.h. + */ private static final int DISPLAY_COLOR_MANAGED = 0; private static final int DISPLAY_COLOR_UNMANAGED = 1; private static final int DISPLAY_COLOR_ENHANCED = 2; + /** + * Display color mode range reserved for vendor customizations by the RenderIntent definition in + * hardware/interfaces/graphics/common/1.1/types.hal. + */ + private static final int VENDOR_MODE_RANGE_MIN = 256; // 0x100 + private static final int VENDOR_MODE_RANGE_MAX = 511; // 0x1ff /** * Map of level -> color transformation matrix. @@ -257,7 +270,11 @@ public class DisplayTransformManager { } else if (colorMode == ColorDisplayManager.COLOR_MODE_AUTOMATIC) { applySaturation(COLOR_SATURATION_NATURAL); setDisplayColor(DISPLAY_COLOR_ENHANCED); + } else if (colorMode >= VENDOR_MODE_RANGE_MIN && colorMode <= VENDOR_MODE_RANGE_MAX) { + applySaturation(COLOR_SATURATION_NATURAL); + setDisplayColor(colorMode); } + setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, nightDisplayMatrix); updateConfiguration(); diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java index 647727f795da..0f73f379900b 100644 --- a/services/core/java/com/android/server/gpu/GpuService.java +++ b/services/core/java/com/android/server/gpu/GpuService.java @@ -36,6 +36,7 @@ import android.os.Build; import android.os.Handler; import android.os.SystemProperties; import android.os.UserHandle; +import android.provider.DeviceConfig; import android.provider.Settings; import android.util.Base64; import android.util.Slog; @@ -69,9 +70,11 @@ public class GpuService extends SystemService { private final String mDriverPackageName; private final PackageManager mPackageManager; private final Object mLock = new Object(); + private final Object mDeviceConfigLock = new Object(); private ContentResolver mContentResolver; private long mGameDriverVersionCode; private SettingsObserver mSettingsObserver; + private DeviceConfigListener mDeviceConfigListener; @GuardedBy("mLock") private Blacklists mBlacklists; @@ -101,10 +104,11 @@ public class GpuService extends SystemService { public void onBootPhase(int phase) { if (phase == PHASE_BOOT_COMPLETED) { mContentResolver = mContext.getContentResolver(); - mSettingsObserver = new SettingsObserver(); if (mDriverPackageName == null || mDriverPackageName.isEmpty()) { return; } + mSettingsObserver = new SettingsObserver(); + mDeviceConfigListener = new DeviceConfigListener(); fetchGameDriverPackageProperties(); processBlacklists(); setBlacklist(); @@ -134,6 +138,24 @@ public class GpuService extends SystemService { } } + private final class DeviceConfigListener implements DeviceConfig.OnPropertyChangedListener { + + DeviceConfigListener() { + super(); + DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_GAME_DRIVER, + mContext.getMainExecutor(), this); + } + @Override + public void onPropertyChanged(String namespace, String name, String value) { + synchronized (mDeviceConfigLock) { + if (Settings.Global.GAME_DRIVER_BLACKLISTS.equals(name)) { + parseBlacklists(value != null ? value : ""); + setBlacklist(); + } + } + } + } + private final class PackageReceiver extends BroadcastReceiver { @Override public void onReceive(@NonNull final Context context, @NonNull final Intent intent) { @@ -229,13 +251,17 @@ public class GpuService extends SystemService { } private void processBlacklists() { - // TODO(b/121350991) Switch to DeviceConfig with property listener. - String base64String = - Settings.Global.getString(mContentResolver, Settings.Global.GAME_DRIVER_BLACKLISTS); - if (base64String == null || base64String.isEmpty()) { - return; + String base64String = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_GAME_DRIVER, + Settings.Global.GAME_DRIVER_BLACKLISTS); + if (base64String == null) { + base64String = + Settings.Global.getString(mContentResolver, + Settings.Global.GAME_DRIVER_BLACKLISTS); } + parseBlacklists(base64String != null ? base64String : ""); + } + private void parseBlacklists(String base64String) { synchronized (mLock) { // Reset all blacklists mBlacklists = null; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 6f1929fd464a..e88d62f58507 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -4101,13 +4101,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // ---------------------------------------------------------------------- - boolean setInputMethodEnabledLocked(String id, boolean enabled) { - // Make sure this is a valid input method. - InputMethodInfo imm = mMethodMap.get(id); - if (imm == null) { - throw new IllegalArgumentException("Unknown id: " + mCurMethodId); - } - + /** + * Enable or disable the given IME by updating {@link Settings.Secure#ENABLED_INPUT_METHODS}. + * + * @param id ID of the IME is to be manipulated. It is OK to pass IME ID that is currently not + * recognized by the system. + * @param enabled {@code true} if {@code id} needs to be enabled. + * @return {@code true} if the IME was previously enabled. {@code false} otherwise. + */ + private boolean setInputMethodEnabledLocked(String id, boolean enabled) { List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings .getEnabledInputMethodsAndSubtypeListLocked(); @@ -4587,25 +4589,42 @@ public class InputMethodManagerService extends IInputMethodManager.Stub pw.decreaseIndent(); pw.decreaseIndent(); - pw.println("enable <ID>"); + pw.println("enable [--user <USER_ID>] <ID>"); pw.increaseIndent(); pw.println("allows the given input method ID to be used."); + pw.increaseIndent(); + pw.print("--user <USER_ID>: Specify which user to enable."); + pw.println(" Assumes the current user if not specified."); + pw.decreaseIndent(); pw.decreaseIndent(); - pw.println("disable <ID>"); + pw.println("disable [--user <USER_ID>] <ID>"); pw.increaseIndent(); pw.println("disallows the given input method ID to be used."); + pw.increaseIndent(); + pw.print("--user <USER_ID>: Specify which user to disable."); + pw.println(" Assumes the current user if not specified."); + pw.decreaseIndent(); pw.decreaseIndent(); - pw.println("set <ID>"); + pw.println("set [--user <USER_ID>] <ID>"); pw.increaseIndent(); pw.println("switches to the given input method ID."); + pw.increaseIndent(); + pw.print("--user <USER_ID>: Specify which user to enable."); + pw.println(" Assumes the current user if not specified."); + pw.decreaseIndent(); pw.decreaseIndent(); - pw.println("reset"); + pw.println("reset [--user <USER_ID>]"); pw.increaseIndent(); pw.println("reset currently selected/enabled IMEs to the default ones as if " + "the device is initially booted with the current locale."); + pw.increaseIndent(); + pw.print("--user <USER_ID>: Specify which user to reset."); + pw.println(" Assumes the current user if not specified."); + pw.decreaseIndent(); + pw.decreaseIndent(); pw.decreaseIndent(); @@ -4691,24 +4710,108 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @ShellCommandResult private int handleShellCommandEnableDisableInputMethod( @NonNull ShellCommand shellCommand, boolean enabled) { - final String id = shellCommand.getNextArgRequired(); - final boolean previouslyEnabled; + final String imeId = shellCommand.getNextArgRequired(); + final PrintWriter out = shellCommand.getOutPrintWriter(); + final PrintWriter error = shellCommand.getErrPrintWriter(); + final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand); synchronized (mMethodMap) { - if (!userHasDebugPriv(mSettings.getCurrentUserId(), shellCommand)) { - return ShellCommandResult.SUCCESS; + final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved, + mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter()); + for (int userId : userIds) { + if (!userHasDebugPriv(userId, shellCommand)) { + continue; + } + handleShellCommandEnableDisableInputMethodInternalLocked(userId, imeId, enabled, + out, error); } - previouslyEnabled = setInputMethodEnabledLocked(id, enabled); } - final PrintWriter pr = shellCommand.getOutPrintWriter(); - pr.print("Input method "); - pr.print(id); - pr.print(": "); - pr.print((enabled == previouslyEnabled) ? "already " : "now "); - pr.println(enabled ? "enabled" : "disabled"); return ShellCommandResult.SUCCESS; } /** + * A special helper method for commands that only have {@code -u} and {@code --user} options. + * + * <p>You cannot use this helper method if the command has other options.</p> + * + * @param shellCommand {@link ShellCommand} from which options should be obtained. + * @return User ID to be resolved. {@link UserHandle#CURRENT} if not specified. + */ + @BinderThread + @UserIdInt + private static int handleOptionsForCommandsThatOnlyHaveUserOption(ShellCommand shellCommand) { + while (true) { + final String nextOption = shellCommand.getNextOption(); + if (nextOption == null) { + break; + } + switch (nextOption) { + case "-u": + case "--user": + return UserHandle.parseUserArg(shellCommand.getNextArgRequired()); + } + } + return UserHandle.USER_CURRENT; + } + + @BinderThread + private void handleShellCommandEnableDisableInputMethodInternalLocked( + @UserIdInt int userId, String imeId, boolean enabled, PrintWriter out, + PrintWriter error) { + boolean failedToEnableUnknownIme = false; + boolean previouslyEnabled = false; + if (userId == mSettings.getCurrentUserId()) { + if (enabled && !mMethodMap.containsKey(imeId)) { + failedToEnableUnknownIme = true; + } else { + previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled); + } + } else { + final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>(); + final ArrayList<InputMethodInfo> methodList = new ArrayList<>(); + final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap = + new ArrayMap<>(); + AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); + queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, + methodMap, methodList); + final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(), + mContext.getContentResolver(), methodMap, userId, false); + if (enabled) { + if (!methodMap.containsKey(imeId)) { + failedToEnableUnknownIme = true; + } else { + for (InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) { + if (TextUtils.equals(imi.getId(), imeId)) { + previouslyEnabled = true; + break; + } + } + if (!previouslyEnabled) { + settings.appendAndPutEnabledInputMethodLocked(imeId, false); + } + } + } else { + previouslyEnabled = + settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked( + new StringBuilder(), + settings.getEnabledInputMethodsAndSubtypeListLocked(), imeId); + } + } + if (failedToEnableUnknownIme) { + error.print("Unknown input method "); + error.print(imeId); + error.println(" cannot be enabled for user #" + userId); + } else { + out.print("Input method "); + out.print(imeId); + out.print(": "); + out.print((enabled == previouslyEnabled) ? "already " : "now "); + out.print(enabled ? "enabled" : "disabled"); + out.print(" for user #"); + out.println(userId); + } + } + + /** * Handles {@code adb shell ime set}. * @param shellCommand {@link ShellCommand} object that is handling this command. * @return Exit code of the command. @@ -4716,17 +4819,55 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @ShellCommandResult private int handleShellCommandSetInputMethod(@NonNull ShellCommand shellCommand) { - final String id = shellCommand.getNextArgRequired(); + final String imeId = shellCommand.getNextArgRequired(); + final PrintWriter out = shellCommand.getOutPrintWriter(); + final PrintWriter error = shellCommand.getErrPrintWriter(); + final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand); synchronized (mMethodMap) { - if (!userHasDebugPriv(mSettings.getCurrentUserId(), shellCommand)) { - return ShellCommandResult.SUCCESS; + final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved, + mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter()); + for (int userId : userIds) { + if (!userHasDebugPriv(userId, shellCommand)) { + continue; + } + boolean failedToSelectUnknownIme = false; + if (userId == mSettings.getCurrentUserId()) { + if (mMethodMap.containsKey(imeId)) { + setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID); + } else { + failedToSelectUnknownIme = true; + } + } else { + final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>(); + final ArrayList<InputMethodInfo> methodList = new ArrayList<>(); + final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap = + new ArrayMap<>(); + AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); + queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, + methodMap, methodList); + final InputMethodSettings settings = new InputMethodSettings( + mContext.getResources(), mContext.getContentResolver(), methodMap, + userId, false); + if (methodMap.containsKey(imeId)) { + settings.putSelectedInputMethod(imeId); + settings.putSelectedSubtype(NOT_A_SUBTYPE_ID); + } else { + failedToSelectUnknownIme = true; + } + } + if (failedToSelectUnknownIme) { + error.print("Unknown input method "); + error.print(imeId); + error.print(" cannot be selected for user #"); + error.println(userId); + } else { + out.print("Input method "); + out.print(imeId); + out.print(" selected for user #"); + error.println(userId); + } } - setInputMethodLocked(id, NOT_A_SUBTYPE_ID); } - final PrintWriter pr = shellCommand.getOutPrintWriter(); - pr.print("Input method "); - pr.print(id); - pr.println(" selected"); return ShellCommandResult.SUCCESS; } @@ -4738,45 +4879,67 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @ShellCommandResult private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) { + final PrintWriter out = shellCommand.getOutPrintWriter(); + final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand); synchronized (mMethodMap) { - if (!userHasDebugPriv(mSettings.getCurrentUserId(), shellCommand)) { - return ShellCommandResult.SUCCESS; - } - final String nextIme; - final List<InputMethodInfo> nextEnabledImes; - hideCurrentInputLocked(0, null); - unbindCurrentMethodLocked(); - // Reset the current IME - resetSelectedInputMethodAndSubtypeLocked(null); - // Also reset the settings of the current IME - mSettings.putSelectedInputMethod(null); - // Disable all enabled IMEs. - mSettings.getEnabledInputMethodListLocked().forEach( - imi -> setInputMethodEnabledLocked(imi.getId(), false)); - // Re-enable with default enabled IMEs. - InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList).forEach( - imi -> setInputMethodEnabledLocked(imi.getId(), true)); - updateInputMethodsFromSettingsLocked(true /* enabledMayChange */); - InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager, - mSettings.getEnabledInputMethodListLocked(), - mSettings.getCurrentUserId(), - mContext.getBasePackageName()); - nextIme = mSettings.getSelectedInputMethod(); - nextEnabledImes = mSettings.getEnabledInputMethodListLocked(); - final PrintWriter pr = shellCommand.getOutPrintWriter(); - pr.println("Reset current and enabled IMEs"); - pr.println("Newly selected IME:"); - pr.print(" "); pr.println(nextIme); - pr.println("Newly enabled IMEs:"); - { - final int N = nextEnabledImes.size(); - for (int i = 0; i < N; ++i) { - pr.print(" "); - pr.println(nextEnabledImes.get(i).getId()); + final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved, + mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter()); + for (int userId : userIds) { + if (!userHasDebugPriv(userId, shellCommand)) { + continue; + } + final String nextIme; + final List<InputMethodInfo> nextEnabledImes; + if (userId == mSettings.getCurrentUserId()) { + hideCurrentInputLocked(0, null); + unbindCurrentMethodLocked(); + // Reset the current IME + resetSelectedInputMethodAndSubtypeLocked(null); + // Also reset the settings of the current IME + mSettings.putSelectedInputMethod(null); + // Disable all enabled IMEs. + mSettings.getEnabledInputMethodListLocked().forEach( + imi -> setInputMethodEnabledLocked(imi.getId(), false)); + // Re-enable with default enabled IMEs. + InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList).forEach( + imi -> setInputMethodEnabledLocked(imi.getId(), true)); + updateInputMethodsFromSettingsLocked(true /* enabledMayChange */); + InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager, + mSettings.getEnabledInputMethodListLocked(), + mSettings.getCurrentUserId(), + mContext.getBasePackageName()); + nextIme = mSettings.getSelectedInputMethod(); + nextEnabledImes = mSettings.getEnabledInputMethodListLocked(); + } else { + final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>(); + final ArrayList<InputMethodInfo> methodList = new ArrayList<>(); + final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap = + new ArrayMap<>(); + AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); + queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, + methodMap, methodList); + final InputMethodSettings settings = new InputMethodSettings( + mContext.getResources(), mContext.getContentResolver(), methodMap, + userId, false); + + nextEnabledImes = InputMethodUtils.getDefaultEnabledImes(mContext, methodList); + nextIme = InputMethodUtils.getMostApplicableDefaultIME(nextEnabledImes).getId(); + + // Reset enabled IMEs. + settings.putEnabledInputMethodsStr(""); + nextEnabledImes.forEach(imi -> settings.appendAndPutEnabledInputMethodLocked( + imi.getId(), false)); + + // Reset selected IME. + settings.putSelectedInputMethod(nextIme); + settings.putSelectedSubtype(NOT_A_SUBTYPE_ID); } + out.println("Reset current and enabled IMEs for user #" + userId); + out.println(" Selected: " + nextIme); + nextEnabledImes.forEach(ime -> out.println(" Enabled: " + ime.getId())); } - return ShellCommandResult.SUCCESS; } + return ShellCommandResult.SUCCESS; } /** diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index 4349b4aa3603..b5e19aeed0c5 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -1003,7 +1003,7 @@ final class InputMethodUtils { return res; } - private void putEnabledInputMethodsStr(@Nullable String str) { + void putEnabledInputMethodsStr(@Nullable String str) { if (DEBUG) { Slog.d(TAG, "putEnabledInputMethodStr: " + str); } diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java index 2948aafbb931..2e72fbd95931 100644 --- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java +++ b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java @@ -457,15 +457,13 @@ class GnssNetworkConnectivityHandler { } mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING; - // The NetworkRequest.Builder class is not used to construct the network request because - // the ConnectivityService requires the network request to be constructed in this way - // to extend support for requestRouteToHostAddress() method for pre-gnss@2.0 devices. - NetworkCapabilities networkCapabilities = new NetworkCapabilities(); - networkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - networkCapabilities.addCapability(getNetworkCapability(mAGpsType)); - NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, - getLegacyDataConnectionType(agpsType), ConnectivityManager.REQUEST_ID_UNSET, - NetworkRequest.Type.REQUEST); + // The transport type must be set to NetworkCapabilities.TRANSPORT_CELLULAR for the + // deprecated requestRouteToHostAddress() method in ConnectivityService to work for + // pre-gnss@2.0 devices. + NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder(); + networkRequestBuilder.addCapability(getNetworkCapability(mAGpsType)); + networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + NetworkRequest networkRequest = networkRequestBuilder.build(); mConnMgr.requestNetwork( networkRequest, mSuplConnectivityCallback, @@ -487,19 +485,6 @@ class GnssNetworkConnectivityHandler { } } - private int getLegacyDataConnectionType(int agpsType) { - switch (agpsType) { - case AGPS_TYPE_C2K: - case AGPS_TYPE_SUPL: - return ConnectivityManager.TYPE_MOBILE_SUPL; - case AGPS_TYPE_EIMS: - return ConnectivityManager.TYPE_MOBILE_EMERGENCY; - case AGPS_TYPE_IMS: - return ConnectivityManager.TYPE_MOBILE_IMS; - default: - throw new IllegalArgumentException("agpsType: " + agpsType); - } - } private void handleReleaseSuplConnection(int agpsDataConnStatus) { if (DEBUG) { String message = String.format( diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java index 0cabf1d35206..de36deafa4d7 100644 --- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java @@ -1489,8 +1489,10 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { final long token = Binder.clearCallingIdentity(); if (DEBUG_KEY_EVENT) { - Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + ", pid=" + pid + ", uid=" - + uid + ", asSystem=" + asSystemService + ", event=" + keyEvent); + Log.d(TAG, "dispatchVolumeKeyEvent, pkg=" + packageName + + ", opPkg=" + opPackageName + ", pid=" + pid + ", uid=" + uid + + ", asSystem=" + asSystemService + ", event=" + keyEvent + + ", stream=" + stream + ", musicOnly=" + musicOnly); } try { diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 15599111f6e5..f34ace55a72e 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -25,6 +25,7 @@ import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.isNetworkTypeMobile; +import static android.net.NetworkStack.checkNetworkStackPermission; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.INTERFACES_ALL; @@ -866,7 +867,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { VpnInfo[] vpnArray, NetworkState[] networkStates, String activeIface) { - mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); + checkNetworkStackPermission(mContext); assertBandwidthControlEnabled(); final long token = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 0488d3a822ad..4a6eb276bd02 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -79,7 +79,6 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.function.Predicate; /** * Manages the lifecycle of application-provided services bound by system server. @@ -1163,6 +1162,7 @@ abstract public class ManagedServices { @Override public void onNullBinding(ComponentName name) { Slog.v(TAG, "onNullBinding() called with: name = [" + name + "]"); + mServicesBound.remove(servicesBindingTag); } }; if (!mContext.bindServiceAsUser(intent, @@ -1180,6 +1180,11 @@ abstract public class ManagedServices { } } + boolean isBound(ComponentName cn, int userId) { + final Pair<ComponentName, Integer> servicesBindingTag = Pair.create(cn, userId); + return mServicesBound.contains(servicesBindingTag); + } + /** * Remove a service for the given user by ComponentName */ diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f2e56b589bd5..7f1b25ca3ca3 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -29,6 +29,7 @@ import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_ import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; +import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECTS_UNSET; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; @@ -1515,6 +1516,11 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting + void setZenHelper(ZenModeHelper zenHelper) { + mZenModeHelper = zenHelper; + } + + @VisibleForTesting void setIsAutomotive(boolean isAutomotive) { mIsAutomotive = isAutomotive; } @@ -2855,7 +2861,7 @@ public class NotificationManagerService extends SystemService { } @Override - public List<String> getAllowedAssistantCapabilities(String pkg) { + public List<String> getAllowedAssistantAdjustments(String pkg) { checkCallerIsSystemOrSameApp(pkg); if (!isCallerSystemOrPhone() @@ -2863,20 +2869,20 @@ public class NotificationManagerService extends SystemService { throw new SecurityException("Not currently an assistant"); } - return mAssistants.getAllowedAssistantCapabilities(); + return mAssistants.getAllowedAssistantAdjustments(); } @Override - public void allowAssistantCapability(String adjustmentType) { - checkCallerIsSystemOrShell(); + public void allowAssistantAdjustment(String adjustmentType) { + checkCallerIsSystemOrSystemUiOrShell(); mAssistants.allowAdjustmentType(adjustmentType); handleSavePolicyFile(); } @Override - public void disallowAssistantCapability(String adjustmentType) { - checkCallerIsSystemOrShell(); + public void disallowAssistantAdjustment(String adjustmentType) { + checkCallerIsSystemOrSystemUiOrShell(); mAssistants.disallowAdjustmentType(adjustmentType); handleSavePolicyFile(); @@ -3419,8 +3425,7 @@ public class NotificationManagerService extends SystemService { } @Override - public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) - throws RemoteException { + public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) { Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null"); Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null"); if (automaticZenRule.getOwner() == null @@ -3429,6 +3434,11 @@ public class NotificationManagerService extends SystemService { "Rule must have a conditionproviderservice and/or configuration activity"); } Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null"); + if (automaticZenRule.getZenPolicy() != null + && automaticZenRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) { + throw new IllegalArgumentException("ZenPolicy is only applicable to " + + "INTERRUPTION_FILTER_PRIORITY filters"); + } enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule"); return mZenModeHelper.addAutomaticZenRule(automaticZenRule, @@ -3558,7 +3568,7 @@ public class NotificationManagerService extends SystemService { return; } boolean accessAllowed = false; - String[] packages = getContext().getPackageManager().getPackagesForUid(uid); + String[] packages = mPackageManagerClient.getPackagesForUid(uid); final int packageCount = packages.length; for (int i = 0; i < packageCount; i++) { if (mConditionProviders.isPackageOrComponentAllowed( @@ -3806,7 +3816,7 @@ public class NotificationManagerService extends SystemService { @Override public ComponentName getAllowedNotificationAssistantForUser(int userId) { - checkCallerIsSystem(); + checkCallerIsSystemOrSystemUiOrShell(); List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId); if (allowedComponents.size() > 1) { throw new IllegalStateException( @@ -3889,7 +3899,7 @@ public class NotificationManagerService extends SystemService { @Override public void setNotificationAssistantAccessGrantedForUser(ComponentName assistant, int userId, boolean granted) { - checkCallerIsSystemOrShell(); + checkCallerIsSystemOrSystemUiOrShell(); mAssistants.setUserSet(userId, true); final long identity = Binder.clearCallingIdentity(); try { @@ -3924,10 +3934,6 @@ public class NotificationManagerService extends SystemService { } } if (!foundEnqueued) { - // adjustment arrived too late to apply to enqueued; apply to posted - // However, since the notification is now posted and may have alerted, - // ignore any importance related adjustments - adjustment.getSignals().remove(Adjustment.KEY_IMPORTANCE); applyAdjustmentFromAssistant(token, adjustment); } } @@ -4118,7 +4124,7 @@ public class NotificationManagerService extends SystemService { } return; } - if (mAllowedManagedServicePackages.test(assistant.getPackageName(), userId, + if (!granted || mAllowedManagedServicePackages.test(assistant.getPackageName(), userId, mAssistants.getRequiredPermission())) { mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(), userId, false, granted); @@ -6990,6 +6996,16 @@ public class NotificationManagerService extends SystemService { throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid()); } + private void checkCallerIsSystemOrSystemUiOrShell() { + if (Binder.getCallingUid() == Process.SHELL_UID) { + return; + } + if (isCallerSystemOrPhone()) { + return; + } + getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE, null); + } + private void checkCallerIsSystemOrSameApp(String pkg) { if (isCallerSystemOrPhone()) { return; @@ -7297,7 +7313,7 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mLock") private ArrayMap<Integer, Boolean> mUserSetMap = new ArrayMap<>(); - private List<String> mAllowedAdjustments = new ArrayList<>(); + private Set<String> mAllowedAdjustments = new ArraySet<>(); public NotificationAssistants(Context context, Object lock, UserProfiles up, IPackageManager pm) { @@ -7385,15 +7401,21 @@ public class NotificationManagerService extends SystemService { synchronized (mLock) { mAllowedAdjustments.add(type); } + for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { + mHandler.post(() -> notifyCapabilitiesChanged(info)); + } } protected void disallowAdjustmentType(String type) { synchronized (mLock) { mAllowedAdjustments.remove(type); } + for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { + mHandler.post(() -> notifyCapabilitiesChanged(info)); + } } - protected List<String> getAllowedAssistantCapabilities() { + protected List<String> getAllowedAssistantAdjustments() { synchronized (mLock) { List<String> types = new ArrayList<>(); types.addAll(mAllowedAdjustments); @@ -7450,6 +7472,15 @@ public class NotificationManagerService extends SystemService { setUserSet(userId, userSet); } + private void notifyCapabilitiesChanged(final ManagedServiceInfo info) { + final INotificationListener assistant = (INotificationListener) info.service; + try { + assistant.onAllowedAdjustmentsChanged(); + } catch (RemoteException ex) { + Slog.e(TAG, "unable to notify assistant (capabilities): " + assistant, ex); + } + } + private void notifySeen(final ManagedServiceInfo info, final ArrayList<String> keys) { final INotificationListener assistant = (INotificationListener) info.service; diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index ea7bf2d23495..7e74cc2368cd 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -389,8 +389,8 @@ public class ZenModeHelper { if (mConfig == null) return; newConfig = mConfig.copy(); + setAutomaticZenRuleStateLocked(newConfig, newConfig.automaticRules.get(id), condition); } - setAutomaticZenRuleState(newConfig, newConfig.automaticRules.get(id), condition); } public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition) { @@ -398,14 +398,15 @@ public class ZenModeHelper { synchronized (mConfig) { if (mConfig == null) return; newConfig = mConfig.copy(); - } - setAutomaticZenRuleState(newConfig, - findMatchingRule(newConfig, ruleDefinition, condition), - condition); + setAutomaticZenRuleStateLocked(newConfig, + findMatchingRule(newConfig, ruleDefinition, condition), + condition); + } } - private void setAutomaticZenRuleState(ZenModeConfig config, ZenRule rule, Condition condition) { + private void setAutomaticZenRuleStateLocked(ZenModeConfig config, ZenRule rule, + Condition condition) { if (rule == null) return; rule.condition = condition; diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 944aef5ab223..21b6f12b6f8b 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -324,6 +324,8 @@ class ApexManager { ipw.println("State: ROLLBACK IN PROGRESS"); } else if (si.isRolledBack) { ipw.println("State: ROLLED BACK"); + } else if (si.isRollbackFailed) { + ipw.println("State: ROLLBACK FAILED"); } ipw.decreaseIndent(); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 1ce9d827d80f..d3aa746f7596 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2967,7 +2967,7 @@ public class PackageManagerService extends IPackageManager.Stub // Uncompress and install any stubbed system applications. // This must be done last to ensure all stubs are replaced or disabled. - decompressSystemApplications(stubSystemApps, scanFlags); + installSystemStubPackages(stubSystemApps, scanFlags); final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get() - cachedSystemApps; @@ -3278,49 +3278,37 @@ public class PackageManagerService extends IPackageManager.Stub * <p>In order to forcefully attempt an installation of a full application, go to app * settings and enable the application. */ - private void decompressSystemApplications(@NonNull List<String> stubSystemApps, int scanFlags) { - for (int i = stubSystemApps.size() - 1; i >= 0; --i) { - final String pkgName = stubSystemApps.get(i); + private void installSystemStubPackages(@NonNull List<String> systemStubPackageNames, + @ScanFlags int scanFlags) { + for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { + final String packageName = systemStubPackageNames.get(i); // skip if the system package is already disabled - if (mSettings.isDisabledSystemPackageLPr(pkgName)) { - stubSystemApps.remove(i); + if (mSettings.isDisabledSystemPackageLPr(packageName)) { + systemStubPackageNames.remove(i); continue; } // skip if the package isn't installed (?!); this should never happen - final PackageParser.Package pkg = mPackages.get(pkgName); + final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { - stubSystemApps.remove(i); + systemStubPackageNames.remove(i); continue; } // skip if the package has been disabled by the user - final PackageSetting ps = mSettings.mPackages.get(pkgName); + final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { final int enabledState = ps.getEnabled(UserHandle.USER_SYSTEM); if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { - stubSystemApps.remove(i); + systemStubPackageNames.remove(i); continue; } } - if (DEBUG_COMPRESSION) { - Slog.i(TAG, "Uncompressing system stub; pkg: " + pkgName); - } - - // uncompress the binary to its eventual destination on /data - final File scanFile = decompressPackage(pkg); - if (scanFile == null) { - continue; - } - // install the package to replace the stub on /system try { - mSettings.disableSystemPackageLPw(pkgName, true /*replaced*/); - removePackageLI(pkg, true /*chatty*/); - scanPackageTracedLI(scanFile, 0 /*reparseFlags*/, scanFlags, 0, null); + installStubPackageLI(pkg, 0, scanFlags); ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, UserHandle.USER_SYSTEM, "android"); - stubSystemApps.remove(i); - continue; + systemStubPackageNames.remove(i); } catch (PackageManagerException e) { Slog.e(TAG, "Failed to parse uncompressed system package: " + e.getMessage()); } @@ -3329,8 +3317,8 @@ public class PackageManagerService extends IPackageManager.Stub } // disable any stub still left; these failed to install the full application - for (int i = stubSystemApps.size() - 1; i >= 0; --i) { - final String pkgName = stubSystemApps.get(i); + for (int i = systemStubPackageNames.size() - 1; i >= 0; --i) { + final String pkgName = systemStubPackageNames.get(i); final PackageSetting ps = mSettings.mPackages.get(pkgName); ps.setEnabled(PackageManager.COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); @@ -3339,20 +3327,107 @@ public class PackageManagerService extends IPackageManager.Stub } /** + * Extract, install and enable a stub package. + * <p>If the compressed file can not be extracted / installed for any reason, the stub + * APK will be installed and the package will be disabled. To recover from this situation, + * the user will need to go into system settings and re-enable the package. + */ + private boolean enableCompressedPackage(PackageParser.Package stubPkg) { + final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY + | PackageParser.PARSE_ENFORCE_CODE; + synchronized (mInstallLock) { + final PackageParser.Package pkg; + try (PackageFreezer freezer = + freezePackage(stubPkg.packageName, "setEnabledSetting")) { + pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/); + synchronized (mPackages) { + prepareAppDataAfterInstallLIF(pkg); + try { + updateSharedLibrariesLocked(pkg, null, mPackages); + } catch (PackageManagerException e) { + Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e); + } + mPermissionManager.updatePermissions( + pkg.packageName, pkg, true, mPackages.values(), + mPermissionCallback); + mSettings.writeLPr(); + } + } catch (PackageManagerException e) { + // Whoops! Something went very wrong; roll back to the stub and disable the package + try (PackageFreezer freezer = + freezePackage(stubPkg.packageName, "setEnabledSetting")) { + synchronized (mPackages) { + // NOTE: Ensure the system package is enabled; even for a compressed stub. + // If we don't, installing the system package fails during scan + enableSystemPackageLPw(stubPkg); + } + installPackageFromSystemLIF(stubPkg.codePath, + null /*allUserHandles*/, null /*origUserHandles*/, + null /*origPermissionsState*/, true /*writeSettings*/); + } catch (PackageManagerException pme) { + // Serious WTF; we have to be able to install the stub + Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.packageName, pme); + } finally { + // Disable the package; the stub by itself is not runnable + synchronized (mPackages) { + final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName); + if (stubPs != null) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, + UserHandle.USER_SYSTEM, "android"); + } + mSettings.writeLPr(); + } + } + return false; + } + clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE + | FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); + mDexManager.notifyPackageUpdated(pkg.packageName, + pkg.baseCodePath, pkg.splitCodePaths); + } + return true; + } + + private PackageParser.Package installStubPackageLI(PackageParser.Package stubPkg, + @ParseFlags int parseFlags, @ScanFlags int scanFlags) + throws PackageManagerException { + if (DEBUG_COMPRESSION) { + Slog.i(TAG, "Uncompressing system stub; pkg: " + stubPkg.packageName); + } + // uncompress the binary to its eventual destination on /data + final File scanFile = decompressPackage(stubPkg.packageName, stubPkg.codePath); + if (scanFile == null) { + throw new PackageManagerException("Unable to decompress stub at " + stubPkg.codePath); + } + synchronized (mPackages) { + mSettings.disableSystemPackageLPw(stubPkg.packageName, true /*replaced*/); + } + removePackageLI(stubPkg, true /*chatty*/); + try { + return scanPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null); + } catch (PackageManagerException e) { + Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.packageName, e); + // Remove the failed install + removeCodePathLI(scanFile); + throw e; + } + } + + /** * Decompresses the given package on the system image onto * the /data partition. * @return The directory the package was decompressed into. Otherwise, {@code null}. */ - private File decompressPackage(PackageParser.Package pkg) { - final File[] compressedFiles = getCompressedFiles(pkg.codePath); + private File decompressPackage(String packageName, String codePath) { + final File[] compressedFiles = getCompressedFiles(codePath); if (compressedFiles == null || compressedFiles.length == 0) { if (DEBUG_COMPRESSION) { - Slog.i(TAG, "No files to decompress: " + pkg.baseCodePath); + Slog.i(TAG, "No files to decompress: " + codePath); } return null; } final File dstCodePath = - getNextCodePath(Environment.getDataAppDirectory(null), pkg.packageName); + getNextCodePath(Environment.getDataAppDirectory(null), packageName); int ret = PackageManager.INSTALL_SUCCEEDED; try { Os.mkdir(dstCodePath.getAbsolutePath(), 0755); @@ -3365,14 +3440,14 @@ public class PackageManagerService extends IPackageManager.Stub ret = decompressFile(srcFile, dstFile); if (ret != PackageManager.INSTALL_SUCCEEDED) { logCriticalInfo(Log.ERROR, "Failed to decompress" - + "; pkg: " + pkg.packageName + + "; pkg: " + packageName + ", file: " + dstFileName); break; } } } catch (ErrnoException e) { logCriticalInfo(Log.ERROR, "Failed to decompress" - + "; pkg: " + pkg.packageName + + "; pkg: " + packageName + ", err: " + e.errno); } if (ret == PackageManager.INSTALL_SUCCEEDED) { @@ -3384,7 +3459,7 @@ public class PackageManagerService extends IPackageManager.Stub null /*abiOverride*/); } catch (IOException e) { logCriticalInfo(Log.ERROR, "Failed to extract native libraries" - + "; pkg: " + pkg.packageName); + + "; pkg: " + packageName); ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; } finally { IoUtils.closeQuietly(handle); @@ -18216,12 +18291,15 @@ public class PackageManagerService extends IPackageManager.Stub return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER; } - PackageSetting uninstalledPs; - PackageParser.Package pkg; + final PackageSetting uninstalledPs; + final PackageSetting disabledSystemPs; + final PackageParser.Package pkg; // for the uninstall-updates case and restricted profiles, remember the per- // user handle installed state int[] allUsers; + /** enabled state of the uninstalled application */ + final int origEnabledState; synchronized (mPackages) { uninstalledPs = mSettings.mPackages.get(packageName); if (uninstalledPs == null) { @@ -18236,6 +18314,11 @@ public class PackageManagerService extends IPackageManager.Stub return PackageManager.DELETE_FAILED_INTERNAL_ERROR; } + disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName); + // Save the enabled state before we delete the package. When deleting a stub + // application we always set the enabled state to 'disabled'. + origEnabledState = uninstalledPs == null + ? COMPONENT_ENABLED_STATE_DEFAULT : uninstalledPs.getEnabled(userId); // Static shared libs can be declared by any package, so let us not // allow removing a package if it provides a lib others depend on. pkg = mPackages.get(packageName); @@ -18304,10 +18387,30 @@ public class PackageManagerService extends IPackageManager.Stub Runtime.getRuntime().gc(); // Delete the resources here after sending the broadcast to let // other processes clean up before deleting resources. - if (info.args != null) { - synchronized (mInstallLock) { + synchronized (mInstallLock) { + if (info.args != null) { info.args.doPostDeleteLI(true); } + final PackageParser.Package stubPkg = + (disabledSystemPs == null) ? null : disabledSystemPs.pkg; + if (stubPkg != null && stubPkg.isStub) { + synchronized (mPackages) { + // restore the enabled state of the stub; the state is overwritten when + // the stub is uninstalled + final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName); + if (stubPs != null) { + stubPs.setEnabled(origEnabledState, userId, "android"); + } + } + if (origEnabledState == COMPONENT_ENABLED_STATE_DEFAULT + || origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) { + if (DEBUG_COMPRESSION) { + Slog.i(TAG, "Enabling system stub after removal; pkg: " + + stubPkg.packageName); + } + enableCompressedPackage(stubPkg); + } + } } return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR; @@ -18713,7 +18816,14 @@ public class PackageManagerService extends IPackageManager.Stub throw new SystemDeleteException(e); } finally { if (disabledPs.pkg.isStub) { - mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/); + // We've re-installed the stub; make sure it's disabled here. If package was + // originally enabled, we'll install the compressed version of the application + // and re-enable it afterward. + final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.packageName); + if (stubPs != null) { + stubPs.setEnabled( + COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); + } } } } @@ -20798,102 +20908,9 @@ public class PackageManagerService extends IPackageManager.Stub if (isSystemStub && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) { - final File codePath = decompressPackage(deletedPkg); - if (codePath == null) { - Slog.e(TAG, "couldn't decompress pkg: " + pkgSetting.name); + if (!enableCompressedPackage(deletedPkg)) { return; } - // TODO remove direct parsing of the package object during internal cleanup - // of scan package - // We need to call parse directly here for no other reason than we need - // the new package in order to disable the old one [we use the information - // for some internal optimization to optionally create a new package setting - // object on replace]. However, we can't get the package from the scan - // because the scan modifies live structures and we need to remove the - // old [system] package from the system before a scan can be attempted. - // Once scan is indempotent we can remove this parse and use the package - // object we scanned, prior to adding it to package settings. - final PackageParser pp = new PackageParser(); - pp.setSeparateProcesses(mSeparateProcesses); - pp.setDisplayMetrics(mMetrics); - pp.setCallback(mPackageParserCallback); - final PackageParser.Package tmpPkg; - try { - final @ParseFlags int parseFlags = mDefParseFlags - | PackageParser.PARSE_MUST_BE_APK - | PackageParser.PARSE_IS_SYSTEM_DIR; - tmpPkg = pp.parsePackage(codePath, parseFlags); - } catch (PackageParserException e) { - Slog.w(TAG, "Failed to parse compressed system package:" + pkgSetting.name, e); - return; - } - synchronized (mInstallLock) { - // Disable the stub and remove any package entries - removePackageLI(deletedPkg, true); - synchronized (mPackages) { - disableSystemPackageLPw(deletedPkg, tmpPkg); - } - final PackageParser.Package pkg; - try (PackageFreezer freezer = - freezePackage(deletedPkg.packageName, "setEnabledSetting")) { - final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY - | PackageParser.PARSE_ENFORCE_CODE; - pkg = scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/, - 0 /*currentTime*/, null /*user*/); - prepareAppDataAfterInstallLIF(pkg); - synchronized (mPackages) { - try { - updateSharedLibrariesLocked(pkg, null, mPackages); - } catch (PackageManagerException e) { - Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e); - } - mPermissionManager.updatePermissions( - pkg.packageName, pkg, true, mPackages.values(), - mPermissionCallback); - mSettings.writeLPr(); - } - } catch (PackageManagerException e) { - // Whoops! Something went wrong; try to roll back to the stub - Slog.w(TAG, "Failed to install compressed system package:" - + pkgSetting.name, e); - // Remove the failed install - removeCodePathLI(codePath); - - // Install the system package - try (PackageFreezer freezer = - freezePackage(deletedPkg.packageName, "setEnabledSetting")) { - synchronized (mPackages) { - // NOTE: The system package always needs to be enabled; even - // if it's for a compressed stub. If we don't, installing the - // system package fails during scan [scanning checks the disabled - // packages]. We will reverse this later, after we've "installed" - // the stub. - // This leaves us in a fragile state; the stub should never be - // enabled, so, cross your fingers and hope nothing goes wrong - // until we can disable the package later. - enableSystemPackageLPw(deletedPkg); - } - installPackageFromSystemLIF(deletedPkg.codePath, - /*isPrivileged*/ null /*allUserHandles*/, - null /*origUserHandles*/, null /*origPermissionsState*/, - true /*writeSettings*/); - } catch (PackageManagerException pme) { - Slog.w(TAG, "Failed to restore system package:" - + deletedPkg.packageName, pme); - } finally { - synchronized (mPackages) { - mSettings.disableSystemPackageLPw( - deletedPkg.packageName, true /*replaced*/); - mSettings.writeLPr(); - } - } - return; - } - clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE - | FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY); - mDexManager.notifyPackageUpdated(pkg.packageName, - pkg.baseCodePath, pkg.splitCodePaths); - } } if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { @@ -24816,11 +24833,9 @@ public class PackageManagerService extends IPackageManager.Stub } if (mExternalSourcesPolicy != null) { int isTrusted = mExternalSourcesPolicy.getPackageTrustedToInstallApps(packageName, uid); - if (isTrusted != PackageManagerInternal.ExternalSourcesPolicy.USER_DEFAULT) { - return isTrusted == PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED; - } + return isTrusted == PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED; } - return checkUidPermission(appOpPermission, uid) == PERMISSION_GRANTED; + return false; } @Override diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index a0f0a3178d1b..1908b3f8b366 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -565,7 +565,8 @@ public class StagingManager { // isRollbackInProgress is included to cover the scenario, when a device is rebooted in // during the rollback, and apexd fails to resume the rollback after reboot. return apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown - || apexSessionInfo.isRolledBack || apexSessionInfo.isRollbackInProgress; + || apexSessionInfo.isRolledBack || apexSessionInfo.isRollbackInProgress + || apexSessionInfo.isRollbackFailed; } @GuardedBy("mStagedSessions") diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 5df2f86d6ad7..35fa9406eb79 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -302,6 +302,7 @@ public final class DefaultPermissionGrantPolicy { } public void grantDefaultPermissions(int userId) { + removeSystemFixedStorage(userId); grantPermissionsToSysComponentsAndPrivApps(userId); grantDefaultSystemHandlerPermissions(userId); grantDefaultPermissionExceptions(userId); @@ -310,6 +311,46 @@ public final class DefaultPermissionGrantPolicy { } } + // STOPSHIP: This is meant to fix the devices messed up by storage permission model 2 and + // should be removed once all devices were updated + private void removeSystemFixedStorage(int userId) { + List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackagesAsUser( + DEFAULT_PACKAGE_INFO_QUERY_FLAGS, userId); + + for (PackageInfo pkg : packages) { + if (pkg == null || pkg.requestedPermissions == null) { + continue; + } + + for (String permission : pkg.requestedPermissions) { + if (!(Manifest.permission.READ_EXTERNAL_STORAGE.equals(permission) + || Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission))) { + continue; + } + + int flags = mContext.getPackageManager().getPermissionFlags(permission, + pkg.packageName, UserHandle.of(userId)); + if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) == 0) { + continue; + } + + Log.v(TAG, "Removing system fixed " + pkg.packageName + "/" + permission); + mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName, + PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, UserHandle.of(userId)); + + if (!doesPackageSupportRuntimePermissions(pkg) + || (flags & (PackageManager.FLAG_PERMISSION_USER_SET + | PackageManager.FLAG_PERMISSION_POLICY_FIXED)) != 0) { + continue; + } + + Log.v(TAG, "Revoking " + pkg.packageName + "/" + permission); + mContext.getPackageManager().revokeRuntimePermission(pkg.packageName, permission, + UserHandle.of(userId)); + } + } + } + private void grantRuntimePermissionsForSystemPackage(int userId, PackageInfo pkg) { Set<String> permissions = new ArraySet<>(); for (String permission : pkg.requestedPermissions) { @@ -1322,6 +1363,9 @@ public final class DefaultPermissionGrantPolicy { private PackageInfo getPackageInfo(String pkg, @PackageManager.PackageInfoFlags int extraFlags) { + if (pkg == null) { + return null; + } try { return mContext.getPackageManager().getPackageInfo(pkg, DEFAULT_PACKAGE_INFO_QUERY_FLAGS | extraFlags); diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 3011808d281c..67f30dc2e9fc 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -22,25 +22,28 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.content.Context; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal.PackageListObserver; import android.content.pm.PackageParser; import android.content.pm.PermissionInfo; +import android.os.Process; import android.os.UserHandle; -import android.os.UserManagerInternal; import android.permission.PermissionControllerManager; import android.permission.PermissionManagerInternal; +import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; -import com.android.internal.util.function.QuadConsumer; -import com.android.internal.util.function.TriConsumer; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CountDownLatch; /** @@ -51,11 +54,17 @@ import java.util.concurrent.CountDownLatch; * and app ops - and vise versa. */ public final class PermissionPolicyService extends SystemService { + private static final String PLATFORM_PACKAGE = "android"; private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName(); + // No need to lock as this is populated on boot when the OS is + // single threaded and is never mutated until a reboot. + private static final ArraySet<String> sAllRestrictedPermissions = new ArraySet<>(); + public PermissionPolicyService(@NonNull Context context) { super(context); + cacheAllRestrictedPermissions(context); } @Override @@ -89,6 +98,20 @@ public final class PermissionPolicyService extends SystemService { startWatchingRuntimePermissionChanges(getContext(), userId); } + private static void cacheAllRestrictedPermissions(@NonNull Context context) { + try { + final PackageInfo packageInfo = context.getPackageManager() + .getPackageInfo(PLATFORM_PACKAGE, PackageManager.GET_PERMISSIONS); + for (PermissionInfo permissionInfo : packageInfo.permissions) { + if (permissionInfo.isRestricted()) { + sAllRestrictedPermissions.add(permissionInfo.name); + } + } + } catch (NameNotFoundException impossible) { + /* cannot happen */ + } + } + private static void grantOrUpgradeDefaultRuntimePermissionsInNeeded(@NonNull Context context, @UserIdInt int userId) { final PackageManagerInternal packageManagerInternal = LocalServices.getService( @@ -123,16 +146,6 @@ public final class PermissionPolicyService extends SystemService { } } - private static void onRestrictedPermissionEnabledChange(@NonNull Context context) { - final PermissionManagerInternal permissionManagerInternal = LocalServices - .getService(PermissionManagerInternal.class); - final UserManagerInternal userManagerInternal = LocalServices.getService( - UserManagerInternal.class); - for (int userId : userManagerInternal.getUserIds()) { - synchronizePermissionsAndAppOpsForUser(context, userId); - } - } - private static void startWatchingRuntimePermissionChanges(@NonNull Context context, int userId) { final PermissionManagerInternal permissionManagerInternal = LocalServices.getService( @@ -149,40 +162,66 @@ public final class PermissionPolicyService extends SystemService { @NonNull String packageName, @UserIdInt int userId) { final PackageManagerInternal packageManagerInternal = LocalServices.getService( PackageManagerInternal.class); - final PackageParser.Package pkg = packageManagerInternal - .getPackage(packageName); - if (pkg != null) { - PermissionToOpSynchronizer.syncPackage(context, pkg, userId); + final PackageParser.Package pkg = packageManagerInternal.getPackage(packageName); + if (pkg == null) { + return; + } + final PermissionToOpSynchroniser synchroniser = new PermissionToOpSynchroniser(context); + synchroniser.addPackage(context, pkg, userId); + final String[] sharedPkgNames = packageManagerInternal.getPackagesForSharedUserId( + pkg.mSharedUserId, userId); + if (sharedPkgNames != null) { + for (String sharedPkgName : sharedPkgNames) { + final PackageParser.Package sharedPkg = packageManagerInternal + .getPackage(sharedPkgName); + if (sharedPkg != null) { + synchroniser.addPackage(context, sharedPkg, userId); + } + } } + synchroniser.syncPackages(); } private static void synchronizePermissionsAndAppOpsForUser(@NonNull Context context, @UserIdInt int userId) { final PackageManagerInternal packageManagerInternal = LocalServices.getService( PackageManagerInternal.class); - final PermissionToOpSynchronizer synchronizer = new PermissionToOpSynchronizer(context); + final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(context); packageManagerInternal.forEachPackage((pkg) -> synchronizer.addPackage(context, pkg, userId)); synchronizer.syncPackages(); } - private static class PermissionToOpSynchronizer { + /** + * Synchronizes permission to app ops. You *must* always sync all packages + * in a shared UID at the same time to ensure proper synchronization. + */ + private static class PermissionToOpSynchroniser { private final @NonNull Context mContext; + private final @NonNull SparseIntArray mUids = new SparseIntArray(); private final @NonNull SparseArray<String> mPackageNames = new SparseArray<>(); private final @NonNull SparseIntArray mAllowedUidOps = new SparseIntArray(); private final @NonNull SparseIntArray mDefaultUidOps = new SparseIntArray(); - PermissionToOpSynchronizer(@NonNull Context context) { + PermissionToOpSynchroniser(@NonNull Context context) { mContext = context; } - private void addPackage(@NonNull Context context, - @NonNull PackageParser.Package pkg, @UserIdInt int userId) { - addPackage(context, pkg, userId, this::addAllowedEntry, this::addIgnoredEntry); - } - void syncPackages() { + // TRICKY: we set the app op for a restricted permission to allow if the app + // requesting the permission is whitelisted and to deny if the app requesting + // the permission is not whitelisted. However, there is another case where an + // app in a shared user can access a component in another app in the same shared + // user due to being in the same shared user and not by having the permission + // that guards the component form the rest of the world. We need to handle this. + // The way we do this is by setting app ops corresponding to non requested + // restricted permissions to allow as this would allow the shared uid access + // case and be okay for other apps as they would not have the permission and + // would fail on the permission checks before reaching the app op check. + final SparseArray<List<String>> unrequestedRestrictedPermissionsForUid = + new SparseArray<>(); + final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class); final int allowedCount = mAllowedUidOps.size(); for (int i = 0; i < allowedCount; i++) { @@ -190,31 +229,84 @@ public final class PermissionPolicyService extends SystemService { final int uid = mAllowedUidOps.valueAt(i); final String packageName = mPackageNames.valueAt(i); setUidModeAllowed(appOpsManager, opCode, uid, packageName); + + // Keep track this permission was requested by the UID. + List<String> unrequestedRestrictedPermissions = + unrequestedRestrictedPermissionsForUid.get(uid); + if (unrequestedRestrictedPermissions == null) { + unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions); + unrequestedRestrictedPermissionsForUid.put(uid, + unrequestedRestrictedPermissions); + } + unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(opCode)); + + mUids.delete(uid); } final int defaultCount = mDefaultUidOps.size(); for (int i = 0; i < defaultCount; i++) { final int opCode = mDefaultUidOps.keyAt(i); final int uid = mDefaultUidOps.valueAt(i); setUidModeDefault(appOpsManager, opCode, uid); + + // Keep track this permission was requested by the UID. + List<String> unrequestedRestrictedPermissions = + unrequestedRestrictedPermissionsForUid.get(uid); + if (unrequestedRestrictedPermissions == null) { + unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions); + unrequestedRestrictedPermissionsForUid.put(uid, + unrequestedRestrictedPermissions); + } + unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(opCode)); + + mUids.delete(uid); } - } - static void syncPackage(@NonNull Context context, @NonNull PackageParser.Package pkg, - @UserIdInt int userId) { - addPackage(context, pkg, userId, PermissionToOpSynchronizer::setUidModeAllowed, - PermissionToOpSynchronizer::setUidModeDefault); + // Give root access + mUids.put(Process.ROOT_UID, Process.ROOT_UID); + + // Add records for UIDs that don't use any restricted permissions. + final int uidCount = mUids.size(); + for (int i = 0; i < uidCount; i++) { + final int uid = mUids.keyAt(i); + unrequestedRestrictedPermissionsForUid.put(uid, + new ArrayList<>(sAllRestrictedPermissions)); + } + + // Flip ops for all unrequested restricted permission for the UIDs. + final int unrequestedUidCount = unrequestedRestrictedPermissionsForUid.size(); + for (int i = 0; i < unrequestedUidCount; i++) { + final List<String> unrequestedRestrictedPermissions = + unrequestedRestrictedPermissionsForUid.valueAt(i); + if (unrequestedRestrictedPermissions != null) { + final int uid = unrequestedRestrictedPermissionsForUid.keyAt(i); + final String[] packageNames = (uid != Process.ROOT_UID) + ? mContext.getPackageManager().getPackagesForUid(uid) + : new String[] {"root"}; + if (packageNames == null) { + continue; + } + final int permissionCount = unrequestedRestrictedPermissions.size(); + for (int j = 0; j < permissionCount; j++) { + final String permission = unrequestedRestrictedPermissions.get(j); + for (String packageName : packageNames) { + setUidModeAllowed(appOpsManager, + AppOpsManager.permissionToOpCode(permission), uid, + packageName); + } + } + } + } } - private static void addPackage(@NonNull Context context, - @NonNull PackageParser.Package pkg, @UserIdInt int userId, - @NonNull QuadConsumer<AppOpsManager, Integer, Integer, String> allowedConsumer, - @NonNull TriConsumer<AppOpsManager, Integer, Integer> defaultConsumer) { + private void addPackage(@NonNull Context context, + @NonNull PackageParser.Package pkg, @UserIdInt int userId) { final PackageManager packageManager = context.getPackageManager(); - final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.applicationInfo.uid)); final UserHandle userHandle = UserHandle.of(userId); + mUids.put(uid, uid); + final int permissionCount = pkg.requestedPermissions.size(); for (int i = 0; i < permissionCount; i++) { final String permission = pkg.requestedPermissions.get(i); @@ -241,9 +333,10 @@ public final class PermissionPolicyService extends SystemService { if (permissionInfo.isHardRestricted()) { if (applyRestriction) { - defaultConsumer.accept(appOpsManager, opCode, uid); + mDefaultUidOps.put(opCode, uid); } else { - allowedConsumer.accept(appOpsManager, opCode, uid, pkg.packageName); + mPackageNames.put(opCode, pkg.packageName); + mAllowedUidOps.put(opCode, uid); } } else if (permissionInfo.isSoftRestricted()) { //TODO: Implement soft restrictions like storage here. @@ -251,19 +344,6 @@ public final class PermissionPolicyService extends SystemService { } } - @SuppressWarnings("unused") - private void addAllowedEntry(@NonNull AppOpsManager appOpsManager, int opCode, - int uid, @NonNull String packageName) { - mPackageNames.put(opCode, packageName); - mAllowedUidOps.put(opCode, uid); - } - - @SuppressWarnings("unused") - private void addIgnoredEntry(@NonNull AppOpsManager appOpsManager, - int opCode, int uid) { - mDefaultUidOps.put(opCode, uid); - } - private static void setUidModeAllowed(@NonNull AppOpsManager appOpsManager, int opCode, int uid, @NonNull String packageName) { final int currentMode = appOpsManager.unsafeCheckOpRaw(AppOpsManager diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index f83b3ea10e05..96924c04e5e1 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -1458,8 +1458,18 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private void pullNumBiometricsEnrolled(int modality, int tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { - FingerprintManager fingerprintManager = mContext.getSystemService(FingerprintManager.class); - FaceManager faceManager = mContext.getSystemService(FaceManager.class); + final PackageManager pm = mContext.getPackageManager(); + FingerprintManager fingerprintManager = null; + FaceManager faceManager = null; + + if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + fingerprintManager = mContext.getSystemService( + FingerprintManager.class); + } + if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) { + faceManager = mContext.getSystemService(FaceManager.class); + } + if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT && fingerprintManager == null) { return; } diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java index 56a6c3cb99b2..a9a6b193fe90 100644 --- a/services/core/java/com/android/server/webkit/SystemImpl.java +++ b/services/core/java/com/android/server/webkit/SystemImpl.java @@ -266,6 +266,11 @@ public class SystemImpl implements SystemInterface { } @Override + public void ensureZygoteStarted() { + WebViewZygote.getProcess(); + } + + @Override public boolean isMultiProcessDefaultEnabled() { // Multiprocess is enabled for all 64-bit devices, since the ability to run the renderer // process in 32-bit when it's a separate process typically results in a net memory saving. diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java index 3fb52790621a..743740d277ba 100644 --- a/services/core/java/com/android/server/webkit/SystemInterface.java +++ b/services/core/java/com/android/server/webkit/SystemInterface.java @@ -61,5 +61,7 @@ public interface SystemInterface { public int getMultiProcessSetting(Context context); public void setMultiProcessSetting(Context context, int value); public void notifyZygote(boolean enableMultiProcess); + /** Start the zygote if it's not already running. */ + public void ensureZygoteStarted(); public boolean isMultiProcessDefaultEnabled(); } diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java index f704c30402d3..890456aa6e9c 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java @@ -17,6 +17,7 @@ package com.android.server.webkit; import android.content.Context; import android.content.pm.PackageInfo; +import android.os.AsyncTask; import android.os.UserHandle; import android.webkit.WebViewProviderInfo; import android.webkit.WebViewProviderResponse; @@ -81,6 +82,14 @@ public class WebViewUpdateServiceImpl { migrateFallbackStateOnBoot(); mWebViewUpdater.prepareWebViewInSystemServer(); mSystemInterface.notifyZygote(isMultiProcessEnabled()); + AsyncTask.THREAD_POOL_EXECUTOR.execute(this::startZygoteWhenReady); + } + + void startZygoteWhenReady() { + // Wait on a background thread for RELRO creation to be done. We ignore the return value + // because even if RELRO creation failed we still want to start the zygote. + waitForAndGetProvider(); + mSystemInterface.ensureZygoteStarted(); } void handleNewUser(int userId) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e08e1ff01348..6cf36d692520 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -88,6 +88,8 @@ import static android.os.Build.VERSION_CODES.HONEYCOMB; import static android.os.Build.VERSION_CODES.O; import static android.os.Process.SYSTEM_UID; import static android.view.Display.INVALID_DISPLAY; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER; import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK; @@ -195,6 +197,7 @@ import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.view.AppTransitionAnimationSpec; +import android.view.DisplayCutout; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; import android.view.RemoteAnimationDefinition; @@ -382,6 +385,12 @@ final class ActivityRecord extends ConfigurationContainer { private int[] mHorizontalSizeConfigurations; private int[] mSmallestSizeConfigurations; + /** + * The precomputed display insets for resolving configuration. It will be non-null if + * {@link #shouldUseSizeCompatMode} returns {@code true}. + */ + private CompatDisplayInsets mCompatDisplayInsets; + boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session IVoiceInteractionSession voiceSession; // Voice interaction session for this activity @@ -693,13 +702,13 @@ final class ActivityRecord extends ConfigurationContainer { } } - void scheduleTopResumedActivityChanged(boolean onTop) { + boolean scheduleTopResumedActivityChanged(boolean onTop) { if (!attachedToProcess()) { if (DEBUG_STATES) { Slog.w(TAG, "Can't report activity position update - client not running" + ", activityRecord=" + this); } - return; + return false; } try { if (DEBUG_STATES) { @@ -710,7 +719,9 @@ final class ActivityRecord extends ConfigurationContainer { TopResumedActivityChangeItem.obtain(onTop)); } catch (RemoteException e) { // If process died, whatever. + return false; } + return true; } void updateMultiWindowMode() { @@ -2831,6 +2842,11 @@ final class ActivityRecord extends ConfigurationContainer { // The smallest screen width is the short side of screen bounds. Because the bounds // and density won't be changed, smallestScreenWidthDp is also fixed. overrideConfig.smallestScreenWidthDp = parentConfig.smallestScreenWidthDp; + + final ActivityDisplay display = getDisplay(); + if (display != null && display.mDisplayContent != null) { + mCompatDisplayInsets = new CompatDisplayInsets(display.mDisplayContent); + } } } onRequestedOverrideConfigurationChanged(overrideConfig); @@ -2847,7 +2863,7 @@ final class ActivityRecord extends ConfigurationContainer { super.resolveOverrideConfiguration(newParentConfiguration); if (hasOverrideBounds) { task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), - newParentConfiguration, true /* insideParentBounds */); + newParentConfiguration); } } @@ -2920,9 +2936,8 @@ final class ActivityRecord extends ConfigurationContainer { resolvedBounds.right -= resolvedAppBounds.left; } - // In size compatibility mode, activity is allowed to have larger bounds than its parent. task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, - false /* insideParentBounds */); + mCompatDisplayInsets); // Use parent orientation if it cannot be decided by bounds, so the activity can fit inside // the parent bounds appropriately. if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) { @@ -3408,7 +3423,6 @@ final class ActivityRecord extends ConfigurationContainer { transaction.addCallback(callbackItem); transaction.setLifecycleStateRequest(lifecycleItem); mAtmService.getLifecycleManager().scheduleTransaction(transaction); - mStackSupervisor.updateTopResumedActivityIfNeeded(); // Note: don't need to call pauseIfSleepingLocked() here, because the caller will only // request resume if this activity is currently resumed, which implies we aren't // sleeping. @@ -3449,6 +3463,7 @@ final class ActivityRecord extends ConfigurationContainer { // configuration. getRequestedOverrideConfiguration().setToDefaults(); getResolvedOverrideConfiguration().setToDefaults(); + mCompatDisplayInsets = null; if (visible) { // Configuration will be ensured when becoming visible, so if it is already visible, // then the manual update is needed. @@ -3795,4 +3810,46 @@ final class ActivityRecord extends ConfigurationContainer { writeToProto(proto); proto.end(token); } + + /** + * The precomputed insets of the display in each rotation. This is used to make the size + * compatibility mode activity compute the configuration without relying on its current display. + */ + static class CompatDisplayInsets { + final int mDisplayWidth; + final int mDisplayHeight; + + /** The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. */ + final Rect[] mNonDecorInsets = new Rect[4]; + /** + * The stableInsets for each rotation. Includes the status bar inset and the + * nonDecorInsets. It is used to compute {@link Configuration#screenWidthDp} and + * {@link Configuration#screenHeightDp}. + */ + final Rect[] mStableInsets = new Rect[4]; + + CompatDisplayInsets(DisplayContent display) { + mDisplayWidth = display.mBaseDisplayWidth; + mDisplayHeight = display.mBaseDisplayHeight; + final DisplayPolicy policy = display.getDisplayPolicy(); + final DisplayCutout cutout = display.getDisplayInfo().displayCutout; + for (int rotation = 0; rotation < 4; rotation++) { + mNonDecorInsets[rotation] = new Rect(); + mStableInsets[rotation] = new Rect(); + final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); + final int dw = rotated ? mDisplayHeight : mDisplayWidth; + final int dh = rotated ? mDisplayWidth : mDisplayHeight; + policy.getNonDecorInsetsLw(rotation, dw, dh, cutout, mNonDecorInsets[rotation]); + mStableInsets[rotation].set(mNonDecorInsets[rotation]); + policy.convertNonDecorInsetsToStableInsets(mStableInsets[rotation], rotation); + } + } + + void getDisplayBounds(Rect outBounds, int rotation) { + final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); + final int dw = rotated ? mDisplayHeight : mDisplayWidth; + final int dh = rotated ? mDisplayWidth : mDisplayHeight; + outBounds.set(0, 0, dw, dh); + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index fad4dbd5613b..419f5be5bbc8 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -1500,7 +1500,6 @@ class ActivityStack extends ConfigurationContainer { + " callers=" + Debug.getCallers(5)); r.setState(RESUMED, "minimalResumeActivityLocked"); r.completeResumeLocked(); - mStackSupervisor.updateTopResumedActivityIfNeeded(); if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Launch completed; removing icicle of " + r.icicle); } @@ -2571,7 +2570,6 @@ class ActivityStack extends ConfigurationContainer { // Protect against recursion. mInResumeTopActivity = true; result = resumeTopActivityInnerLocked(prev, options); - mStackSupervisor.updateTopResumedActivityIfNeeded(); // When resuming the top activity, it may be necessary to pause the top activity (for // example, returning to the lock screen. We suppress the normal pause logic in @@ -2606,6 +2604,7 @@ class ActivityStack extends ConfigurationContainer { if (DEBUG_STACK) Slog.d(TAG_STACK, "setResumedActivity stack:" + this + " + from: " + mResumedActivity + " to:" + r + " reason:" + reason); mResumedActivity = r; + mStackSupervisor.updateTopResumedActivityIfNeeded(); } @GuardedBy("mService") diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 53dc1df5a46a..a9d76a61f4f5 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -853,7 +853,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Schedule transaction. mService.getLifecycleManager().scheduleTransaction(clientTransaction); - updateTopResumedActivityIfNeeded(); if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0 && mService.mHasHeavyWeightFeature) { @@ -2321,8 +2320,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // mTopResumedActivityWaitingForPrev == true at this point would mean that an activity // before the prevTopActivity one hasn't reported back yet. So server never sent the top // resumed state change message to prevTopActivity. - if (prevActivityReceivedTopState) { - prevTopActivity.scheduleTopResumedActivityChanged(false /* onTop */); + if (prevActivityReceivedTopState + && prevTopActivity.scheduleTopResumedActivityChanged(false /* onTop */)) { scheduleTopResumedStateLossTimeout(prevTopActivity); mTopResumedActivityWaitingForPrev = true; } @@ -2776,8 +2775,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { true /* forceSend */, targetActivity); mActivityMetricsLogger.notifyActivityLaunching(task.intent); try { - mService.moveTaskToFrontLocked(task.taskId, 0, options, - true /* fromRecents */); + mService.moveTaskToFrontLocked(null /* appThread */, null /* callingPackage */, + task.taskId, 0, options, true /* fromRecents */); // Apply options to prevent pendingOptions be taken by client to make sure // the override pending app transition will be applied immediately. targetActivity.applyOptionsLocked(); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 3b358e897ccc..473a8757f09f 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -759,8 +759,8 @@ class ActivityStarter { try { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "shouldAbortBackgroundActivityStart"); - abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid, callingPid, - callingPackage, realCallingUid, realCallingPid, callerApp, + abortBackgroundStart = shouldAbortBackgroundActivityStart(callingUid, + callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, originatingPendingIntent, allowBackgroundActivityStart, intent); } finally { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); @@ -768,14 +768,7 @@ class ActivityStarter { abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled()); // TODO: remove this toast after feature development is done if (abortBackgroundStart) { - final Resources res = mService.mContext.getResources(); - final String toastMsg = res.getString(abort - ? R.string.activity_starter_block_bg_activity_starts_enforcing - : R.string.activity_starter_block_bg_activity_starts_permissive, - callingPackage); - mService.mUiHandler.post(() -> { - Toast.makeText(mService.mContext, toastMsg, Toast.LENGTH_LONG).show(); - }); + showBackgroundActivityBlockedToast(abort, callingPackage); } } @@ -935,7 +928,7 @@ class ActivityStarter { return res; } - private boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, + boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, final String callingPackage, int realCallingUid, int realCallingPid, WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart, Intent intent) { @@ -1025,11 +1018,11 @@ class ActivityStarter { if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) { return false; } - // don't abort if the callingPackage is the device owner - if (mService.isDeviceOwner(callingPackage)) { + // don't abort if the callingUid is the device owner + if (mService.isDeviceOwner(callingUid)) { return false; } - // don't abort if the callingPackage has companion device + // don't abort if the callingUid has companion device final int callingUserId = UserHandle.getUserId(callingUid); if (mService.isAssociatedCompanionApp(callingUserId, callingUid)) { return false; @@ -1048,7 +1041,7 @@ class ActivityStarter { + "; realCallingUid: " + realCallingUid + "; isRealCallingUidForeground: " + isRealCallingUidForeground + "; isRealCallingUidPersistentSystemProcess: " - + isRealCallingUidPersistentSystemProcess + + isRealCallingUidPersistentSystemProcess + "; originatingPendingIntent: " + originatingPendingIntent + "; isBgStartWhitelisted: " + allowBackgroundActivityStart + "; intent: " + intent @@ -1076,6 +1069,18 @@ class ActivityStarter { return false; } + // TODO: remove this toast after feature development is done + void showBackgroundActivityBlockedToast(boolean abort, String callingPackage) { + final Resources res = mService.mContext.getResources(); + final String toastMsg = res.getString(abort + ? R.string.activity_starter_block_bg_activity_starts_enforcing + : R.string.activity_starter_block_bg_activity_starts_permissive, + callingPackage); + mService.mUiHandler.post(() -> { + Toast.makeText(mService.mContext, toastMsg, Toast.LENGTH_LONG).show(); + }); + } + /** * Creates a launch intent for the given auxiliary resolution data. */ diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index b2e5b6ab7a1a..7d25466bf348 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -507,9 +507,9 @@ public abstract class ActivityTaskManagerInternal { public abstract boolean isUidForeground(int uid); /** - * Called by DevicePolicyManagerService to set the package name of the device owner. + * Called by DevicePolicyManagerService to set the uid of the device owner. */ - public abstract void setDeviceOwnerPackageName(String deviceOwnerPkg); + public abstract void setDeviceOwnerUid(int uid); /** Set all associated companion app that belongs to an userId. */ public abstract void setCompanionAppPackages(int userId, Set<String> companionAppPackages); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 10ae78297a41..b4249046c1b5 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -633,7 +633,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private FontScaleSettingObserver mFontScaleSettingObserver; - private String mDeviceOwnerPackageName; + private int mDeviceOwnerUid = Process.INVALID_UID; private final class FontScaleSettingObserver extends ContentObserver { private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE); @@ -2281,25 +2281,48 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * TODO: Add mController hook */ @Override - public void moveTaskToFront(int taskId, int flags, Bundle bOptions) { + public void moveTaskToFront(IApplicationThread appThread, String callingPackage, int taskId, + int flags, Bundle bOptions) { mAmInternal.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()"); if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId); synchronized (mGlobalLock) { - moveTaskToFrontLocked(taskId, flags, SafeActivityOptions.fromBundle(bOptions), - false /* fromRecents */); + moveTaskToFrontLocked(appThread, callingPackage, taskId, flags, + SafeActivityOptions.fromBundle(bOptions), false /* fromRecents */); } } - void moveTaskToFrontLocked(int taskId, int flags, SafeActivityOptions options, + void moveTaskToFrontLocked(@Nullable IApplicationThread appThread, + @Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options, boolean fromRecents) { - if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), - Binder.getCallingUid(), -1, -1, "Task to front")) { + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); + if (!isSameApp(callingUid, callingPackage)) { + String msg = "Permission Denial: moveTaskToFrontLocked() from pid=" + + Binder.getCallingPid() + " as package " + callingPackage; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + if (!checkAppSwitchAllowedLocked(callingPid, callingUid, -1, -1, "Task to front")) { SafeActivityOptions.abort(options); return; } final long origId = Binder.clearCallingIdentity(); + WindowProcessController callerApp = null; + if (appThread != null) { + callerApp = getProcessController(appThread); + } + final ActivityStarter starter = getActivityStartController().obtainStarter( + null /* intent */, "moveTaskToFront"); + if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1, + -1, callerApp, null, false, null)) { + boolean abort = !isBackgroundActivityStartsEnabled(); + starter.showBackgroundActivityBlockedToast(abort, callingPackage); + if (abort) { + return; + } + } try { final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId); if (task == null) { @@ -2331,6 +2354,26 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + /** + * Return true if callingUid is system, or packageName belongs to that callingUid. + */ + boolean isSameApp(int callingUid, @Nullable String packageName) { + try { + if (callingUid != 0 && callingUid != SYSTEM_UID) { + if (packageName == null) { + return false; + } + final int uid = AppGlobals.getPackageManager().getPackageUid(packageName, + PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + UserHandle.getUserId(callingUid)); + return UserHandle.isSameApp(callingUid, uid); + } + } catch (RemoteException e) { + // Should not happen + } + return true; + } + boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid, int callingPid, int callingUid, String name) { if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) { @@ -5291,7 +5334,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return mAmInternal.isBackgroundActivityStartsEnabled(); } - boolean isPackageNameWhitelistedForBgActivityStarts(String packageName) { + boolean isPackageNameWhitelistedForBgActivityStarts(@Nullable String packageName) { + if (packageName == null) { + return false; + } return mAmInternal.isPackageNameWhitelistedForBgActivityStarts(packageName); } @@ -5830,15 +5876,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { || mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid); } - boolean isDeviceOwner(String packageName) { - if (packageName == null) { - return false; - } - return packageName.equals(mDeviceOwnerPackageName); + boolean isDeviceOwner(int uid) { + return uid >= 0 && mDeviceOwnerUid == uid; } - void setDeviceOwnerPackageName(String deviceOwnerPkg) { - mDeviceOwnerPackageName = deviceOwnerPkg; + void setDeviceOwnerUid(int uid) { + mDeviceOwnerUid = uid; } /** @@ -7288,9 +7331,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void setDeviceOwnerPackageName(String deviceOwnerPkg) { + public void setDeviceOwnerUid(int uid) { synchronized (mGlobalLock) { - ActivityTaskManagerService.this.setDeviceOwnerPackageName(deviceOwnerPkg); + ActivityTaskManagerService.this.setDeviceOwnerUid(uid); } } diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 441c5935a507..e967a92f1891 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -27,6 +27,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.UserHandle; +import android.util.Slog; /** * An implementation of IAppTask, that allows an app to manage its own tasks via @@ -34,6 +35,7 @@ import android.os.UserHandle; * only the process that calls getAppTasks() can call the AppTask methods. */ class AppTaskImpl extends IAppTask.Stub { + private static final String TAG = "AppTaskImpl"; private ActivityTaskManagerService mService; private int mTaskId; @@ -90,16 +92,36 @@ class AppTaskImpl extends IAppTask.Stub { } @Override - public void moveToFront() { + public void moveToFront(IApplicationThread appThread, String callingPackage) { checkCaller(); // Will bring task to front if it already has a root activity. final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); + if (!mService.isSameApp(callingUid, callingPackage)) { + String msg = "Permission Denial: moveToFront() from pid=" + + Binder.getCallingPid() + " as package " + callingPackage; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } final long origId = Binder.clearCallingIdentity(); try { synchronized (mService.mGlobalLock) { - mService.mStackSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId, - null); + WindowProcessController callerApp = null; + if (appThread != null) { + callerApp = mService.getProcessController(appThread); + } + final ActivityStarter starter = mService.getActivityStartController().obtainStarter( + null /* intent */, "moveToFront"); + if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, + callingPackage, -1, -1, callerApp, null, false, null)) { + boolean abort = !mService.isBackgroundActivityStartsEnabled(); + starter.showBackgroundActivityBlockedToast(abort, callingPackage); + if (abort) { + return; + } + } + mService.mStackSupervisor.startActivityFromRecents(callingPid, + callingUid, mTaskId, null); } } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index f9980bebca9e..c1d872f23f0f 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -73,6 +73,13 @@ public class BoundsAnimationController { /** Schedule a PiP mode changed callback when this animation ends. */ public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2; + public static final int BOUNDS = 0; + public static final int FADE_IN = 1; + + @IntDef({BOUNDS, FADE_IN}) public @interface AnimationType {} + + private static final int FADE_IN_DURATION = 500; + // Only accessed on UI thread. private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>(); @@ -115,6 +122,7 @@ public class BoundsAnimationController { private boolean mFinishAnimationAfterTransition = false; private final AnimationHandler mAnimationHandler; private Choreographer mChoreographer; + private @AnimationType int mAnimationType; private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000; @@ -140,6 +148,7 @@ public class BoundsAnimationController { implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { private final BoundsAnimationTarget mTarget; + private final @AnimationType int mAnimationType; private final Rect mFrom = new Rect(); private final Rect mTo = new Rect(); private final Rect mTmpRect = new Rect(); @@ -166,8 +175,8 @@ public class BoundsAnimationController { // Depending on whether we are animating from // a smaller to a larger size - private final int mFrozenTaskWidth; - private final int mFrozenTaskHeight; + private int mFrozenTaskWidth; + private int mFrozenTaskHeight; // Timeout callback to ensure we continue the animation if waiting for resuming or app // windows drawn fails @@ -176,12 +185,13 @@ public class BoundsAnimationController { resume(); }; - BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to, - @SchedulePipModeChangedState int schedulePipModeChangedState, + BoundsAnimator(BoundsAnimationTarget target, @AnimationType int animationType, Rect from, + Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState, @SchedulePipModeChangedState int prevShedulePipModeChangedState, boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) { super(); mTarget = target; + mAnimationType = animationType; mFrom.set(from); mTo.set(to); mSchedulePipModeChangedState = schedulePipModeChangedState; @@ -195,12 +205,14 @@ public class BoundsAnimationController { // to their final size immediately so we can use scaling to make the window // larger. Likewise if we are going from bigger to smaller, we want to wait until // the end so we don't have to upscale from the smaller finished size. - if (animatingToLargerSize()) { - mFrozenTaskWidth = mTo.width(); - mFrozenTaskHeight = mTo.height(); - } else { - mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width(); - mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height(); + if (mAnimationType == BOUNDS) { + if (animatingToLargerSize()) { + mFrozenTaskWidth = mTo.width(); + mFrozenTaskHeight = mTo.height(); + } else { + mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width(); + mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height(); + } } } @@ -222,8 +234,9 @@ public class BoundsAnimationController { // otherwise. boolean continueAnimation; if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) { - continueAnimation = mTarget.onAnimationStart(mSchedulePipModeChangedState == - SCHEDULE_PIP_MODE_CHANGED_ON_START, false /* forceUpdate */); + continueAnimation = mTarget.onAnimationStart( + mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START, + false /* forceUpdate */, mAnimationType); // When starting an animation from fullscreen, pause here and wait for the // windows-drawn signal before we start the rest of the transition down into PiP. @@ -238,7 +251,8 @@ public class BoundsAnimationController { // However, we still need to report to them that they are leaving PiP, so this will // force an update via a mode changed callback. continueAnimation = mTarget.onAnimationStart( - true /* schedulePipModeChangedCallback */, true /* forceUpdate */); + true /* schedulePipModeChangedCallback */, true /* forceUpdate */, + mAnimationType); } else { // The animation is already running, but we should check that the TaskStack is still // valid before continuing with the animation @@ -285,6 +299,13 @@ public class BoundsAnimationController { @Override public void onAnimationUpdate(ValueAnimator animation) { final float value = (Float) animation.getAnimatedValue(); + if (mAnimationType == FADE_IN) { + if (!mTarget.setPinnedStackAlpha(value)) { + cancelAndCallAnimationEnd(); + } + return; + } + final float remains = 1 - value; mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f); mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f); @@ -408,16 +429,29 @@ public class BoundsAnimationController { public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to, int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen) { + boolean moveFromFullscreen, boolean moveToFullscreen, + @AnimationType int animationType) { animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState, - moveFromFullscreen, moveToFullscreen); + moveFromFullscreen, moveToFullscreen, animationType); } @VisibleForTesting BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to, int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen) { + boolean moveFromFullscreen, boolean moveToFullscreen, + @AnimationType int animationType) { final BoundsAnimator existing = mRunningAnimations.get(target); + // animateBoundsImpl gets called twice for each animation. The second time we get the final + // to rect that respects the shelf, which is when we want to resize. Our signal for fade in + // comes in from how to enter into pip, but we also need to use the to and from rect to + // decide which animation we want to run finally. + boolean shouldResize = false; + if (isRunningFadeInAnimation(target)) { + shouldResize = true; + if (from.contains(to)) { + animationType = FADE_IN; + } + } final boolean replacing = existing != null; @SchedulePipModeChangedState int prevSchedulePipModeChangedState = NO_PIP_MODE_CHANGED_CALLBACKS; @@ -477,18 +511,34 @@ public class BoundsAnimationController { // Since we are replacing, we skip both animation start and end callbacks existing.cancel(); } - final BoundsAnimator animator = new BoundsAnimator(target, from, to, + if (shouldResize) { + target.setPinnedStackSize(to, null); + } + final BoundsAnimator animator = new BoundsAnimator(target, animationType, from, to, schedulePipModeChangedState, prevSchedulePipModeChangedState, moveFromFullscreen, moveToFullscreen, frozenTask); mRunningAnimations.put(target, animator); animator.setFloatValues(0f, 1f); - animator.setDuration((animationDuration != -1 ? animationDuration - : DEFAULT_TRANSITION_DURATION) * DEBUG_ANIMATION_SLOW_DOWN_FACTOR); + animator.setDuration(animationType == FADE_IN ? FADE_IN_DURATION + : (animationDuration != -1 ? animationDuration : DEFAULT_TRANSITION_DURATION) + * DEBUG_ANIMATION_SLOW_DOWN_FACTOR); animator.setInterpolator(mFastOutSlowInInterpolator); animator.start(); return animator; } + public void setAnimationType(@AnimationType int animationType) { + mAnimationType = animationType; + } + + /** return the current animation type. */ + public @AnimationType int getAnimationType() { + @AnimationType int animationType = mAnimationType; + // Default to BOUNDS. + mAnimationType = BOUNDS; + return animationType; + } + public Handler getHandler() { return mHandler; } @@ -498,6 +548,11 @@ public class BoundsAnimationController { mHandler.post(this::resume); } + private boolean isRunningFadeInAnimation(final BoundsAnimationTarget target) { + final BoundsAnimator existing = mRunningAnimations.get(target); + return existing != null && existing.mAnimationType == FADE_IN && existing.isStarted(); + } + private void resume() { for (int i = 0; i < mRunningAnimations.size(); i++) { final BoundsAnimator b = mRunningAnimations.valueAt(i); diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java index 5cb80de1a36d..9f54e49e0022 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java @@ -32,7 +32,8 @@ interface BoundsAnimationTarget { * callbacks * @return whether to continue the animation */ - boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate); + boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, + @BoundsAnimationController.AnimationType int animationType); /** * @return Whether the animation should be paused waiting for the windows to draw before @@ -53,6 +54,9 @@ interface BoundsAnimationTarget { */ boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds); + /** Sets the alpha of the animation target */ + boolean setPinnedStackAlpha(float alpha); + /** * Callback for the target to inform it that the animation has ended, so it can do some * necessary cleanup. diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 6ae772003a9a..1934e2508c9b 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -2775,6 +2775,16 @@ public class DisplayPolicy { } /** + * Calculates the stable insets if we already have the non-decor insets. + * + * @param inOutInsets The known non-decor insets. It will be modified to stable insets. + * @param rotation The current display rotation. + */ + void convertNonDecorInsetsToStableInsets(Rect inOutInsets, int rotation) { + inOutInsets.top = Math.max(inOutInsets.top, mStatusBarHeightForRotation[rotation]); + } + + /** * Calculates the stable insets without running a layout. * * @param displayRotation the current display rotation @@ -2789,7 +2799,7 @@ public class DisplayPolicy { // Navigation bar and status bar. getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, displayCutout, outInsets); - outInsets.top = Math.max(outInsets.top, mStatusBarHeightForRotation[displayRotation]); + convertNonDecorInsetsToStableInsets(outInsets, displayRotation); } /** diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 144efb49e84a..07d3fb990470 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -26,6 +26,8 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.WindowManager.TRANSIT_NONE; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.BoundsAnimationController.BOUNDS; +import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP; @@ -201,7 +203,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } } - private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode) { + private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode, + boolean sendUserLeaveHint) { synchronized (mService.mGlobalLock) { if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller=" + mWindowManager.getRecentsAnimationController() @@ -246,7 +249,18 @@ class RecentsAnimation implements RecentsAnimationCallbacks, if (reorderMode == REORDER_MOVE_TO_TOP) { // Bring the target stack to the front mStackSupervisor.mNoAnimActivities.add(targetActivity); - targetStack.moveToFront("RecentsAnimation.onAnimationFinished()"); + + if (sendUserLeaveHint) { + // Setting this allows the previous app to PiP. + mStackSupervisor.mUserLeaving = true; + targetStack.moveTaskToFrontLocked(targetActivity.getTaskRecord(), + true /* noAnimation */, null /* activityOptions */, + targetActivity.appTimeTracker, + "RecentsAnimation.onAnimationFinished()"); + } else { + targetStack.moveToFront("RecentsAnimation.onAnimationFinished()"); + } + if (DEBUG) { final ActivityStack topStack = getTopNonAlwaysOnTopStack(); if (topStack != targetStack) { @@ -300,11 +314,11 @@ class RecentsAnimation implements RecentsAnimationCallbacks, @Override public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode, - boolean runSychronously) { + boolean runSychronously, boolean sendUserLeaveHint) { if (runSychronously) { - finishAnimation(reorderMode); + finishAnimation(reorderMode, sendUserLeaveHint); } else { - mService.mH.post(() -> finishAnimation(reorderMode)); + mService.mH.post(() -> finishAnimation(reorderMode, sendUserLeaveHint)); } } @@ -317,6 +331,10 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } final RecentsAnimationController controller = mWindowManager.getRecentsAnimationController(); + final DisplayContent dc = + mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent; + dc.mBoundsAnimationController.setAnimationType( + controller.shouldCancelWithDeferredScreenshot() ? FADE_IN : BOUNDS); // Cancel running recents animation and screenshot previous task when the next // transition starts in below cases: diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 381366995dd5..d2c510fa8902 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -27,6 +27,7 @@ import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; import static com.android.server.wm.AnimationAdapterProto.REMOTE; +import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS; import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; @@ -142,7 +143,9 @@ public class RecentsAnimationController implements DeathRecipient { }; public interface RecentsAnimationCallbacks { - void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously); + /** Callback when recents animation is finished. */ + void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously, + boolean sendUserLeaveHint); } private final IRecentsAnimationController mController = @@ -179,7 +182,7 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - public void finish(boolean moveHomeToTop) { + public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) { if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "finish(" + moveHomeToTop + "):" + " mCanceled=" + mCanceled); final long token = Binder.clearCallingIdentity(); @@ -195,7 +198,9 @@ public class RecentsAnimationController implements DeathRecipient { mCallbacks.onAnimationFinished(moveHomeToTop ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION, - true /* runSynchronously */); + true /* runSynchronously */, sendUserLeaveHint); + final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); + dc.mBoundsAnimationController.setAnimationType(FADE_IN); } finally { Binder.restoreCallingIdentity(token); } @@ -497,7 +502,8 @@ public class RecentsAnimationController implements DeathRecipient { Slog.e(TAG, "Failed to cancel recents animation", e); } // Clean up and return to the previous app - mCallbacks.onAnimationFinished(reorderMode, runSynchronously); + mCallbacks.onAnimationFinished(reorderMode, runSynchronously, + false /* sendUserLeaveHint */); } } @@ -542,7 +548,8 @@ public class RecentsAnimationController implements DeathRecipient { if (DEBUG_RECENTS_ANIMATIONS) { Slog.d(TAG, "mRecentScreenshotAnimator finish"); } - mCallbacks.onAnimationFinished(reorderMode, runSynchronously); + mCallbacks.onAnimationFinished(reorderMode, runSynchronously, + false /* sendUserLeaveHint */); }, mService); mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator); } diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 714c2274af8e..15060e1fc712 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -22,6 +22,7 @@ import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED; import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; @@ -1688,6 +1689,8 @@ class TaskRecord extends ConfigurationContainer { int colorBackground = 0; int statusBarColor = 0; int navigationBarColor = 0; + boolean statusBarContrastWhenTransparent = false; + boolean navigationBarContrastWhenTransparent = false; boolean topActivity = true; for (--activityNdx; activityNdx >= 0; --activityNdx) { final ActivityRecord r = mActivities.get(activityNdx); @@ -1711,12 +1714,17 @@ class TaskRecord extends ConfigurationContainer { colorBackground = r.taskDescription.getBackgroundColor(); statusBarColor = r.taskDescription.getStatusBarColor(); navigationBarColor = r.taskDescription.getNavigationBarColor(); + statusBarContrastWhenTransparent = + r.taskDescription.getEnsureStatusBarContrastWhenTransparent(); + navigationBarContrastWhenTransparent = + r.taskDescription.getEnsureNavigationBarContrastWhenTransparent(); } } topActivity = false; } lastTaskDescription = new TaskDescription(label, null, iconResource, iconFilename, - colorPrimary, colorBackground, statusBarColor, navigationBarColor); + colorPrimary, colorBackground, statusBarColor, navigationBarColor, + statusBarContrastWhenTransparent, navigationBarContrastWhenTransparent); if (mTask != null) { mTask.setTaskDescription(lastTaskDescription); } @@ -2041,15 +2049,12 @@ class TaskRecord extends ConfigurationContainer { } mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); - policy.getStableInsetsLw(displayInfo.rotation, - displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout, - mTmpInsets); - intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets); - - policy.getNonDecorInsetsLw(displayInfo.rotation, - displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout, - mTmpInsets); + policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth, + displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets); intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets); + + policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation); + intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets); } /** @@ -2066,7 +2071,7 @@ class TaskRecord extends ConfigurationContainer { void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig) { - computeConfigResourceOverrides(inOutConfig, parentConfig, true /* insideParentBounds */); + computeConfigResourceOverrides(inOutConfig, parentConfig, null /* compatInsets */); } /** @@ -2078,7 +2083,8 @@ class TaskRecord extends ConfigurationContainer { * just be inherited from the parent configuration. **/ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, - @NonNull Configuration parentConfig, boolean insideParentBounds) { + @NonNull Configuration parentConfig, + @Nullable ActivityRecord.CompatDisplayInsets compatInsets) { int windowingMode = inOutConfig.windowConfiguration.getWindowingMode(); if (windowingMode == WINDOWING_MODE_UNDEFINED) { windowingMode = parentConfig.windowConfiguration.getWindowingMode(); @@ -2096,6 +2102,9 @@ class TaskRecord extends ConfigurationContainer { inOutConfig.windowConfiguration.setAppBounds(bounds); outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); } + // Non-null compatibility insets means the activity prefers to keep its original size, so + // the out bounds doesn't need to be restricted by the parent. + final boolean insideParentBounds = compatInsets == null; if (insideParentBounds && windowingMode != WINDOWING_MODE_FREEFORM) { final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds(); if (parentAppBounds != null && !parentAppBounds.isEmpty()) { @@ -2118,6 +2127,17 @@ class TaskRecord extends ConfigurationContainer { // Set to app bounds because it excludes decor insets. mTmpNonDecorBounds.set(outAppBounds); mTmpStableBounds.set(outAppBounds); + + // Apply the given non-decor and stable insets to calculate the corresponding bounds + // for screen size of configuration. + final int rotation = parentConfig.windowConfiguration.getRotation(); + if (rotation != ROTATION_UNDEFINED && compatInsets != null) { + compatInsets.getDisplayBounds(mTmpBounds, rotation); + intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds, + compatInsets.mNonDecorInsets[rotation]); + intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds, + compatInsets.mStableInsets[rotation]); + } } if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 6fe8b43db139..f31416cdc620 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -362,11 +362,9 @@ class TaskSnapshotController { } final int color = ColorUtils.setAlphaComponent( task.getTaskDescription().getBackgroundColor(), 255); - final int statusBarColor = task.getTaskDescription().getStatusBarColor(); - final int navigationBarColor = task.getTaskDescription().getNavigationBarColor(); final LayoutParams attrs = mainWindow.getAttrs(); final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags, - attrs.privateFlags, attrs.systemUiVisibility, statusBarColor, navigationBarColor); + attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription()); final int width = mainWindow.getFrameLw().width(); final int height = mainWindow.getFrameLw().height(); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 39b56625c011..5d99db5a00de 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -18,6 +18,8 @@ package com.android.server.wm; import static android.graphics.Color.WHITE; import static android.graphics.Color.alpha; +import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; +import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES; @@ -148,9 +150,8 @@ class TaskSnapshotSurface implements StartingSurface { final Rect tmpStableInsets = new Rect(); final InsetsState mTmpInsetsState = new InsetsState(); final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); - int backgroundColor = WHITE; - int statusBarColor = 0; - int navigationBarColor = 0; + final TaskDescription taskDescription = new TaskDescription(); + taskDescription.setBackgroundColor(WHITE); final int sysUiVis; final int windowFlags; final int windowPrivateFlags; @@ -194,11 +195,9 @@ class TaskSnapshotSurface implements StartingSurface { layoutParams.systemUiVisibility = sysUiVis; layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId)); - final TaskDescription taskDescription = task.getTaskDescription(); - if (taskDescription != null) { - backgroundColor = taskDescription.getBackgroundColor(); - statusBarColor = taskDescription.getStatusBarColor(); - navigationBarColor = taskDescription.getNavigationBarColor(); + final TaskDescription td = task.getTaskDescription(); + if (td != null) { + taskDescription.copyFrom(td); } taskBounds = new Rect(); task.getBounds(taskBounds); @@ -216,8 +215,8 @@ class TaskSnapshotSurface implements StartingSurface { // Local call. } final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window, - surfaceControl, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor, - navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds, + surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis, + windowFlags, windowPrivateFlags, taskBounds, currentOrientation); window.setOuter(snapshotSurface); try { @@ -234,9 +233,9 @@ class TaskSnapshotSurface implements StartingSurface { @VisibleForTesting TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl, - TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor, - int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags, - Rect taskBounds, int currentOrientation) { + TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, + int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds, + int currentOrientation) { mService = service; mSurface = new Surface(); mHandler = new Handler(mService.mH.getLooper()); @@ -245,11 +244,12 @@ class TaskSnapshotSurface implements StartingSurface { mSurfaceControl = surfaceControl; mSnapshot = snapshot; mTitle = title; + int backgroundColor = taskDescription.getBackgroundColor(); mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); mTaskBounds = taskBounds; mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags, - windowPrivateFlags, sysUiVis, statusBarColor, navigationBarColor); - mStatusBarColor = statusBarColor; + windowPrivateFlags, sysUiVis, taskDescription); + mStatusBarColor = taskDescription.getStatusBarColor(); mOrientationOnCreation = currentOrientation; } @@ -490,7 +490,7 @@ class TaskSnapshotSurface implements StartingSurface { private final int mSysUiVis; SystemBarBackgroundPainter( int windowFlags, int windowPrivateFlags, int sysUiVis, - int statusBarColor, int navigationBarColor) { + TaskDescription taskDescription) { mWindowFlags = windowFlags; mWindowPrivateFlags = windowPrivateFlags; mSysUiVis = sysUiVis; @@ -498,11 +498,17 @@ class TaskSnapshotSurface implements StartingSurface { final int semiTransparent = context.getColor( R.color.system_bar_background_semi_transparent); mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS, - semiTransparent, statusBarColor); + semiTransparent, taskDescription.getStatusBarColor(), sysUiVis, + SYSTEM_UI_FLAG_LIGHT_STATUS_BAR, + taskDescription.getEnsureStatusBarContrastWhenTransparent()); mNavigationBarColor = DecorView.calculateBarColor(windowFlags, - FLAG_TRANSLUCENT_NAVIGATION, semiTransparent, navigationBarColor); + FLAG_TRANSLUCENT_NAVIGATION, semiTransparent, + taskDescription.getNavigationBarColor(), sysUiVis, + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + taskDescription.getEnsureNavigationBarContrastWhenTransparent() + && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim)); mStatusBarPaint.setColor(mStatusBarColor); - mNavigationBarPaint.setColor(navigationBarColor); + mNavigationBarPaint.setColor(mNavigationBarColor); } void setInsets(Rect contentInsets, Rect stableInsets) { diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 09baf8cf1111..bdb4d0474865 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -34,6 +34,7 @@ import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_RIGHT; import static android.view.WindowManager.DOCKED_TOP; +import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; @@ -148,6 +149,7 @@ public class TaskStack extends WindowContainer<Task> implements private boolean mCancelCurrentBoundsAnimation = false; private Rect mBoundsAnimationTarget = new Rect(); private Rect mBoundsAnimationSourceHintBounds = new Rect(); + private @BoundsAnimationController.AnimationType int mAnimationType; Rect mPreAnimationBounds = new Rect(); @@ -1572,7 +1574,8 @@ public class TaskStack extends WindowContainer<Task> implements } @Override // AnimatesBounds - public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) { + public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, + @BoundsAnimationController.AnimationType int animationType) { // Hold the lock since this is called from the BoundsAnimator running on the UiThread synchronized (mWmService.mGlobalLock) { if (!isAttached()) { @@ -1583,6 +1586,7 @@ public class TaskStack extends WindowContainer<Task> implements mBoundsAnimatingRequested = false; mBoundsAnimating = true; mCancelCurrentBoundsAnimation = false; + mAnimationType = animationType; // If we are changing UI mode, as in the PiP to fullscreen // transition, then we need to wait for the window to draw. @@ -1599,7 +1603,8 @@ public class TaskStack extends WindowContainer<Task> implements // I don't believe you... } - if (schedulePipModeChangedCallback && mActivityStack != null) { + if ((schedulePipModeChangedCallback || animationType == FADE_IN) + && mActivityStack != null) { // We need to schedule the PiP mode change before the animation up. It is possible // in this case for the animation down to not have been completed, so always // force-schedule and update to the client to ensure that it is notified that it @@ -1614,6 +1619,21 @@ public class TaskStack extends WindowContainer<Task> implements @Override // AnimatesBounds public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, boolean moveToFullscreen) { + if (mAnimationType == BoundsAnimationController.FADE_IN) { + setPinnedStackAlpha(1f); + try { + mWmService.mActivityTaskManager.notifyPinnedStackAnimationEnded(); + } catch (RemoteException e) { + // I don't believe you... + } + return; + } + + onBoundAnimationEnd(schedulePipModeChangedCallback, finalStackSize, moveToFullscreen); + } + + private void onBoundAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, + boolean moveToFullscreen) { if (inPinnedWindowingMode()) { // Update to the final bounds if requested. This is done here instead of in the bounds // animator to allow us to coordinate this after we notify the PiP mode changed @@ -1725,10 +1745,23 @@ public class TaskStack extends WindowContainer<Task> implements final @SchedulePipModeChangedState int finalSchedulePipModeChangedState = schedulePipModeChangedState; final DisplayContent displayContent = getDisplayContent(); + @BoundsAnimationController.AnimationType int intendedAnimationType = + displayContent.mBoundsAnimationController.getAnimationType(); + if (intendedAnimationType == FADE_IN) { + if (fromFullscreen) { + setPinnedStackAlpha(0f); + } + if (toBounds.width() == fromBounds.width() + && toBounds.height() == fromBounds.height()) { + intendedAnimationType = BoundsAnimationController.BOUNDS; + } + } + + final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType; displayContent.mBoundsAnimationController.getHandler().post(() -> { displayContent.mBoundsAnimationController.animateBounds(this, fromBounds, finalToBounds, animationDuration, finalSchedulePipModeChangedState, - fromFullscreen, toFullscreen); + fromFullscreen, toFullscreen, animationType); }); } @@ -1905,6 +1938,20 @@ public class TaskStack extends WindowContainer<Task> implements } } + @Override + public boolean setPinnedStackAlpha(float alpha) { + // Hold the lock since this is called from the BoundsAnimator running on the UiThread + synchronized (mWmService.mGlobalLock) { + if (mCancelCurrentBoundsAnimation) { + return false; + } + getPendingTransaction().setAlpha(getSurfaceControl(), alpha); + scheduleAnimation(); + } + + return true; + } + public DisplayInfo getDisplayInfo() { return mDisplayContent.getDisplayInfo(); } diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp new file mode 100644 index 000000000000..967750ddc5de --- /dev/null +++ b/services/core/xsd/vts/Android.bp @@ -0,0 +1,33 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_test { + name: "vts_defaultPermissions_validate_test", + srcs: [ + "ValidateDefaultPermissions.cpp" + ], + static_libs: [ + "android.hardware.audio.common.test.utility", + "libxml2", + ], + shared_libs: [ + "liblog", + ], + cflags: [ + "-Wall", + "-Werror", + ], +} diff --git a/services/core/xsd/vts/Android.mk b/services/core/xsd/vts/Android.mk new file mode 100644 index 000000000000..6dc2c437a2f9 --- /dev/null +++ b/services/core/xsd/vts/Android.mk @@ -0,0 +1,22 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := VtsValidateDefaultPermissions +include test/vts/tools/build/Android.host_config.mk diff --git a/services/core/xsd/vts/AndroidTest.xml b/services/core/xsd/vts/AndroidTest.xml new file mode 100644 index 000000000000..4f3b2ef1ace1 --- /dev/null +++ b/services/core/xsd/vts/AndroidTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Config for VTS VtsValidateDefaultPermissions."> + <option name="config-descriptor:metadata" key="plan" value="vts-treble" /> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher"> + <option name="abort-on-push-failure" value="false"/> + <option name="push-group" value="HostDrivenTest.push"/> + <option name="push" value="DATA/etc/default-permissions.xsd->/data/local/tmp/default-permissions.xsd"/> + </target_preparer> + <test class="com.android.tradefed.testtype.VtsMultiDeviceTest"> + <option name="test-module-name" value="VtsValidateDefaultPermissions"/> + <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_defaultPermissions_validate_test/vts_defaultPermissions_validate_test" /> + <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_defaultPermissions_validate_test/vts_defaultPermissions_validate_test" /> + <option name="binary-test-type" value="gtest"/> + <option name="test-timeout" value="30s"/> + </test> +</configuration> diff --git a/services/core/xsd/vts/ValidateDefaultPermissions.cpp b/services/core/xsd/vts/ValidateDefaultPermissions.cpp new file mode 100644 index 000000000000..54c115b14e37 --- /dev/null +++ b/services/core/xsd/vts/ValidateDefaultPermissions.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utility/ValidateXml.h" + +TEST(CheckConfig, mediaDefaultPermissions) { + RecordProperty("description", + "Verify that the default-permissions file " + "is valid according to the schema"); + + const char* location = "/vendor/etc/default-permissions"; + + EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS("default-permissions.xml", {location}, + "/data/local/tmp/default-permissions.xsd"); +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 8f2a2d2dae67..8f1709e4d614 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4096,6 +4096,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isSeparateProfileChallengeAllowed(int userHandle) { + if (!isCallerWithSystemUid()) { + throw new SecurityException("Caller must be system"); + } ComponentName profileOwner = getProfileOwner(userHandle); // Profile challenge is supported on N or newer release. return profileOwner != null && diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 27cd70c9a606..215e46f9aecb 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; import android.os.Binder; import android.os.Environment; +import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; @@ -209,8 +210,11 @@ class Owners { } private void pushToActivityTaskManagerLocked() { - mActivityTaskManagerInternal.setDeviceOwnerPackageName(mDeviceOwner != null - ? mDeviceOwner.packageName : null); + final int uid = mDeviceOwner != null ? mPackageManagerInternal.getPackageUid( + mDeviceOwner.packageName, + PackageManager.MATCH_ALL | PackageManager.MATCH_KNOWN_PACKAGES, mDeviceOwnerUserId) + : Process.INVALID_UID; + mActivityTaskManagerInternal.setDeviceOwnerUid(uid); } String getDeviceOwnerPackageName() { diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java index 7befd0870c00..6b5842ff9065 100644 --- a/services/net/java/android/net/NetworkStackClient.java +++ b/services/net/java/android/net/NetworkStackClient.java @@ -32,6 +32,7 @@ import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IIpClientCallbacks; import android.net.util.SharedLog; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; @@ -148,14 +149,18 @@ public class NetworkStackClient { private class NetworkStackConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { - log("Network stack service connected"); + logi("Network stack service connected"); registerNetworkStackService(service); } @Override public void onServiceDisconnected(ComponentName name) { - // TODO: crash/reboot the system ? - logWtf("Lost network stack connector", null); + // The system has lost its network stack (probably due to a crash in the + // network stack process): better crash rather than stay in a bad state where all + // networking is broken. + // onServiceDisconnected is not being called on device shutdown, so this method being + // called always indicates a bad state for the system server. + maybeCrashWithTerribleFailure("Lost network stack"); } }; @@ -211,8 +216,7 @@ public class NetworkStackClient { } if (intent == null) { - logWtf("Could not resolve the network stack", null); - // TODO: crash/reboot system server ? + maybeCrashWithTerribleFailure("Could not resolve the network stack"); return; } @@ -220,9 +224,9 @@ public class NetworkStackClient { // NetworkStackConnection.onServiceConnected(). if (!context.bindServiceAsUser(intent, new NetworkStackConnection(), Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { - logWtf("Could not bind to network stack with " + intent, null); + maybeCrashWithTerribleFailure( + "Could not bind to network stack in-process, or in app with " + intent); return; - // TODO: crash/reboot system server if no network stack after a timeout ? } log("Network stack service start requested"); @@ -270,6 +274,16 @@ public class NetworkStackClient { } } + private void maybeCrashWithTerribleFailure(@NonNull String message) { + logWtf(message, null); + if (Build.IS_DEBUGGABLE) { + throw new IllegalStateException(message); + } + } + + /** + * Log a message in the local log. + */ private void log(@NonNull String message) { synchronized (mLog) { mLog.log(message); @@ -290,6 +304,15 @@ public class NetworkStackClient { } /** + * Log a message in the local and system logs. + */ + private void logi(@NonNull String message) { + synchronized (mLog) { + mLog.i(message); + } + } + + /** * For non-system server clients, get the connector registered by the system server. */ private INetworkStackConnector getRemoteConnector() { diff --git a/services/net/java/android/net/shared/NetworkMonitorUtils.java b/services/net/java/android/net/shared/NetworkMonitorUtils.java index a17cb4647158..bb4a603ba421 100644 --- a/services/net/java/android/net/shared/NetworkMonitorUtils.java +++ b/services/net/java/android/net/shared/NetworkMonitorUtils.java @@ -21,9 +21,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; -import android.content.Context; import android.net.NetworkCapabilities; -import android.provider.Settings; /** @hide */ public class NetworkMonitorUtils { @@ -45,16 +43,6 @@ public class NetworkMonitorUtils { "android.permission.ACCESS_NETWORK_CONDITIONS"; /** - * Get the captive portal server HTTP URL that is configured on the device. - */ - public static String getCaptivePortalServerHttpUrl(Context context, String defaultUrl) { - final String settingUrl = Settings.Global.getString( - context.getContentResolver(), - Settings.Global.CAPTIVE_PORTAL_HTTP_URL); - return settingUrl != null ? settingUrl : defaultUrl; - } - - /** * Return whether validation is required for a network. * @param dfltNetCap Default requested network capabilities. * @param nc Network capabilities of the network to test. diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java index 7a68e92cff6d..c0a11b27c65c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -78,6 +78,7 @@ import android.provider.Settings; import android.util.Log; import android.util.SparseArray; +import androidx.test.filters.FlakyTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.annotations.GuardedBy; @@ -403,6 +404,7 @@ public class AlarmManagerServiceTest { assertEquals(expectedTriggerTime, mTestTimer.getElapsed()); } + @FlakyTest(bugId = 130313408) @Test public void testEarliestAlarmSet() { final PendingIntent pi6 = getNewMockPendingIntent(); diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index fce7599d0b59..01f2f6b26415 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -68,6 +68,7 @@ <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> <!-- Uses API introduced in O (26) --> <uses-sdk android:minSdkVersion="1" diff --git a/services/tests/servicestests/src/com/android/server/display/color/DisplayTransformManagerTest.java b/services/tests/servicestests/src/com/android/server/display/color/DisplayTransformManagerTest.java new file mode 100644 index 000000000000..fc74c972ed83 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/color/DisplayTransformManagerTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.color; + +import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY; +import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_DISPLAY_COLOR; +import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_SATURATION; + +import static com.google.common.truth.Truth.assertThat; + +import android.hardware.display.ColorDisplayManager; +import android.os.SystemProperties; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class DisplayTransformManagerTest { + + private DisplayTransformManager mDtm; + private float[] mNightDisplayMatrix; + + @Before + public void setUp() { + mDtm = new DisplayTransformManager(); + mNightDisplayMatrix = mDtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY); + + SystemProperties.set(PERSISTENT_PROPERTY_DISPLAY_COLOR, null); + SystemProperties.set(PERSISTENT_PROPERTY_SATURATION, null); + } + + @Test + public void setColorMode_natural() { + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) + .isEqualTo("0" /* managed */); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) + .isEqualTo("1.0" /* natural */); + } + + @Test + public void setColorMode_boosted() { + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED, mNightDisplayMatrix); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) + .isEqualTo("0" /* managed */); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) + .isEqualTo("1.1" /* boosted */); + } + + @Test + public void setColorMode_saturated() { + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED, mNightDisplayMatrix); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) + .isEqualTo("1" /* unmanaged */); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) + .isEqualTo("1.0" /* natural */); + } + + @Test + public void setColorMode_automatic() { + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC, mNightDisplayMatrix); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) + .isEqualTo("2" /* enhanced */); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) + .isEqualTo("1.0" /* natural */); + } + + @Test + public void setColorMode_vendor() { + mDtm.setColorMode(0x100, mNightDisplayMatrix); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) + .isEqualTo(Integer.toString(0x100) /* pass-through */); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) + .isEqualTo("1.0" /* default */); + } + + @Test + public void setColorMode_outOfBounds() { + mDtm.setColorMode(0x50, mNightDisplayMatrix); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR, null)) + .isEqualTo("" /* default */); + assertThat(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION, null)) + .isEqualTo("" /* default */); + } +} diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java index 3f687c81ad29..8cde10675fe3 100644 --- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java +++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java @@ -185,6 +185,9 @@ public class TestSystemImpl implements SystemInterface { public void notifyZygote(boolean enableMultiProcess) {} @Override + public void ensureZygoteStarted() {} + + @Override public boolean isMultiProcessDefaultEnabled() { return mMultiProcessDefault; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 43fe674c5d78..8aaf29a11033 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -23,9 +23,11 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -36,6 +38,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -61,6 +64,7 @@ import com.google.android.collect.Lists; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; @@ -965,6 +969,62 @@ public class ManagedServicesTest extends UiServiceTestCase { } @Test + public void testOnNullBinding() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_COMPONENT); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + service.registerSystemService(cn, 0); + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onNullBinding(cn); + return true; + }); + + service.registerSystemService(cn, 0); + assertFalse(service.isBound(cn, 0)); + } + + @Test + public void testOnServiceConnected() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_COMPONENT); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + service.registerSystemService(cn, 0); + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.registerSystemService(cn, 0); + assertTrue(service.isBound(cn, 0)); + } + + @Test public void testOnPackagesChanged_nullValuesPassed_noNullPointers() { for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index ca7a71ecad75..355ff63d18f7 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -71,8 +71,10 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Activity; import android.app.ActivityManager; import android.app.AppOpsManager; +import android.app.AutomaticZenRule; import android.app.IActivityManager; import android.app.INotificationManager; import android.app.ITransientNotification; @@ -117,6 +119,7 @@ import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; import android.service.notification.NotifyingApp; import android.service.notification.StatusBarNotification; +import android.service.notification.ZenPolicy; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; @@ -345,6 +348,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false); when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner); when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG}); + when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG}); // write to a test file; the system file isn't readable from tests mFile = new File(mContext.getCacheDir(), "test.xml"); @@ -2887,7 +2891,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testApplyEnqueuedAdjustmentFromAssistant_importance_onTime() throws Exception { + public void testApplyEnqueuedAdjustmentFromAssistant_importance() throws Exception { final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); mService.addEnqueuedNotification(r); NotificationManagerService.WorkerHandler handler = mock( @@ -2905,25 +2909,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testApplyEnqueuedAdjustmentFromAssistant_importance_tooLate() throws Exception { - final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); - mService.addNotification(r); - NotificationManagerService.WorkerHandler handler = mock( - NotificationManagerService.WorkerHandler.class); - mService.setHandler(handler); - when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true); - - Bundle signals = new Bundle(); - signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW); - Adjustment adjustment = new Adjustment( - r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier()); - mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment); - - assertEquals(IMPORTANCE_DEFAULT, r.getImportance()); - assertFalse(r.hasAdjustment(KEY_IMPORTANCE)); - } - - @Test public void testApplyEnqueuedAdjustmentFromAssistant_crossUser() throws Exception { final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); mService.addEnqueuedNotification(r); @@ -4305,6 +4290,36 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testFlagBubble() throws RemoteException { + // Bubbles are allowed! + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.areBubblesAllowed(anyString(), anyInt())).thenReturn(true); + when(mPreferencesHelper.getNotificationChannel( + anyString(), anyInt(), anyString(), anyBoolean())).thenReturn( + mTestNotificationChannel); + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn( + mTestNotificationChannel.getImportance()); + + // Notif with bubble metadata but not our other misc requirements + NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, + null /* tvExtender */, true /* isBubble */); + + // Say we're foreground + when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn( + IMPORTANCE_FOREGROUND); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifs.length); + assertTrue((notifs[0].getNotification().flags & FLAG_BUBBLE) != 0); + assertTrue(mService.getNotificationRecord( + nr.sbn.getKey()).getNotification().isBubbleNotification()); + } + + @Test public void testFlagBubbleNotifs_flag_appForeground() throws RemoteException { // Bubbles are allowed! mService.setPreferencesHelper(mPreferencesHelper); @@ -4934,22 +4949,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(1, mService.getNotificationRecordCount()); } - public void testGetAllowedAssistantCapabilities() throws Exception { - List<String> capabilities = mBinderService.getAllowedAssistantCapabilities(null); + @Test + public void testGetAllowedAssistantAdjustments() throws Exception { + List<String> capabilities = mBinderService.getAllowedAssistantAdjustments(null); assertNotNull(capabilities); for (int i = capabilities.size() - 1; i >= 0; i--) { String capability = capabilities.get(i); - mBinderService.disallowAssistantCapability(capability); - assertEquals(i + 1, mBinderService.getAllowedAssistantCapabilities(null).size()); - List<String> currentCapabilities = mBinderService.getAllowedAssistantCapabilities(null); + mBinderService.disallowAssistantAdjustment(capability); + assertEquals(i + 1, mBinderService.getAllowedAssistantAdjustments(null).size()); + List<String> currentCapabilities = mBinderService.getAllowedAssistantAdjustments(null); assertNotNull(currentCapabilities); assertFalse(currentCapabilities.contains(capability)); } } + @Test public void testAdjustRestrictedKey() throws Exception { NotificationRecord r = generateNotificationRecord(mTestNotificationChannel); + mService.addNotification(r); + when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); when(mAssistants.isAdjustmentAllowed(KEY_IMPORTANCE)).thenReturn(true); when(mAssistants.isAdjustmentAllowed(KEY_USER_SENTIMENT)).thenReturn(false); @@ -4967,6 +4986,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(USER_SENTIMENT_NEUTRAL, r.getUserSentiment()); } + @Test + public void testAutomaticZenRuleValidation_policyFilterAgreement() throws Exception { + when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) + .thenReturn(true); + mService.setZenHelper(mock(ZenModeHelper.class)); + ComponentName owner = new ComponentName(mContext, this.getClass()); + ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build(); + boolean isEnabled = true; + AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), + zenPolicy, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled); + + try { + mBinderService.addAutomaticZenRule(rule); + fail("Zen policy only applies to priority only mode"); + } catch (IllegalArgumentException e) { + // yay + } + + rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), + zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); + mBinderService.addAutomaticZenRule(rule); + + rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), + null, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled); + mBinderService.addAutomaticZenRule(rule); + } + + @Test public void testAreNotificationsEnabledForPackage_crossUser() throws Exception { try { mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), @@ -4983,6 +5030,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mUid + UserHandle.PER_USER_RANGE); } + @Test public void testAreBubblesAllowedForPackage_crossUser() throws Exception { try { mBinderService.areBubblesAllowedForPackage(mContext.getPackageName(), diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 32e96a592207..44390b0c8d43 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -57,6 +57,7 @@ import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.util.MergedConfiguration; import android.util.MutableBoolean; +import android.view.DisplayInfo; import androidx.test.filters.MediumTest; @@ -87,6 +88,10 @@ public class ActivityRecordTests extends ActivityTestsBase { doReturn(false).when(mService).isBooting(); doReturn(true).when(mService).isBooted(); + + final DisplayContent displayContent = mStack.getDisplay().mDisplayContent; + doReturn(mock(DisplayPolicy.class)).when(displayContent).getDisplayPolicy(); + doReturn(mock(DisplayInfo.class)).when(displayContent).getDisplayInfo(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index d02db7b2af22..44aa55d07133 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -659,7 +659,7 @@ public class ActivityStarterTests extends ActivityTestsBase { boolean hasForegroundActivities, boolean callerIsRecents, boolean callerIsTempWhitelisted, boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges, - boolean isCallingPackageNameDeviceOwner, boolean isCallingPackageTempWhitelisted) { + boolean isCallingUidDeviceOwner, boolean isCallingPackageTempWhitelisted) { // window visibility doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager.mRoot) .isAnyNonToastWindowVisibleForUid(callingUid); @@ -685,8 +685,8 @@ public class ActivityStarterTests extends ActivityTestsBase { // caller is instrumenting with background activity starts privileges callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges, callerIsInstrumentingWithBackgroundActivityStartPrivileges); - // calling package name is the device owner - doReturn(isCallingPackageNameDeviceOwner).when(mService).isDeviceOwner(any()); + // callingUid is the device owner + doReturn(isCallingUidDeviceOwner).when(mService).isDeviceOwner(callingUid); // calling package name is temporarily whitelisted doReturn(isCallingPackageTempWhitelisted).when(mService) .isPackageNameWhitelistedForBgActivityStarts("com.whatever.dude"); diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java index 9ce579512eda..beec1a8b8942 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java @@ -18,6 +18,8 @@ package com.android.server.wm; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.server.wm.BoundsAnimationController.BOUNDS; +import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; @@ -131,6 +133,8 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { boolean mCancelRequested; Rect mStackBounds; Rect mTaskBounds; + float mAlpha; + @BoundsAnimationController.AnimationType int mAnimationType; void initialize(Rect from) { mAwaitingAnimationStart = true; @@ -148,11 +152,12 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @Override public boolean onAnimationStart(boolean schedulePipModeChangedCallback, - boolean forceUpdate) { + boolean forceUpdate, @BoundsAnimationController.AnimationType int animationType) { mAwaitingAnimationStart = false; mAnimationStarted = true; mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback; mForcePipModeChangedCallback = forceUpdate; + mAnimationType = animationType; return true; } @@ -185,6 +190,12 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { mMovedToFullscreen = moveToFullscreen; mTaskBounds = null; } + + @Override + public boolean setPinnedStackAlpha(float alpha) { + mAlpha = alpha; + return true; + } } /** @@ -201,6 +212,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { private Rect mTo; private Rect mLargerBounds; private Rect mExpectedFinalBounds; + private @BoundsAnimationController.AnimationType int mAnimationType; BoundsAnimationDriver(BoundsAnimationController controller, TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) { @@ -209,7 +221,8 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { mMockAnimator = mockValueAnimator; } - BoundsAnimationDriver start(Rect from, Rect to) { + BoundsAnimationDriver start(Rect from, Rect to, + @BoundsAnimationController.AnimationType int animationType) { if (mAnimator != null) { throw new IllegalArgumentException("Call restart() to restart an animation"); } @@ -223,7 +236,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { assertTrue(mTarget.mAwaitingAnimationStart); assertFalse(mTarget.mAnimationStarted); - startImpl(from, to); + startImpl(from, to, animationType); // Ensure that the animator is paused for the all windows drawn signal when animating // to/from fullscreen @@ -253,7 +266,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { mTarget.mAnimationStarted = false; // Start animation - startImpl(mTarget.mStackBounds, to); + startImpl(mTarget.mStackBounds, to, BOUNDS); if (toSameBounds) { // Same animator if same final bounds @@ -273,13 +286,15 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { return this; } - private BoundsAnimationDriver startImpl(Rect from, Rect to) { + private BoundsAnimationDriver startImpl(Rect from, Rect to, + @BoundsAnimationController.AnimationType int animationType) { boolean fromFullscreen = from.equals(BOUNDS_FULL); boolean toFullscreen = to.equals(BOUNDS_FULL); mFrom = new Rect(from); mTo = new Rect(to); mExpectedFinalBounds = new Rect(to); mLargerBounds = getLargerBounds(mFrom, mTo); + mAnimationType = animationType; // Start animation final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen @@ -288,17 +303,19 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { ? SCHEDULE_PIP_MODE_CHANGED_ON_END : NO_PIP_MODE_CHANGED_CALLBACKS; mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION, - schedulePipModeChangedState, fromFullscreen, toFullscreen); - - // Original stack bounds, frozen task bounds - assertEquals(mFrom, mTarget.mStackBounds); - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); + schedulePipModeChangedState, fromFullscreen, toFullscreen, animationType); - // Animating to larger size - if (mFrom.equals(mLargerBounds)) { - assertFalse(mAnimator.animatingToLargerSize()); - } else if (mTo.equals(mLargerBounds)) { - assertTrue(mAnimator.animatingToLargerSize()); + if (animationType == BOUNDS) { + // Original stack bounds, frozen task bounds + assertEquals(mFrom, mTarget.mStackBounds); + assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); + + // Animating to larger size + if (mFrom.equals(mLargerBounds)) { + assertFalse(mAnimator.animatingToLargerSize()); + } else if (mTo.equals(mLargerBounds)) { + assertTrue(mAnimator.animatingToLargerSize()); + } } return this; @@ -315,16 +332,20 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { BoundsAnimationDriver update(float t) { mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t)); - // Temporary stack bounds, frozen task bounds - if (t == 0f) { - assertEquals(mFrom, mTarget.mStackBounds); - } else if (t == 1f) { - assertEquals(mTo, mTarget.mStackBounds); + if (mAnimationType == BOUNDS) { + // Temporary stack bounds, frozen task bounds + if (t == 0f) { + assertEquals(mFrom, mTarget.mStackBounds); + } else if (t == 1f) { + assertEquals(mTo, mTarget.mStackBounds); + } else { + assertNotEquals(mFrom, mTarget.mStackBounds); + assertNotEquals(mTo, mTarget.mStackBounds); + } + assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); } else { - assertNotEquals(mFrom, mTarget.mStackBounds); - assertNotEquals(mTo, mTarget.mStackBounds); + assertEquals((float) mMockAnimator.getAnimatedValue(), mTarget.mAlpha, 0.01f); } - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); return this; } @@ -353,10 +374,14 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { BoundsAnimationDriver end() { mAnimator.end(); - // Final stack bounds - assertEquals(mTo, mTarget.mStackBounds); - assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds); - assertNull(mTarget.mTaskBounds); + if (mAnimationType == BOUNDS) { + // Final stack bounds + assertEquals(mTo, mTarget.mStackBounds); + assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds); + assertNull(mTarget.mTaskBounds); + } else { + assertEquals(mTarget.mAlpha, 1f, 0.01f); + } return this; } @@ -413,7 +438,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFullscreenToFloatingTransition() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0f) .update(0.5f) @@ -425,7 +450,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToFullscreenTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) .expectStarted(SCHEDULE_PIP_MODE_CHANGED) .update(0f) .update(0.5f) @@ -437,7 +462,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToSmallerFloatingTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING) + mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0f) .update(0.5f) @@ -449,7 +474,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToLargerFloatingTransition() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING) + mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0f) .update(0.5f) @@ -463,7 +488,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFullscreenToFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .cancel() @@ -473,7 +498,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFullscreenToFloatingCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */) @@ -484,7 +509,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .restart(BOUNDS_SMALLER_FLOATING, @@ -498,7 +523,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() { // When animating from fullscreen and the animation is interruped, we expect the animation // start callback to be made, with a forced pip mode change callback - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */) @@ -511,7 +536,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToFullscreenCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) .expectStarted(SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .cancel() @@ -521,7 +546,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToFullscreenCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) .expectStarted(SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */) @@ -532,7 +557,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) .expectStarted(SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .restart(BOUNDS_SMALLER_FLOATING, @@ -546,7 +571,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToSmallerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING) + mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .cancel() @@ -556,13 +581,25 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { @UiThreadTest @Test public void testFloatingToLargerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING) + mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS) .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) .update(0.25f) .cancel() .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); } + @UiThreadTest + @Test + public void testFadeIn() { + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, FADE_IN) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0f) + .update(0.5f) + .update(1f) + .end() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + /** MISC **/ @UiThreadTest @@ -570,7 +607,7 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { public void testBoundsAreCopied() { Rect from = new Rect(0, 0, 100, 100); Rect to = new Rect(25, 25, 75, 75); - mDriver.start(from, to) + mDriver.start(from, to, BOUNDS) .update(0.25f) .end(); assertEquals(new Rect(0, 0, 100, 100), from); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index e392353a8875..0c2ce614b772 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -163,7 +163,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { // Assume IRecentsAnimationController#cleanupScreenshot called to finish screenshot // animation. mController.mRecentScreenshotAnimator.cancelAnimation(); - verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true); + verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false); } private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index 5625ea42726f..f615823a645c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -71,6 +71,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testCancelAnimationOnVisibleStackOrderChange() { ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay(); + display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class); ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mService) diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index dc307b547a63..d87eed26b211 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -25,7 +25,10 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEFAULT; +import static android.view.Surface.ROTATION_0; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.hamcrest.Matchers.not; @@ -35,6 +38,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import android.app.ActivityManager; @@ -357,6 +362,7 @@ public class TaskRecordTests extends ActivityTestsBase { parentConfig.densityDpi = 400; parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px + parentConfig.windowConfiguration.setRotation(ROTATION_0); // Portrait bounds. inOutConfig.windowConfiguration.getBounds().set(0, 0, shortSide, longSide); @@ -370,12 +376,27 @@ public class TaskRecordTests extends ActivityTestsBase { inOutConfig.setToDefaults(); // Landscape bounds. inOutConfig.windowConfiguration.getBounds().set(0, 0, longSide, shortSide); + + // Setup the display with a top stable inset. The later assertion will ensure the inset is + // excluded from screenHeightDp. + final int statusBarHeight = 100; + final DisplayContent displayContent = mock(DisplayContent.class); + final DisplayPolicy policy = mock(DisplayPolicy.class); + doAnswer(invocationOnMock -> { + final Rect insets = invocationOnMock.<Rect>getArgument(0); + insets.top = statusBarHeight; + return null; + }).when(policy).convertNonDecorInsetsToStableInsets(any(), eq(ROTATION_0)); + doReturn(policy).when(displayContent).getDisplayPolicy(); + doReturn(mock(DisplayInfo.class)).when(displayContent).getDisplayInfo(); + // Without limiting to be inside the parent bounds, the out screen size should keep relative // to the input bounds. - task.computeConfigResourceOverrides(inOutConfig, parentConfig, - false /* insideParentBounds */); + final ActivityRecord.CompatDisplayInsets compatIntsets = + new ActivityRecord.CompatDisplayInsets(displayContent); + task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets); - assertEquals(shortSide * DENSITY_DEFAULT / parentConfig.densityDpi, + assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi, inOutConfig.screenHeightDp); assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi, inOutConfig.screenWidthDp); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index ca815eca9a1f..cb6dc6d42bd5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -30,6 +30,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; +import android.app.ActivityManager.TaskDescription; import android.app.ActivityManager.TaskSnapshot; import android.content.ComponentName; import android.graphics.Canvas; @@ -38,7 +39,6 @@ import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; -import android.view.Surface; import android.view.SurfaceControl; import androidx.test.filters.SmallTest; @@ -67,8 +67,17 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */); mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test", - Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds, - ORIENTATION_PORTRAIT); + createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0, + taskBounds, ORIENTATION_PORTRAIT); + } + + private static TaskDescription createTaskDescription(int background, int statusBar, + int navigationBar) { + final TaskDescription td = new TaskDescription(); + td.setBackgroundColor(background); + td.setStatusBarColor(statusBar); + td.setNavigationBarColor(navigationBar); + return td; } private void setupSurface(int width, int height) { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java index 1eb716a63935..9722d2ccce99 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java @@ -207,7 +207,8 @@ public class TaskStackChangedListenerTest { // Test for onTaskMovedToFront. assertEquals(1, taskMovedToFrontLatch.getCount()); - mService.moveTaskToFront(id, 0, null); + mService.moveTaskToFront(null, getInstrumentation().getContext().getPackageName(), id, 0, + null); waitForCallback(taskMovedToFrontLatch); assertEquals(activity.getTaskId(), params[0]); diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java index 37fab09cd745..36c637723c0a 100644 --- a/telecomm/java/android/telecom/CallRedirectionService.java +++ b/telecomm/java/android/telecom/CallRedirectionService.java @@ -119,18 +119,18 @@ public abstract class CallRedirectionService extends Service { * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. The response corresponds to the * latest request via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}. * - * @param handle the new phone number to dial + * @param gatewayUri the gateway uri for call redirection. * @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call. * @param confirmFirst Telecom will ask users to confirm the redirection via a yes/no dialog * if the confirmFirst is true, and if the redirection request of this * response was sent with a true flag of allowInteractiveResponse via * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} */ - public final void redirectCall(@NonNull Uri handle, + public final void redirectCall(@NonNull Uri gatewayUri, @NonNull PhoneAccountHandle targetPhoneAccount, boolean confirmFirst) { try { - mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount, confirmFirst); + mCallRedirectionAdapter.redirectCall(gatewayUri, targetPhoneAccount, confirmFirst); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java index 52e0ebd334b5..2d8a8cbae59f 100644 --- a/telephony/java/android/provider/Telephony.java +++ b/telephony/java/android/provider/Telephony.java @@ -4321,6 +4321,22 @@ public final class Telephony { * @hide */ public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation"; + + /** + * The current registered raw data network operator name in long alphanumeric format. + * <p> + * This is the same as {@link ServiceState#getOperatorAlphaLongRaw()}. + * @hide + */ + public static final String OPERATOR_ALPHA_LONG_RAW = "operator_alpha_long_raw"; + + /** + * The current registered raw data network operator name in short alphanumeric format. + * <p> + * This is the same as {@link ServiceState#getOperatorAlphaShortRaw()}. + * @hide + */ + public static final String OPERATOR_ALPHA_SHORT_RAW = "operator_alpha_short_raw"; } /** diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 0f8f873b6847..9f6528bc4709 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1312,6 +1312,24 @@ public class CarrierConfigManager { "hide_lte_plus_data_icon_bool"; /** + * The string is used to filter redundant string from PLMN Network Name that's supplied by + * specific carrier. + * + * @hide + */ + public static final String KEY_OPERATOR_NAME_FILTER_PATTERN_STRING = + "operator_name_filter_pattern_string"; + + /** + * The string is used to compare with operator name. If it matches the pattern then show + * specific data icon. + * + * @hide + */ + public static final String KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING = + "show_carrier_data_icon_pattern_string"; + + /** * Boolean to decide whether to show precise call failed cause to user * @hide */ @@ -2803,6 +2821,19 @@ public class CarrierConfigManager { public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL = "key_is_opportunistic_subscription_bool"; + /** + * A list of 4 GSM RSSI thresholds above which a signal level is considered POOR, + * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting. + * + * Note that the min and max thresholds are fixed at -113 and -51, as set in 3GPP TS 27.007 + * section 8.5. + * <p> + * See CellSignalStrengthGsm#GSM_RSSI_MAX and CellSignalStrengthGsm#GSM_RSSI_MIN. Any signal + * level outside these boundaries is considered invalid. + * @hide + */ + public static final String KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY = + "gsm_rssi_thresholds_int_array"; /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -3139,6 +3170,8 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false); sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false); sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, false); + sDefaults.putString(KEY_OPERATOR_NAME_FILTER_PATTERN_STRING, ""); + sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, ""); sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true); sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_TDSCDMA_BOOL, false); @@ -3204,6 +3237,13 @@ public class CarrierConfigManager { false); sDefaults.putString(KEY_SUBSCRIPTION_GROUP_UUID_STRING, ""); sDefaults.putBoolean(KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL, false); + sDefaults.putIntArray(KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY, + new int[] { + -107, /* SIGNAL_STRENGTH_POOR */ + -103, /* SIGNAL_STRENGTH_MODERATE */ + -97, /* SIGNAL_STRENGTH_GOOD */ + -89, /* SIGNAL_STRENGTH_GREAT */ + }); } /** diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java index 374527749354..30875165867a 100644 --- a/telephony/java/android/telephony/CellIdentity.java +++ b/telephony/java/android/telephony/CellIdentity.java @@ -49,10 +49,10 @@ public abstract class CellIdentity implements Parcelable { // long alpha Operator Name String or Enhanced Operator Name String /** @hide */ - protected final String mAlphaLong; + protected String mAlphaLong; // short alpha Operator Name String or Enhanced Operator Name String /** @hide */ - protected final String mAlphaShort; + protected String mAlphaShort; /** @hide */ protected CellIdentity(String tag, int type, String mcc, String mnc, String alphal, @@ -145,6 +145,13 @@ public abstract class CellIdentity implements Parcelable { } /** + * @hide + */ + public void setOperatorAlphaLong(String alphaLong) { + mAlphaLong = alphaLong; + } + + /** * @return The short alpha tag associated with the current scan result (may be the operator * name string or extended operator name string). May be null if unknown. */ @@ -154,6 +161,13 @@ public abstract class CellIdentity implements Parcelable { } /** + * @hide + */ + public void setOperatorAlphaShort(String alphaShort) { + mAlphaShort = alphaShort; + } + + /** * @return a CellLocation object for this CellIdentity * @hide */ diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java index 5e44bf2e37a8..864540d91be3 100644 --- a/telephony/java/android/telephony/CellIdentityGsm.java +++ b/telephony/java/android/telephony/CellIdentityGsm.java @@ -166,6 +166,7 @@ public final class CellIdentityGsm extends CellIdentity { /** * @return Mobile Country Code in string format, null if unavailable. */ + @Nullable public String getMccString() { return mMccStr; } @@ -173,6 +174,7 @@ public final class CellIdentityGsm extends CellIdentity { /** * @return Mobile Network Code in string format, null if unavailable. */ + @Nullable public String getMncString() { return mMncStr; } diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index 2dd72d6ea69c..14503c7cdd4d 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -187,6 +187,7 @@ public final class CellIdentityLte extends CellIdentity { /** * @return Mobile Country Code in string format, null if unavailable. */ + @Nullable public String getMccString() { return mMccStr; } @@ -194,6 +195,7 @@ public final class CellIdentityLte extends CellIdentity { /** * @return Mobile Network Code in string format, null if unavailable. */ + @Nullable public String getMncString() { return mMncStr; } diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java index a591bd15f95f..937de706aec0 100644 --- a/telephony/java/android/telephony/CellIdentityTdscdma.java +++ b/telephony/java/android/telephony/CellIdentityTdscdma.java @@ -104,6 +104,7 @@ public final class CellIdentityTdscdma extends CellIdentity { * Get Mobile Country Code in string format * @return Mobile Country Code in string format, null if unknown */ + @Nullable public String getMccString() { return mMccStr; } @@ -112,6 +113,7 @@ public final class CellIdentityTdscdma extends CellIdentity { * Get Mobile Network Code in string format * @return Mobile Network Code in string format, null if unknown */ + @Nullable public String getMncString() { return mMncStr; } diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java index 674c40c2d36f..b4a2ead7fc3d 100644 --- a/telephony/java/android/telephony/CellIdentityWcdma.java +++ b/telephony/java/android/telephony/CellIdentityWcdma.java @@ -150,6 +150,7 @@ public final class CellIdentityWcdma extends CellIdentity { /** * @return Mobile Country Code in string version, null if unavailable. */ + @Nullable public String getMccString() { return mMccStr; } @@ -157,6 +158,7 @@ public final class CellIdentityWcdma extends CellIdentity { /** * @return Mobile Network Code in string version, null if unavailable. */ + @Nullable public String getMncString() { return mMncStr; } diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java index a4570e41dc26..30b131faf51d 100644 --- a/telephony/java/android/telephony/CellInfoCdma.java +++ b/telephony/java/android/telephony/CellInfoCdma.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -76,18 +77,25 @@ public final class CellInfoCdma extends CellInfo implements Parcelable { new CellSignalStrengthCdma(cic.signalStrengthCdma, cic.signalStrengthEvdo); } + /** + * @return a {@link CellIdentityCdma} instance. + */ @Override - public CellIdentityCdma getCellIdentity() { + public @NonNull CellIdentityCdma getCellIdentity() { return mCellIdentityCdma; } + /** @hide */ @UnsupportedAppUsage public void setCellIdentity(CellIdentityCdma cid) { mCellIdentityCdma = cid; } + /** + * @return a {@link CellSignalStrengthCdma} instance. + */ @Override - public CellSignalStrengthCdma getCellSignalStrength() { + public @NonNull CellSignalStrengthCdma getCellSignalStrength() { return mCellSignalStrengthCdma; } diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java index ce32bc1b9cb7..137f97eeee62 100644 --- a/telephony/java/android/telephony/CellInfoGsm.java +++ b/telephony/java/android/telephony/CellInfoGsm.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -71,17 +72,24 @@ public final class CellInfoGsm extends CellInfo implements Parcelable { mCellSignalStrengthGsm = new CellSignalStrengthGsm(cig.signalStrengthGsm); } + /** + * @return a {@link CellIdentityGsm} instance. + */ @Override - public CellIdentityGsm getCellIdentity() { + public @NonNull CellIdentityGsm getCellIdentity() { return mCellIdentityGsm; } + /** @hide */ public void setCellIdentity(CellIdentityGsm cid) { mCellIdentityGsm = cid; } + /** + * @return a {@link CellSignalStrengthGsm} instance. + */ @Override - public CellSignalStrengthGsm getCellSignalStrength() { + public @NonNull CellSignalStrengthGsm getCellSignalStrength() { return mCellSignalStrengthGsm; } diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java index 01ee20a7fa1e..da7b7ab1488d 100644 --- a/telephony/java/android/telephony/CellInfoLte.java +++ b/telephony/java/android/telephony/CellInfoLte.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.NonNull; import android.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -79,11 +80,15 @@ public final class CellInfoLte extends CellInfo implements Parcelable { mCellConfig = new CellConfigLte(cil.cellConfig); } + /** + * @return a {@link CellIdentityLte} instance. + */ @Override - public CellIdentityLte getCellIdentity() { + public @NonNull CellIdentityLte getCellIdentity() { if (DBG) log("getCellIdentity: " + mCellIdentityLte); return mCellIdentityLte; } + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public void setCellIdentity(CellIdentityLte cid) { @@ -91,8 +96,11 @@ public final class CellInfoLte extends CellInfo implements Parcelable { mCellIdentityLte = cid; } + /** + * @return a {@link CellSignalStrengthLte} instance. + */ @Override - public CellSignalStrengthLte getCellSignalStrength() { + public @NonNull CellSignalStrengthLte getCellSignalStrength() { if (DBG) log("getCellSignalStrength: " + mCellSignalStrengthLte); return mCellSignalStrengthLte; } diff --git a/telephony/java/android/telephony/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java index ba4a907fdce8..9775abd5075c 100644 --- a/telephony/java/android/telephony/CellInfoNr.java +++ b/telephony/java/android/telephony/CellInfoNr.java @@ -43,12 +43,18 @@ public final class CellInfoNr extends CellInfo { mCellSignalStrength = other.mCellSignalStrength; } + /** + * @return a {@link CellIdentityNr} instance. + */ @Override @NonNull public CellIdentity getCellIdentity() { return mCellIdentity; } + /** + * @return a {@link CellSignalStrengthNr} instance. + */ @Override @NonNull public CellSignalStrength getCellSignalStrength() { diff --git a/telephony/java/android/telephony/CellInfoTdscdma.java b/telephony/java/android/telephony/CellInfoTdscdma.java index ccafda61a177..f1305f5ca768 100644 --- a/telephony/java/android/telephony/CellInfoTdscdma.java +++ b/telephony/java/android/telephony/CellInfoTdscdma.java @@ -75,6 +75,9 @@ public final class CellInfoTdscdma extends CellInfo implements Parcelable { mCellSignalStrengthTdscdma = new CellSignalStrengthTdscdma(cit.signalStrengthTdscdma); } + /** + * @return a {@link CellIdentityTdscdma} instance. + */ @Override public @NonNull CellIdentityTdscdma getCellIdentity() { return mCellIdentityTdscdma; @@ -85,6 +88,9 @@ public final class CellInfoTdscdma extends CellInfo implements Parcelable { mCellIdentityTdscdma = cid; } + /** + * @return a {@link CellSignalStrengthTdscdma} instance. + */ @Override public @NonNull CellSignalStrengthTdscdma getCellSignalStrength() { return mCellSignalStrengthTdscdma; diff --git a/telephony/java/android/telephony/CellInfoWcdma.java b/telephony/java/android/telephony/CellInfoWcdma.java index 1b32178db337..ee5fec838d2d 100644 --- a/telephony/java/android/telephony/CellInfoWcdma.java +++ b/telephony/java/android/telephony/CellInfoWcdma.java @@ -71,15 +71,22 @@ public final class CellInfoWcdma extends CellInfo implements Parcelable { mCellSignalStrengthWcdma = new CellSignalStrengthWcdma(ciw.signalStrengthWcdma); } + /** + * @return a {@link CellIdentityWcdma} instance. + */ @Override public CellIdentityWcdma getCellIdentity() { return mCellIdentityWcdma; } + /** @hide */ public void setCellIdentity(CellIdentityWcdma cid) { mCellIdentityWcdma = cid; } + /** + * @return a {@link CellSignalStrengthWcdma} instance. + */ @Override public CellSignalStrengthWcdma getCellSignalStrength() { return mCellSignalStrengthWcdma; diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java index 740b970b8e7c..e65b048ec0a5 100644 --- a/telephony/java/android/telephony/CellSignalStrength.java +++ b/telephony/java/android/telephony/CellSignalStrength.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.IntRange; import android.os.PersistableBundle; /** @@ -57,23 +58,24 @@ public abstract class CellSignalStrength { public abstract void setDefaultValues(); /** - * Get signal level as an int from 0..4 - * <p> - * @see #SIGNAL_STRENGTH_NONE_OR_UNKNOWN - * @see #SIGNAL_STRENGTH_POOR - * @see #SIGNAL_STRENGTH_MODERATE - * @see #SIGNAL_STRENGTH_GOOD - * @see #SIGNAL_STRENGTH_GREAT + * Retrieve an abstract level value for the overall signal quality. + * + * @return a single integer from 0 to 4 representing the general signal quality. + * 0 represents very poor or unknown signal quality while 4 represents excellent + * signal quality. */ + @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT) public abstract int getLevel(); /** - * Get the signal level as an asu value between 0..31, 99 is unknown + * Get the technology-specific signal strength in Arbitrary Strength Units, calculated from the + * strength of the pilot signal or equivalent. */ public abstract int getAsuLevel(); /** - * Get the signal strength as dBm + * Get the technology-specific signal strength in dBm, which is the signal strength of the + * pilot signal or equivalent. */ public abstract int getDbm(); diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java index 5b195999078c..199843905854 100644 --- a/telephony/java/android/telephony/CellSignalStrengthCdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.IntRange; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -114,13 +115,9 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; } - /** - * Retrieve an abstract level value for the overall signal strength. - * - * @return a single integer from 0 to 4 representing the general signal quality. - * 0 represents very poor signal strength while 4 represents a very strong signal strength. - */ + /** {@inheritDoc} */ @Override + @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT) public int getLevel() { return mLevel; } diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index 0aeb0f6e66d8..14ae68981745 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.IntRange; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -37,6 +38,10 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P private static final int GSM_RSSI_GOOD = -97; private static final int GSM_RSSI_MODERATE = -103; private static final int GSM_RSSI_POOR = -107; + private static final int GSM_RSSI_MIN = -113; + + private static final int[] sRssiThresholds = new int[] { + GSM_RSSI_POOR, GSM_RSSI_MODERATE, GSM_RSSI_GOOD, GSM_RSSI_GREAT}; private int mRssi; // in dBm [-113, -51] or UNAVAILABLE @UnsupportedAppUsage @@ -53,7 +58,7 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P /** @hide */ public CellSignalStrengthGsm(int rssi, int ber, int ta) { - mRssi = inRangeOrUnavailable(rssi, -113, -51); + mRssi = inRangeOrUnavailable(rssi, GSM_RSSI_MIN, GSM_RSSI_MAX); mBitErrorRate = inRangeOrUnavailable(ber, 0, 7, 99); mTimingAdvance = inRangeOrUnavailable(ta, 0, 219); updateLevel(null, null); @@ -97,13 +102,9 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; } - /** - * Retrieve an abstract level value for the overall signal strength. - * - * @return a single integer from 0 to 4 representing the general signal quality. - * 0 represents very poor signal strength while 4 represents a very strong signal strength. - */ + /** {@inheritDoc} */ @Override + @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT) public int getLevel() { return mLevel; } @@ -111,12 +112,22 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P /** @hide */ @Override public void updateLevel(PersistableBundle cc, ServiceState ss) { - if (mRssi > GSM_RSSI_MAX) mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; - else if (mRssi >= GSM_RSSI_GREAT) mLevel = SIGNAL_STRENGTH_GREAT; - else if (mRssi >= GSM_RSSI_GOOD) mLevel = SIGNAL_STRENGTH_GOOD; - else if (mRssi >= GSM_RSSI_MODERATE) mLevel = SIGNAL_STRENGTH_MODERATE; - else if (mRssi >= GSM_RSSI_POOR) mLevel = SIGNAL_STRENGTH_POOR; - else mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + int[] rssiThresholds; + if (cc == null) { + rssiThresholds = sRssiThresholds; + } else { + rssiThresholds = cc.getIntArray(CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY); + if (rssiThresholds == null || rssiThresholds.length != NUM_SIGNAL_STRENGTH_THRESHOLDS) { + rssiThresholds = sRssiThresholds; + } + } + int level = NUM_SIGNAL_STRENGTH_THRESHOLDS; + if (mRssi < GSM_RSSI_MIN || mRssi > GSM_RSSI_MAX) { + mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + return; + } + while (level > 0 && mRssi < rssiThresholds[level - 1]) level--; + mLevel = level; } /** @@ -141,7 +152,7 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P /** * Get the RSSI in ASU. * - * Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69 + * Asu is calculated based on 3GPP RSSI. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69 * * @return RSSI in ASU 0..31, 99, or UNAVAILABLE */ diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java index 5687adaabed5..2272dc9071ea 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.IntRange; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -145,13 +146,9 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; } - /** - * Retrieve an abstract level value for the overall signal strength. - * - * @return a single integer from 0 to 4 representing the general signal quality. - * 0 represents very poor signal strength while 4 represents a very strong signal strength. - */ + /** {@inheritDoc} */ @Override + @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT) public int getLevel() { return mLevel; } diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index fff3adf04f7b..1912c60ac122 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.IntRange; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -183,7 +184,9 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; } + /** {@inheritDoc} */ @Override + @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT) public int getLevel() { return mLevel; } @@ -227,6 +230,9 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa return asuLevel; } + /** + * Get the CSI-RSRP as dBm value -140..-44dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}. + */ @Override public int getDbm() { return mCsiRsrp; diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java index b562f3270fba..f4a3dbb37988 100644 --- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.IntRange; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; @@ -121,13 +122,10 @@ public final class CellSignalStrengthTdscdma extends CellSignalStrength implemen mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; } - /** - * Retrieve an abstract level value for the overall signal strength. - * - * @return a single integer from 0 to 4 representing the general signal quality. - * 0 represents very poor signal strength while 4 represents a very strong signal strength. - */ + + /** {@inheritDoc} */ @Override + @IntRange(from = 0, to = 4) public int getLevel() { return mLevel; } @@ -144,7 +142,7 @@ public final class CellSignalStrengthTdscdma extends CellSignalStrength implemen } /** - * Get the signal strength as dBm + * Get the RSCP as dBm value -120..-24dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}. */ @Override public int getDbm() { diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java index 8efc0f281543..169325276821 100644 --- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.IntRange; import android.annotation.StringDef; import android.os.Parcel; import android.os.Parcelable; @@ -143,13 +144,9 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; } - /** - * Retrieve an abstract level value for the overall signal strength. - * - * @return a single integer from 0 to 4 representing the general signal quality. - * 0 represents very poor signal strength while 4 represents a very strong signal strength. - */ + /** {@inheritDoc} */ @Override + @IntRange(from = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to = SIGNAL_STRENGTH_GREAT) public int getLevel() { return mLevel; } @@ -202,7 +199,7 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements } /** - * Get the signal strength as dBm + * Get the RSCP as dBm value -120..-24dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}. */ @Override public int getDbm() { diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 8c92e84b45b6..1a160f4f57a6 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -339,6 +339,9 @@ public class ServiceState implements Parcelable { private List<NetworkRegistrationInfo> mNetworkRegistrationInfos = new ArrayList<>(); + private String mOperatorAlphaLongRaw; + private String mOperatorAlphaShortRaw; + /** * get String description of roaming type * @hide @@ -420,6 +423,8 @@ public class ServiceState implements Parcelable { mNetworkRegistrationInfos = s.mNetworkRegistrationInfos == null ? null : new ArrayList<>(s.mNetworkRegistrationInfos); mNrFrequencyRange = s.mNrFrequencyRange; + mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw; + mOperatorAlphaShortRaw = s.mOperatorAlphaShortRaw; } /** @@ -453,6 +458,8 @@ public class ServiceState implements Parcelable { mChannelNumber = in.readInt(); mCellBandwidths = in.createIntArray(); mNrFrequencyRange = in.readInt(); + mOperatorAlphaLongRaw = in.readString(); + mOperatorAlphaShortRaw = in.readString(); } public void writeToParcel(Parcel out, int flags) { @@ -478,6 +485,8 @@ public class ServiceState implements Parcelable { out.writeInt(mChannelNumber); out.writeIntArray(mCellBandwidths); out.writeInt(mNrFrequencyRange); + out.writeString(mOperatorAlphaLongRaw); + out.writeString(mOperatorAlphaShortRaw); } public int describeContents() { @@ -836,7 +845,9 @@ public class ServiceState implements Parcelable { mIsEmergencyOnly, mLteEarfcnRsrpBoost, mNetworkRegistrationInfos, - mNrFrequencyRange); + mNrFrequencyRange, + mOperatorAlphaLongRaw, + mOperatorAlphaShortRaw); } @Override @@ -862,6 +873,8 @@ public class ServiceState implements Parcelable { && equalsHandlesNulls(mCdmaDefaultRoamingIndicator, s.mCdmaDefaultRoamingIndicator) && mIsEmergencyOnly == s.mIsEmergencyOnly + && equalsHandlesNulls(mOperatorAlphaLongRaw, s.mOperatorAlphaLongRaw) + && equalsHandlesNulls(mOperatorAlphaShortRaw, s.mOperatorAlphaShortRaw) && (mNetworkRegistrationInfos == null ? s.mNetworkRegistrationInfos == null : s.mNetworkRegistrationInfos != null && mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos)) @@ -1019,6 +1032,8 @@ public class ServiceState implements Parcelable { .append(", mLteEarfcnRsrpBoost=").append(mLteEarfcnRsrpBoost) .append(", mNetworkRegistrationInfos=").append(mNetworkRegistrationInfos) .append(", mNrFrequencyRange=").append(mNrFrequencyRange) + .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw) + .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw) .append("}").toString(); } @@ -1056,6 +1071,8 @@ public class ServiceState implements Parcelable { .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN) .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN) .build()); + mOperatorAlphaLongRaw = null; + mOperatorAlphaShortRaw = null; } public void setStateOutOfService() { @@ -1297,6 +1314,8 @@ public class ServiceState implements Parcelable { m.putInt("ChannelNumber", mChannelNumber); m.putIntArray("CellBandwidths", mCellBandwidths); m.putInt("mNrFrequencyRange", mNrFrequencyRange); + m.putString("operator-alpha-long-raw", mOperatorAlphaLongRaw); + m.putString("operator-alpha-short-raw", mOperatorAlphaShortRaw); } /** @hide */ @@ -1906,4 +1925,36 @@ public class ServiceState implements Parcelable { return state; } + + /** + * @hide + */ + public void setOperatorAlphaLongRaw(String operatorAlphaLong) { + mOperatorAlphaLongRaw = operatorAlphaLong; + } + + /** + * The current registered raw data network operator name in long alphanumeric format. + * + * @hide + */ + public String getOperatorAlphaLongRaw() { + return mOperatorAlphaLongRaw; + } + + /** + * @hide + */ + public void setOperatorAlphaShortRaw(String operatorAlphaShort) { + mOperatorAlphaShortRaw = operatorAlphaShort; + } + + /** + * The current registered raw data network operator name in short alphanumeric format. + * + * @hide + */ + public String getOperatorAlphaShortRaw() { + return mOperatorAlphaShortRaw; + } } diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index ee28ca23be35..cf15b92ae640 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -87,8 +87,8 @@ public class SubscriptionInfo implements Parcelable { private int mCarrierId; /** - * The source of the name, NAME_SOURCE_UNDEFINED, NAME_SOURCE_DEFAULT_SOURCE, - * NAME_SOURCE_SIM_SOURCE or NAME_SOURCE_USER_INPUT. + * The source of the name, NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SOURCE or + * NAME_SOURCE_USER_INPUT. */ private int mNameSource; @@ -103,7 +103,7 @@ public class SubscriptionInfo implements Parcelable { private String mNumber; /** - * Data roaming state, DATA_RAOMING_ENABLE, DATA_RAOMING_DISABLE + * Data roaming state, DATA_ROAMING_ENABLE, DATA_ROAMING_DISABLE */ private int mDataRoaming; @@ -306,8 +306,8 @@ public class SubscriptionInfo implements Parcelable { } /** - * @return the source of the name, eg NAME_SOURCE_UNDEFINED, NAME_SOURCE_DEFAULT_SOURCE, - * NAME_SOURCE_SIM_SOURCE or NAME_SOURCE_USER_INPUT. + * @return the source of the name, eg NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SOURCE or + * NAME_SOURCE_USER_INPUT. * @hide */ @UnsupportedAppUsage @@ -316,8 +316,8 @@ public class SubscriptionInfo implements Parcelable { } /** - * Creates and returns an icon {@code Bitmap} to represent this {@code SubscriptionInfo} in a user - * interface. + * Creates and returns an icon {@code Bitmap} to represent this {@code SubscriptionInfo} in a + * user interface. * * @param context A {@code Context} to get the {@code DisplayMetrics}s from. * diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 57c84a638f12..0c6341111029 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -364,12 +364,6 @@ public class SubscriptionManager { public static final String NAME_SOURCE = "name_source"; /** - * The name_source is undefined - * @hide - */ - public static final int NAME_SOURCE_UNDEFINDED = -1; - - /** * The name_source is the default * @hide */ @@ -1598,27 +1592,16 @@ public class SubscriptionManager { } /** - * Set display name by simInfo index - * @param displayName the display name of SIM card - * @param subId the unique SubscriptionInfo index in database - * @return the number of records updated - * @hide - */ - public int setDisplayName(String displayName, int subId) { - return setDisplayName(displayName, subId, NAME_SOURCE_UNDEFINDED); - } - - /** * Set display name by simInfo index with name source * @param displayName the display name of SIM card * @param subId the unique SubscriptionInfo index in database * @param nameSource 0: NAME_SOURCE_DEFAULT_SOURCE, 1: NAME_SOURCE_SIM_SOURCE, - * 2: NAME_SOURCE_USER_INPUT, -1 NAME_SOURCE_UNDEFINED + * 2: NAME_SOURCE_USER_INPUT * @return the number of records updated or < 0 if invalid subId * @hide */ @UnsupportedAppUsage - public int setDisplayName(String displayName, int subId, long nameSource) { + public int setDisplayName(String displayName, int subId, int nameSource) { if (VDBG) { logd("[setDisplayName]+ displayName:" + displayName + " subId:" + subId + " nameSource:" + nameSource); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 9c63a82b5673..0d3bc1db831f 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -3790,6 +3790,7 @@ public class TelephonyManager { * @hide * nobody seems to call this. */ + @TestApi @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag() { return getLine1AlphaTag(getSubId()); diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index 01fdae800972..cfba0529e664 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -145,21 +145,13 @@ interface ISub { int setIconTint(int tint, int subId); /** - * Set display name by simInfo index - * @param displayName the display name of SIM card - * @param subId the unique SubscriptionInfo index in database - * @return the number of records updated - */ - int setDisplayName(String displayName, int subId); - - /** * Set display name by simInfo index with name source * @param displayName the display name of SIM card * @param subId the unique SubscriptionInfo index in database * @param nameSource, 0: DEFAULT_SOURCE, 1: SIM_SOURCE, 2: USER_INPUT * @return the number of records updated */ - int setDisplayNameUsingSrc(String displayName, int subId, long nameSource); + int setDisplayNameUsingSrc(String displayName, int subId, int nameSource); /** * Set phone number by subId diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index b308982c2343..fa7bf61a07b2 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -16,6 +16,7 @@ package com.android.server; +import static com.android.server.PackageWatchdog.MonitoredPackage; import static com.android.server.PackageWatchdog.TRIGGER_FAILURE_COUNT; import static org.junit.Assert.assertEquals; @@ -27,6 +28,7 @@ import android.content.Context; import android.content.pm.VersionedPackage; import android.os.Handler; import android.os.test.TestLooper; +import android.service.watchdog.PackageInfo; import android.util.AtomicFile; import androidx.test.InstrumentationRegistry; @@ -143,6 +145,31 @@ public class PackageWatchdogTest { assertNull(watchdog.getPackages(observer3)); } + /** Observing already observed package extends the observation time. */ + @Test + public void testObserveAlreadyObservedPackage() throws Exception { + PackageWatchdog watchdog = createWatchdog(); + TestObserver observer = new TestObserver(OBSERVER_NAME_1); + + // Start observing APP_A + watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); + + // Then advance time half-way + Thread.sleep(SHORT_DURATION / 2); + mTestLooper.dispatchAll(); + + // Start observing APP_A again + watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION); + + // Then advance time such that it should have expired were it not for the second observation + Thread.sleep((SHORT_DURATION / 2) + 1); + mTestLooper.dispatchAll(); + + // Verify that APP_A not expired since second observation extended the time + assertEquals(1, watchdog.getPackages(observer).size()); + assertTrue(watchdog.getPackages(observer).contains(APP_A)); + } + /** * Test package observers are persisted and loaded on startup */ @@ -577,6 +604,84 @@ public class PackageWatchdogTest { assertEquals(APP_C, observer.mFailedPackages.get(0)); } + /** + * Tests failure when health check duration is different from package observation duration + * Failure is also notified only once. + */ + @Test + public void testExplicitHealthCheckFailureBeforeExpiry() throws Exception { + TestController controller = new TestController(); + PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */); + TestObserver observer = new TestObserver(OBSERVER_NAME_1, + PackageHealthObserverImpact.USER_IMPACT_MEDIUM); + + // Start observing with explicit health checks for APP_A and + // package observation duration == LONG_DURATION + // health check duration == SHORT_DURATION (set by default in the TestController) + controller.setSupportedPackages(Arrays.asList(APP_A)); + watchdog.startObservingHealth(observer, Arrays.asList(APP_A), LONG_DURATION); + + // Then APP_A has exceeded health check duration + Thread.sleep(SHORT_DURATION); + mTestLooper.dispatchAll(); + + // Verify that health check is failed + assertEquals(1, observer.mFailedPackages.size()); + assertEquals(APP_A, observer.mFailedPackages.get(0)); + + // Then clear failed packages and start observing a random package so requests are synced + // and PackageWatchdog#onSupportedPackages is called and APP_A has a chance to fail again + // this time due to package expiry. + observer.mFailedPackages.clear(); + watchdog.startObservingHealth(observer, Arrays.asList(APP_B), LONG_DURATION); + + // Verify that health check failure is not notified again + assertTrue(observer.mFailedPackages.isEmpty()); + } + + /** Tests {@link MonitoredPackage} health check state transitions. */ + @Test + public void testPackageHealthCheckStateTransitions() { + MonitoredPackage m1 = new MonitoredPackage(APP_A, LONG_DURATION, + false /* hasPassedHealthCheck */); + MonitoredPackage m2 = new MonitoredPackage(APP_B, LONG_DURATION, false); + MonitoredPackage m3 = new MonitoredPackage(APP_C, LONG_DURATION, false); + MonitoredPackage m4 = new MonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true); + + // Verify transition: inactive -> active -> passed + // Verify initially inactive + assertEquals(MonitoredPackage.STATE_INACTIVE, m1.getHealthCheckStateLocked()); + // Verify still inactive, until we #setHealthCheckActiveLocked + assertEquals(MonitoredPackage.STATE_INACTIVE, m1.handleElapsedTimeLocked(SHORT_DURATION)); + // Verify now active + assertEquals(MonitoredPackage.STATE_ACTIVE, m1.setHealthCheckActiveLocked(SHORT_DURATION)); + // Verify now passed + assertEquals(MonitoredPackage.STATE_PASSED, m1.tryPassHealthCheckLocked()); + + // Verify transition: inactive -> active -> failed + // Verify initially inactive + assertEquals(MonitoredPackage.STATE_INACTIVE, m2.getHealthCheckStateLocked()); + // Verify now active + assertEquals(MonitoredPackage.STATE_ACTIVE, m2.setHealthCheckActiveLocked(SHORT_DURATION)); + // Verify now failed + assertEquals(MonitoredPackage.STATE_FAILED, m2.handleElapsedTimeLocked(SHORT_DURATION)); + + // Verify transition: inactive -> failed + // Verify initially inactive + assertEquals(MonitoredPackage.STATE_INACTIVE, m3.getHealthCheckStateLocked()); + // Verify now failed because package expired + assertEquals(MonitoredPackage.STATE_FAILED, m3.handleElapsedTimeLocked(LONG_DURATION)); + // Verify remains failed even when asked to pass + assertEquals(MonitoredPackage.STATE_FAILED, m3.tryPassHealthCheckLocked()); + + // Verify transition: passed + assertEquals(MonitoredPackage.STATE_PASSED, m4.getHealthCheckStateLocked()); + // Verify remains passed even if health check fails + assertEquals(MonitoredPackage.STATE_PASSED, m4.handleElapsedTimeLocked(SHORT_DURATION)); + // Verify remains passed even if package expires + assertEquals(MonitoredPackage.STATE_PASSED, m4.handleElapsedTimeLocked(LONG_DURATION)); + } + private PackageWatchdog createWatchdog() { return createWatchdog(new TestController(), true /* withPackagesReady */); } @@ -636,7 +741,7 @@ public class PackageWatchdogTest { private List<String> mSupportedPackages = new ArrayList<>(); private List<String> mRequestedPackages = new ArrayList<>(); private Consumer<String> mPassedConsumer; - private Consumer<List<String>> mSupportedConsumer; + private Consumer<List<PackageInfo>> mSupportedConsumer; private Runnable mNotifySyncRunnable; @Override @@ -649,7 +754,7 @@ public class PackageWatchdogTest { @Override public void setCallbacks(Consumer<String> passedConsumer, - Consumer<List<String>> supportedConsumer, Runnable notifySyncRunnable) { + Consumer<List<PackageInfo>> supportedConsumer, Runnable notifySyncRunnable) { mPassedConsumer = passedConsumer; mSupportedConsumer = supportedConsumer; mNotifySyncRunnable = notifySyncRunnable; @@ -661,7 +766,11 @@ public class PackageWatchdogTest { if (mIsEnabled) { packages.retainAll(mSupportedPackages); mRequestedPackages.addAll(packages); - mSupportedConsumer.accept(mSupportedPackages); + List<PackageInfo> packageInfos = new ArrayList<>(); + for (String packageName: packages) { + packageInfos.add(new PackageInfo(packageName, SHORT_DURATION)); + } + mSupportedConsumer.accept(packageInfos); } else { mSupportedConsumer.accept(Collections.emptyList()); } diff --git a/tests/ProtoInputStreamTests/Android.mk b/tests/ProtoInputStreamTests/Android.mk new file mode 100644 index 000000000000..eb747cc2cdcc --- /dev/null +++ b/tests/ProtoInputStreamTests/Android.mk @@ -0,0 +1,34 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_PACKAGE_NAME := ProtoInputStreamTests +LOCAL_PROTOC_OPTIMIZE_TYPE := nano +LOCAL_MODULE_TAGS := tests optional +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) \ + $(call all-proto-files-under, src) +LOCAL_PRIVATE_PLATFORM_APIS := true +LOCAL_CERTIFICATE := platform +LOCAL_COMPATIBILITY_SUITE := device-tests + +LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_STATIC_JAVA_LIBRARIES := \ + androidx.test.rules \ + frameworks-base-testutils \ + mockito-target-minus-junit4 + +include $(BUILD_PACKAGE)
\ No newline at end of file diff --git a/tests/ProtoInputStreamTests/AndroidManifest.xml b/tests/ProtoInputStreamTests/AndroidManifest.xml new file mode 100644 index 000000000000..c11aa7393431 --- /dev/null +++ b/tests/ProtoInputStreamTests/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.protoinputstream"> + <application> + <uses-library android:name="android.test.runner"/> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.test.protoinputstream" + android:label="ProtoInputStream Tests"> + </instrumentation> +</manifest>
\ No newline at end of file diff --git a/tests/ProtoInputStreamTests/AndroidTest.xml b/tests/ProtoInputStreamTests/AndroidTest.xml new file mode 100644 index 000000000000..51ab88e4d4d0 --- /dev/null +++ b/tests/ProtoInputStreamTests/AndroidTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 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. +--> +<configuration description="Configuration for ProtoInputStream Tests"> + <option name="test-suite-tag" value="ProtoInputStreamTests" /> + <option name="config-descriptor:metadata" key="component" value="metrics" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="ProtoInputStreamTests.apk" /> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.test.protoinputstream" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/tests/ProtoInputStreamTests/TEST_MAPPING b/tests/ProtoInputStreamTests/TEST_MAPPING new file mode 100644 index 000000000000..cf9f0772ac2d --- /dev/null +++ b/tests/ProtoInputStreamTests/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "ProtoInputStreamTests" + } + ] +}
\ No newline at end of file diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBoolTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBoolTest.java new file mode 100644 index 000000000000..c21c4033a0ff --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBoolTest.java @@ -0,0 +1,500 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamBoolTest extends TestCase { + + /** + * Test reading single bool field + */ + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + /** + * Implementation of testRead with a given chunkSize. + */ + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 3 -> 1 + (byte) 0x18, + (byte) 0x01, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + boolean[] results = new boolean[2]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readBoolean(fieldId2); + break; + case (int) fieldId3: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(false, results[0]); + assertEquals(true, results[1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(false); + testReadCompat(true); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(boolean val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL; + final long fieldId = fieldFlags | ((long) 130 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.boolField = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + boolean result = false; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readBoolean(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.boolField, result); + } + + + /** + * Test reading repeated bool field + */ + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + /** + * Implementation of testRepeated with a given chunkSize. + */ + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BOOL; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + + // 4 -> 0 + (byte) 0x20, + (byte) 0x00, + // 4 -> 1 + (byte) 0x20, + (byte) 0x01, + + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + + // 3 -> 0 + (byte) 0x18, + (byte) 0x00, + // 3 -> 1 + (byte) 0x18, + (byte) 0x01, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + boolean[][] results = new boolean[3][2]; + int[] indices = new int[3]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readBoolean(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readBoolean(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readBoolean(fieldId3); + break; + case (int) fieldId4: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(false, results[0][0]); + assertEquals(false, results[0][1]); + assertEquals(true, results[1][0]); + assertEquals(true, results[1][1]); + assertEquals(false, results[2][0]); + assertEquals(true, results[2][1]); + } + + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new boolean[0]); + testRepeatedCompat(new boolean[]{false, true}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(boolean[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BOOL; + final long fieldId = fieldFlags | ((long) 131 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.boolFieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + boolean[] result = new boolean[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readBoolean(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.boolFieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.boolFieldRepeated[i], result[i]); + } + } + + /** + * Test reading packed bool field + */ + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + /** + * Implementation of testPacked with a given chunkSize. + */ + public void testPacked(int chunkSize) throws IOException { + + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_BOOL; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x02, + (byte) 0x00, + (byte) 0x00, + // 4 -> 0,1 + (byte) 0x22, + (byte) 0x02, + (byte) 0x00, + (byte) 0x01, + // 2 -> 1 + (byte) 0x12, + (byte) 0x02, + (byte) 0x01, + (byte) 0x01, + // 3 -> 0,1 + (byte) 0x1a, + (byte) 0x02, + (byte) 0x00, + (byte) 0x01, + }; + + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + boolean[][] results = new boolean[3][2]; + int[] indices = new int[3]; + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readBoolean(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readBoolean(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readBoolean(fieldId3); + break; + case (int) fieldId4: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(false, results[0][0]); + assertEquals(false, results[0][1]); + assertEquals(true, results[1][0]); + assertEquals(true, results[1][1]); + assertEquals(false, results[2][0]); + assertEquals(true, results[2][1]); + } + + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new boolean[0]); + testPackedCompat(new boolean[]{false, true}); + } + + /** + * Implementation of testPackedCompat with a given value. + */ + private void testPackedCompat(boolean[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_BOOL; + final long fieldId = fieldFlags | ((long) 132 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.boolFieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + boolean[] result = new boolean[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readBoolean(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.boolFieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.boolFieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x08, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readInt(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BOOL; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x01, + (byte) 0x01, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readBoolean(fieldId1); + // don't fail, varint is ok + break; + case (int) fieldId2: + pi.readBoolean(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readBoolean(fieldId3); + // don't fail, length delimited is ok (represents packed booleans) + break; + case (int) fieldId6: + pi.readBoolean(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBytesTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBytesTest.java new file mode 100644 index 000000000000..09fe40edda6c --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamBytesTest.java @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +public class ProtoInputStreamBytesTest extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> null - default value, written when repeated + (byte) 0x0a, + (byte) 0x00, + // 2 -> { } - default value, written when repeated + (byte) 0x12, + (byte) 0x00, + // 5 -> { 0, 1, 2, 3, 4 } + (byte) 0x2a, + (byte) 0x05, + (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, + // 3 -> { 0, 1, 2, 3, 4, 5 } + (byte) 0x1a, + (byte) 0x06, + (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, + // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc } + (byte) 0x22, + (byte) 0x04, + (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + byte[][] results = new byte[4][]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0] = pi.readBytes(fieldId1); + break; + case (int) fieldId2: + results[1] = pi.readBytes(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readBytes(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readBytes(fieldId4); + break; + case (int) fieldId5: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertTrue("Expected: [] Actual: " + Arrays.toString(results[0]), + Arrays.equals(new byte[]{}, results[0])); + assertTrue("Expected: [] Actual: " + Arrays.toString(results[1]), + Arrays.equals(new byte[]{}, results[1])); + assertTrue("Expected: [0, 1, 2, 3, 4, 5] Actual: " + Arrays.toString(results[2]), + Arrays.equals(new byte[]{0, 1, 2, 3, 4, 5}, results[2])); + assertTrue("Expected: [-1, -2, -3, -4] Actual: " + Arrays.toString(results[3]), + Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc}, + results[3])); + } + + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(new byte[0]); + testReadCompat(new byte[]{1, 2, 3, 4}); + testReadCompat(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc}); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(byte[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES; + final long fieldId = fieldFlags | ((long) 150 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.bytesField = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + byte[] result = new byte[val.length]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readBytes(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.bytesField.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.bytesField[i], result[i]); + } + } + + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BYTES; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> null - default value, written when repeated + (byte) 0x0a, + (byte) 0x00, + // 2 -> { } - default value, written when repeated + (byte) 0x12, + (byte) 0x00, + // 3 -> { 0, 1, 2, 3, 4, 5 } + (byte) 0x1a, + (byte) 0x06, + (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, + // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc } + (byte) 0x22, + (byte) 0x04, + (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc, + + // 5 -> { 0, 1, 2, 3, 4} + (byte) 0x2a, + (byte) 0x05, + (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, + + // 1 -> null - default value, written when repeated + (byte) 0x0a, + (byte) 0x00, + // 2 -> { } - default value, written when repeated + (byte) 0x12, + (byte) 0x00, + // 3 -> { 0, 1, 2, 3, 4, 5 } + (byte) 0x1a, + (byte) 0x06, + (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, + // 4 -> { (byte)0xff, (byte)0xfe, (byte)0xfd, (byte)0xfc } + (byte) 0x22, + (byte) 0x04, + (byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + byte[][][] results = new byte[4][2][]; + int[] indices = new int[4]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readBytes(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readBytes(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readBytes(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readBytes(fieldId4); + break; + case (int) fieldId5: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assert (Arrays.equals(new byte[]{}, results[0][0])); + assert (Arrays.equals(new byte[]{}, results[0][1])); + assert (Arrays.equals(new byte[]{}, results[1][0])); + assert (Arrays.equals(new byte[]{}, results[1][1])); + assert (Arrays.equals(new byte[]{1, 2, 3, 4}, results[2][0])); + assert (Arrays.equals(new byte[]{1, 2, 3, 4}, results[2][1])); + assert (Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc}, + results[3][0])); + assert (Arrays.equals(new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc}, + results[3][1])); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new byte[0][]); + testRepeatedCompat(new byte[][]{ + new byte[0], + new byte[]{1, 2, 3, 4}, + new byte[]{(byte) 0xff, (byte) 0xfe, (byte) 0xfd, (byte) 0xfc} + }); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(byte[][] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_BYTES; + final long fieldId = fieldFlags | ((long) 151 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.bytesFieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + byte[][] result = new byte[val.length][]; // start off with default value + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readBytes(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.bytesFieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.bytesFieldRepeated[i].length, result[i].length); + for (int j = 0; j < result[i].length; j++) { + assertEquals(readback.bytesFieldRepeated[i][j], result[i][j]); + } + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> {1} + (byte) 0x0a, + (byte) 0x01, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readInt(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x01, + (byte) 0x01, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readBytes(fieldId1); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId2: + pi.readBytes(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readBytes(fieldId3); + // don't fail, length delimited is ok + break; + case (int) fieldId6: + pi.readBytes(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } + +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamDoubleTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamDoubleTest.java new file mode 100644 index 000000000000..118fe3431e01 --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamDoubleTest.java @@ -0,0 +1,728 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamDoubleTest extends TestCase { + + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL); + final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x11, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f, + // 10 -> 1 + (byte) 0x51, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f, + // 3 -> -1234.432 + (byte) 0x19, + (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e, + (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0, + // 4 -> 42.42 + (byte) 0x21, + (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f, + (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40, + // 5 -> Double.MIN_NORMAL + (byte) 0x29, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00, + // 6 -> DOUBLE.MIN_VALUE + (byte) 0x31, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 7 -> Double.NEGATIVE_INFINITY + (byte) 0x39, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff, + // 8 -> Double.NaN + (byte) 0x41, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f, + // 9 -> Double.POSITIVE_INFINITY + (byte) 0x49, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + double[] results = new double[9]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readDouble(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readDouble(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readDouble(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readDouble(fieldId5); + break; + case (int) fieldId6: + results[5] = pi.readDouble(fieldId6); + break; + case (int) fieldId7: + results[6] = pi.readDouble(fieldId7); + break; + case (int) fieldId8: + results[7] = pi.readDouble(fieldId8); + break; + case (int) fieldId9: + results[8] = pi.readDouble(fieldId9); + break; + case (int) fieldId10: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + assertEquals(0.0, results[0]); + assertEquals(1.0, results[1]); + assertEquals(-1234.432, results[2]); + assertEquals(42.42, results[3]); + assertEquals(Double.MIN_NORMAL, results[4]); + assertEquals(Double.MIN_VALUE, results[5]); + assertEquals(Double.NEGATIVE_INFINITY, results[6]); + assertEquals(Double.NaN, results[7]); + assertEquals(Double.POSITIVE_INFINITY, results[8]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1234.432); + testReadCompat(42.42); + testReadCompat(Double.MIN_NORMAL); + testReadCompat(Double.MIN_VALUE); + testReadCompat(Double.NEGATIVE_INFINITY); + testReadCompat(Double.NaN); + testReadCompat(Double.POSITIVE_INFINITY); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(double val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE; + final long fieldId = fieldFlags | ((long) 10 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.doubleField = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + double result = 0.0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readDouble(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.doubleField, result); + } + + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_DOUBLE; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL); + final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x09, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x11, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f, + // 3 -> -1234.432 + (byte) 0x19, + (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e, + (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0, + // 4 -> 42.42 + (byte) 0x21, + (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f, + (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40, + // 5 -> Double.MIN_NORMAL + (byte) 0x29, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00, + // 6 -> DOUBLE.MIN_VALUE + (byte) 0x31, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 7 -> Double.NEGATIVE_INFINITY + (byte) 0x39, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff, + // 8 -> Double.NaN + (byte) 0x41, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f, + // 9 -> Double.POSITIVE_INFINITY + (byte) 0x49, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f, + // 10 -> 1 + (byte) 0x51, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f, + + // 1 -> 0 - default value, written when repeated + (byte) 0x09, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x11, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f, + // 3 -> -1234.432 + (byte) 0x19, + (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e, + (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0, + // 4 -> 42.42 + (byte) 0x21, + (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f, + (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40, + // 5 -> Double.MIN_NORMAL + (byte) 0x29, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00, + // 6 -> DOUBLE.MIN_VALUE + (byte) 0x31, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 7 -> Double.NEGATIVE_INFINITY + (byte) 0x39, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff, + // 8 -> Double.NaN + (byte) 0x41, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f, + // 9 -> Double.POSITIVE_INFINITY + (byte) 0x49, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + double[][] results = new double[9][2]; + int[] indices = new int[9]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readDouble(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readDouble(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readDouble(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readDouble(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readDouble(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readDouble(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readDouble(fieldId7); + break; + case (int) fieldId8: + results[7][indices[7]++] = pi.readDouble(fieldId8); + break; + case (int) fieldId9: + results[8][indices[8]++] = pi.readDouble(fieldId9); + break; + case (int) fieldId10: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + assertEquals(0.0, results[0][0]); + assertEquals(0.0, results[0][1]); + assertEquals(1.0, results[1][0]); + assertEquals(1.0, results[1][1]); + assertEquals(-1234.432, results[2][0]); + assertEquals(-1234.432, results[2][1]); + assertEquals(42.42, results[3][0]); + assertEquals(42.42, results[3][1]); + assertEquals(Double.MIN_NORMAL, results[4][0]); + assertEquals(Double.MIN_NORMAL, results[4][1]); + assertEquals(Double.MIN_VALUE, results[5][0]); + assertEquals(Double.MIN_VALUE, results[5][1]); + assertEquals(Double.NEGATIVE_INFINITY, results[6][0]); + assertEquals(Double.NEGATIVE_INFINITY, results[6][1]); + assertEquals(Double.NaN, results[7][0]); + assertEquals(Double.NaN, results[7][1]); + assertEquals(Double.POSITIVE_INFINITY, results[8][0]); + assertEquals(Double.POSITIVE_INFINITY, results[8][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new double[0]); + testRepeatedCompat(new double[]{0, 1, -1234.432, 42.42, + Double.MIN_NORMAL, Double.MIN_VALUE, Double.NEGATIVE_INFINITY, Double.NaN, + Double.POSITIVE_INFINITY, + }); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(double[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_DOUBLE; + final long fieldId = fieldFlags | ((long) 11 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.doubleFieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + double[] result = new double[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readDouble(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.doubleFieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.doubleFieldRepeated[i], result[i]); + } + } + + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_DOUBLE; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL); + final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f, + // 10 -> 1 + (byte) 0x52, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x3f, + // 3 -> -1234.432 + (byte) 0x1a, + (byte) 0x10, + (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e, + (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0, + (byte) 0x7d, (byte) 0x3f, (byte) 0x35, (byte) 0x5e, + (byte) 0xba, (byte) 0x49, (byte) 0x93, (byte) 0xc0, + // 4 -> 42.42 + (byte) 0x22, + (byte) 0x10, + (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f, + (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40, + (byte) 0xf6, (byte) 0x28, (byte) 0x5c, (byte) 0x8f, + (byte) 0xc2, (byte) 0x35, (byte) 0x45, (byte) 0x40, + // 5 -> Double.MIN_NORMAL + (byte) 0x2a, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00, + // 6 -> DOUBLE.MIN_VALUE + (byte) 0x32, + (byte) 0x10, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 7 -> Double.NEGATIVE_INFINITY + (byte) 0x3a, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0xff, + // 8 -> Double.NaN + (byte) 0x42, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf8, (byte) 0x7f, + // 9 -> Double.POSITIVE_INFINITY + (byte) 0x4a, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xf0, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + double[][] results = new double[9][2]; + int[] indices = new int[9]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readDouble(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readDouble(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readDouble(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readDouble(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readDouble(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readDouble(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readDouble(fieldId7); + break; + case (int) fieldId8: + results[7][indices[7]++] = pi.readDouble(fieldId8); + break; + case (int) fieldId9: + results[8][indices[8]++] = pi.readDouble(fieldId9); + break; + case (int) fieldId10: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + assertEquals(0.0, results[0][0]); + assertEquals(0.0, results[0][1]); + assertEquals(1.0, results[1][0]); + assertEquals(1.0, results[1][1]); + assertEquals(-1234.432, results[2][0]); + assertEquals(-1234.432, results[2][1]); + assertEquals(42.42, results[3][0]); + assertEquals(42.42, results[3][1]); + assertEquals(Double.MIN_NORMAL, results[4][0]); + assertEquals(Double.MIN_NORMAL, results[4][1]); + assertEquals(Double.MIN_VALUE, results[5][0]); + assertEquals(Double.MIN_VALUE, results[5][1]); + assertEquals(Double.NEGATIVE_INFINITY, results[6][0]); + assertEquals(Double.NEGATIVE_INFINITY, results[6][1]); + assertEquals(Double.NaN, results[7][0]); + assertEquals(Double.NaN, results[7][1]); + assertEquals(Double.POSITIVE_INFINITY, results[8][0]); + assertEquals(Double.POSITIVE_INFINITY, results[8][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new double[0]); + testPackedCompat(new double[]{0, 1, -1234.432, 42.42, + Double.MIN_NORMAL, Double.MIN_VALUE, Double.NEGATIVE_INFINITY, Double.NaN, + Double.POSITIVE_INFINITY, + }); + } + + /** + * Implementation of testPackedCompat with a given value. + */ + private void testPackedCompat(double[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_DOUBLE; + final long fieldId = fieldFlags | ((long) 12 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.doubleFieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + double[] result = new double[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readDouble(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.doubleFieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.doubleFieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x09, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readInt(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_DOUBLE; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x08, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readDouble(fieldId1); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId2: + pi.readDouble(fieldId2); + // don't fail, fixed64 is ok + break; + case (int) fieldId3: + pi.readDouble(fieldId3); + // don't fail, length delimited is ok (represents packed doubles) + break; + case (int) fieldId6: + pi.readDouble(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamEnumTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamEnumTest.java new file mode 100644 index 000000000000..f55d95129588 --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamEnumTest.java @@ -0,0 +1,570 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamEnumTest extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 6 -> MAX_VALUE + (byte) 0x30, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[] results = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0]); + assertEquals(1, results[1]); + assertEquals(-1, results[2]); + assertEquals(Integer.MIN_VALUE, results[3]); + assertEquals(Integer.MAX_VALUE, results[4]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1); + testReadCompat(Integer.MIN_VALUE); + testReadCompat(Integer.MAX_VALUE); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(int val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM; + final long fieldId = fieldFlags | ((long) 160 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.outsideField = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + + int result = 0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + // Nano proto drops values that are outside the range, so compare against val + assertEquals(val, result); + } + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_ENUM; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 6 -> MAX_VALUE + (byte) 0x30, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new int[]{}); + testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_ENUM; + final long fieldId = fieldFlags | ((long) 161 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.outsideFieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + // Nano proto drops values that are outside the range, so compare against val + assertEquals(val.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(val[i], result[i]); + } + } + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_ENUM; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x02, + (byte) 0x00, + (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x02, + (byte) 0x01, + (byte) 0x01, + + // 6 -> MAX_VALUE + (byte) 0x32, + (byte) 0x0a, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 3 -> -1 + (byte) 0x1a, + (byte) 0x14, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + // 4 -> MIN_VALUE + (byte) 0x22, + (byte) 0x14, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + // 5 -> MAX_VALUE + (byte) 0x2a, + (byte) 0x0a, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new int[]{}); + testPackedCompat(new int[]{0, 1}); + + // Nano proto has a bug. It gets the size with computeInt32SizeNoTag (correctly) + // but incorrectly uses writeRawVarint32 to write the value for negative numbers. + //testPackedCompat(new int[] { 0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE }); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testPackedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_ENUM; + final long fieldId = fieldFlags | ((long) 162 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.outsideFieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + // Nano proto drops values that are outside the range, so compare against val + assertEquals(val.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(val[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x08, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_ENUM; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x01, + (byte) 0x01, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readInt(fieldId1); + // don't fail, varint is ok + break; + case (int) fieldId2: + pi.readInt(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readInt(fieldId3); + // don't fail, length delimited is ok (represents packed enums) + break; + case (int) fieldId6: + pi.readInt(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed32Test.java new file mode 100644 index 000000000000..df68476f0c36 --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed32Test.java @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamFixed32Test extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x15, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> 1 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x1d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x25, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 5 -> Integer.MAX_VALUE + (byte) 0x2d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[] results = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0]); + assertEquals(1, results[1]); + assertEquals(-1, results[2]); + assertEquals(Integer.MIN_VALUE, results[3]); + assertEquals(Integer.MAX_VALUE, results[4]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1); + testReadCompat(Integer.MIN_VALUE); + testReadCompat(Integer.MAX_VALUE); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(int val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32; + final long fieldId = fieldFlags | ((long) 90 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.fixed32Field = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int result = 0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.fixed32Field, result); + } + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0d, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x15, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x1d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x25, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 5 -> Integer.MAX_VALUE + (byte) 0x2d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + + // 6 -> 1 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // 1 -> 0 - default value, written when repeated + (byte) 0x0d, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x15, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x1d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x25, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 5 -> Integer.MAX_VALUE + (byte) 0x2d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new int[0]); + testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32; + final long fieldId = fieldFlags | ((long) 91 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.fixed32FieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.fixed32FieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.fixed32FieldRepeated[i], result[i]); + } + } + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FIXED32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> 1 + (byte) 0x32, + (byte) 0x08, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x08, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x1a, + (byte) 0x08, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x22, + (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 5 -> Integer.MAX_VALUE + (byte) 0x2a, + (byte) 0x08, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new int[0]); + testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testPackedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED32; + final long fieldId = fieldFlags | ((long) 92 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.fixed32FieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.fixed32FieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.fixed32FieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x0d, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x04, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readInt(fieldId1); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId2: + pi.readInt(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readInt(fieldId3); + // don't fail, length delimited is ok (represents packed fixed32) + break; + case (int) fieldId6: + pi.readInt(fieldId6); + // don't fail, fixed32 is ok + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed64Test.java new file mode 100644 index 000000000000..af4130b28cd8 --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFixed64Test.java @@ -0,0 +1,649 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamFixed64Test extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> 1 + (byte) 0x41, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x19, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x21, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 5 -> Integer.MAX_VALUE + (byte) 0x29, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> Long.MIN_VALUE + (byte) 0x31, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 7 -> Long.MAX_VALUE + (byte) 0x39, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[] results = new long[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0]); + assertEquals(1, results[1]); + assertEquals(-1, results[2]); + assertEquals(Integer.MIN_VALUE, results[3]); + assertEquals(Integer.MAX_VALUE, results[4]); + assertEquals(Long.MIN_VALUE, results[5]); + assertEquals(Long.MAX_VALUE, results[6]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1); + testReadCompat(Integer.MIN_VALUE); + testReadCompat(Integer.MAX_VALUE); + testReadCompat(Long.MIN_VALUE); + testReadCompat(Long.MAX_VALUE); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(long val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64; + final long fieldId = fieldFlags | ((long) 100 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.fixed64Field = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long result = 0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.fixed64Field, result); + } + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x09, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x19, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x21, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 5 -> Integer.MAX_VALUE + (byte) 0x29, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> Long.MIN_VALUE + (byte) 0x31, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 7 -> Long.MAX_VALUE + (byte) 0x39, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + + // 8 -> 1 + (byte) 0x41, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // 1 -> 0 - default value, written when repeated + (byte) 0x09, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x19, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x21, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 5 -> Integer.MAX_VALUE + (byte) 0x29, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> Long.MIN_VALUE + (byte) 0x31, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 7 -> Long.MAX_VALUE + (byte) 0x39, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[][] results = new long[7][2]; + int[] indices = new int[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readLong(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + assertEquals(Long.MIN_VALUE, results[5][0]); + assertEquals(Long.MIN_VALUE, results[5][1]); + assertEquals(Long.MAX_VALUE, results[6][0]); + assertEquals(Long.MAX_VALUE, results[6][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new long[0]); + testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(long[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64; + final long fieldId = fieldFlags | ((long) 101 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.fixed64FieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long[] result = new long[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.fixed64FieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.fixed64FieldRepeated[i], result[i]); + } + } + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FIXED64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x10, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 8 -> 1 + (byte) 0x42, + (byte) 0x10, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x1a, + (byte) 0x10, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x22, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 5 -> Integer.MAX_VALUE + (byte) 0x2a, + (byte) 0x10, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> Long.MIN_VALUE + (byte) 0x32, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 7 -> Long.MAX_VALUE + (byte) 0x3a, + (byte) 0x10, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[][] results = new long[7][2]; + int[] indices = new int[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readLong(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + assertEquals(Long.MIN_VALUE, results[5][0]); + assertEquals(Long.MIN_VALUE, results[5][1]); + assertEquals(Long.MAX_VALUE, results[6][0]); + assertEquals(Long.MAX_VALUE, results[6][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new long[0]); + testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testPackedCompat(long[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FIXED64; + final long fieldId = fieldFlags | ((long) 102 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.fixed64FieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long[] result = new long[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.fixed64FieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.fixed64FieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x09, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readInt(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x08, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readLong(fieldId1); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId2: + pi.readLong(fieldId2); + // don't fail, fixed64 is ok + break; + case (int) fieldId3: + pi.readLong(fieldId3); + // don't fail, length delimited is ok (represents packed fixed64) + break; + case (int) fieldId6: + pi.readLong(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFloatTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFloatTest.java new file mode 100644 index 000000000000..9bc07dc513e1 --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamFloatTest.java @@ -0,0 +1,679 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamFloatTest extends TestCase { + + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL); + final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x15, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f, + // 10 -> 1 + (byte) 0x55, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f, + // 3 -> -1234.432 + (byte) 0x1d, + (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4, + // 4 -> 42.42 + (byte) 0x25, + (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42, + // 5 -> Float.MIN_NORMAL + (byte) 0x2d, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00, + // 6 -> DOUBLE.MIN_VALUE + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 7 -> Float.NEGATIVE_INFINITY + (byte) 0x3d, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff, + // 8 -> Float.NaN + (byte) 0x45, + (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f, + // 9 -> Float.POSITIVE_INFINITY + (byte) 0x4d, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + float[] results = new float[9]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readFloat(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readFloat(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readFloat(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readFloat(fieldId5); + break; + case (int) fieldId6: + results[5] = pi.readFloat(fieldId6); + break; + case (int) fieldId7: + results[6] = pi.readFloat(fieldId7); + break; + case (int) fieldId8: + results[7] = pi.readFloat(fieldId8); + break; + case (int) fieldId9: + results[8] = pi.readFloat(fieldId9); + break; + case (int) fieldId10: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + assertEquals(0.0f, results[0]); + assertEquals(1.0f, results[1]); + assertEquals(-1234.432f, results[2]); + assertEquals(42.42f, results[3]); + assertEquals(Float.MIN_NORMAL, results[4]); + assertEquals(Float.MIN_VALUE, results[5]); + assertEquals(Float.NEGATIVE_INFINITY, results[6]); + assertEquals(Float.NaN, results[7]); + assertEquals(Float.POSITIVE_INFINITY, results[8]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1234.432f); + testReadCompat(42.42f); + testReadCompat(Float.MIN_NORMAL); + testReadCompat(Float.MIN_VALUE); + testReadCompat(Float.NEGATIVE_INFINITY); + testReadCompat(Float.NaN); + testReadCompat(Float.POSITIVE_INFINITY); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(float val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT; + final long fieldId = fieldFlags | ((long) 20 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.floatField = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + float result = 0.0f; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readFloat(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.floatField, result); + } + + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FLOAT; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL); + final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0d, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x15, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f, + // 3 -> -1234.432 + (byte) 0x1d, + (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4, + // 4 -> 42.42 + (byte) 0x25, + (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42, + // 5 -> Float.MIN_NORMAL + (byte) 0x2d, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00, + // 6 -> DOUBLE.MIN_VALUE + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 7 -> Float.NEGATIVE_INFINITY + (byte) 0x3d, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff, + // 8 -> Float.NaN + (byte) 0x45, + (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f, + // 9 -> Float.POSITIVE_INFINITY + (byte) 0x4d, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f, + + // 10 -> 1 + (byte) 0x55, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f, + + // 1 -> 0 - default value, written when repeated + (byte) 0x0d, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x15, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f, + // 3 -> -1234.432 + (byte) 0x1d, + (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4, + // 4 -> 42.42 + (byte) 0x25, + (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42, + // 5 -> Float.MIN_NORMAL + (byte) 0x2d, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00, + // 6 -> DOUBLE.MIN_VALUE + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 7 -> Float.NEGATIVE_INFINITY + (byte) 0x3d, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff, + // 8 -> Float.NaN + (byte) 0x45, + (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f, + // 9 -> Float.POSITIVE_INFINITY + (byte) 0x4d, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + float[][] results = new float[9][2]; + int[] indices = new int[9]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readFloat(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readFloat(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readFloat(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readFloat(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readFloat(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readFloat(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readFloat(fieldId7); + break; + case (int) fieldId8: + results[7][indices[7]++] = pi.readFloat(fieldId8); + break; + case (int) fieldId9: + results[8][indices[8]++] = pi.readFloat(fieldId9); + break; + case (int) fieldId10: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + assertEquals(0.0f, results[0][0]); + assertEquals(0.0f, results[0][1]); + assertEquals(1.0f, results[1][0]); + assertEquals(1.0f, results[1][1]); + assertEquals(-1234.432f, results[2][0]); + assertEquals(-1234.432f, results[2][1]); + assertEquals(42.42f, results[3][0]); + assertEquals(42.42f, results[3][1]); + assertEquals(Float.MIN_NORMAL, results[4][0]); + assertEquals(Float.MIN_NORMAL, results[4][1]); + assertEquals(Float.MIN_VALUE, results[5][0]); + assertEquals(Float.MIN_VALUE, results[5][1]); + assertEquals(Float.NEGATIVE_INFINITY, results[6][0]); + assertEquals(Float.NEGATIVE_INFINITY, results[6][1]); + assertEquals(Float.NaN, results[7][0]); + assertEquals(Float.NaN, results[7][1]); + assertEquals(Float.POSITIVE_INFINITY, results[8][0]); + assertEquals(Float.POSITIVE_INFINITY, results[8][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new float[0]); + testRepeatedCompat(new float[]{0, 1, -1234.432f, 42.42f, + Float.MIN_NORMAL, Float.MIN_VALUE, Float.NEGATIVE_INFINITY, Float.NaN, + Float.POSITIVE_INFINITY, + }); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(float[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_FLOAT; + final long fieldId = fieldFlags | ((long) 21 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.floatFieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + float[] result = new float[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readFloat(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.floatFieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.floatFieldRepeated[i], result[i]); + } + } + + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FLOAT; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + final long fieldId9 = fieldFlags | ((long) 9 & 0x0ffffffffL); + final long fieldId10 = fieldFlags | ((long) 10 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f, + // 10 -> 1 + (byte) 0x52, + (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x3f, + // 3 -> -1234.432 + (byte) 0x1a, + (byte) 0x08, + (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4, + (byte) 0xd3, (byte) 0x4d, (byte) 0x9a, (byte) 0xc4, + // 4 -> 42.42 + (byte) 0x22, + (byte) 0x08, + (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42, + (byte) 0x14, (byte) 0xae, (byte) 0x29, (byte) 0x42, + // 5 -> Float.MIN_NORMAL + (byte) 0x2a, + (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x00, + // 6 -> DOUBLE.MIN_VALUE + (byte) 0x32, + (byte) 0x08, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 7 -> Float.NEGATIVE_INFINITY + (byte) 0x3a, + (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0xff, + // 8 -> Float.NaN + (byte) 0x42, + (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0xc0, (byte) 0x7f, + // 9 -> Float.POSITIVE_INFINITY + (byte) 0x4a, + (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x80, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + float[][] results = new float[9][2]; + int[] indices = new int[9]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readFloat(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readFloat(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readFloat(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readFloat(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readFloat(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readFloat(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readFloat(fieldId7); + break; + case (int) fieldId8: + results[7][indices[7]++] = pi.readFloat(fieldId8); + break; + case (int) fieldId9: + results[8][indices[8]++] = pi.readFloat(fieldId9); + break; + case (int) fieldId10: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + assertEquals(0.0f, results[0][0]); + assertEquals(0.0f, results[0][1]); + assertEquals(1.0f, results[1][0]); + assertEquals(1.0f, results[1][1]); + assertEquals(-1234.432f, results[2][0]); + assertEquals(-1234.432f, results[2][1]); + assertEquals(42.42f, results[3][0]); + assertEquals(42.42f, results[3][1]); + assertEquals(Float.MIN_NORMAL, results[4][0]); + assertEquals(Float.MIN_NORMAL, results[4][1]); + assertEquals(Float.MIN_VALUE, results[5][0]); + assertEquals(Float.MIN_VALUE, results[5][1]); + assertEquals(Float.NEGATIVE_INFINITY, results[6][0]); + assertEquals(Float.NEGATIVE_INFINITY, results[6][1]); + assertEquals(Float.NaN, results[7][0]); + assertEquals(Float.NaN, results[7][1]); + assertEquals(Float.POSITIVE_INFINITY, results[8][0]); + assertEquals(Float.POSITIVE_INFINITY, results[8][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new float[0]); + testPackedCompat(new float[]{0, 1, -1234.432f, 42.42f, + Float.MIN_NORMAL, Float.MIN_VALUE, Float.NEGATIVE_INFINITY, Float.NaN, + Float.POSITIVE_INFINITY, + }); + } + + /** + * Implementation of testPackedCompat with a given value. + */ + private void testPackedCompat(float[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_FLOAT; + final long fieldId = fieldFlags | ((long) 22 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.floatFieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + float[] result = new float[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readFloat(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.floatFieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.floatFieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x0d, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readInt(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FLOAT; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x04, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readFloat(fieldId1); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId2: + pi.readFloat(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readFloat(fieldId3); + // don't fail, length delimited is ok (represents packed floats) + break; + case (int) fieldId6: + pi.readFloat(fieldId6); + // don't fail, fixed32 is ok + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt32Test.java new file mode 100644 index 000000000000..0065870486f2 --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt32Test.java @@ -0,0 +1,565 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamInt32Test extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 6 -> MAX_VALUE + (byte) 0x30, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[] results = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0]); + assertEquals(1, results[1]); + assertEquals(-1, results[2]); + assertEquals(Integer.MIN_VALUE, results[3]); + assertEquals(Integer.MAX_VALUE, results[4]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1); + testReadCompat(Integer.MIN_VALUE); + testReadCompat(Integer.MAX_VALUE); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(int val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32; + final long fieldId = fieldFlags | ((long) 30 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.int32Field = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int result = 0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.int32Field, result); + } + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 6 -> MAX_VALUE + (byte) 0x30, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new int[0]); + testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32; + final long fieldId = fieldFlags | ((long) 31 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.int32FieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.int32FieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.int32FieldRepeated[i], result[i]); + } + } + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_INT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x02, + (byte) 0x00, + (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x02, + (byte) 0x01, + (byte) 0x01, + + // 6 -> MAX_VALUE + (byte) 0x32, + (byte) 0x0a, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 3 -> -1 + (byte) 0x1a, + (byte) 0x14, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + // 4 -> MIN_VALUE + (byte) 0x22, + (byte) 0x14, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + // 5 -> MAX_VALUE + (byte) 0x2a, + (byte) 0x0a, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new int[0]); + testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testPackedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT32; + final long fieldId = fieldFlags | ((long) 32 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.int32FieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.int32FieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.int32FieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x08, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x01, + (byte) 0x01, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readInt(fieldId1); + // don't fail, varint is ok + break; + case (int) fieldId2: + pi.readInt(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readInt(fieldId3); + // don't fail, length delimited is ok (represents packed int32) + break; + case (int) fieldId6: + pi.readInt(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt64Test.java new file mode 100644 index 000000000000..4d6d105e60b0 --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamInt64Test.java @@ -0,0 +1,645 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamInt64Test extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 8 -> Long.MAX_VALUE + (byte) 0x40, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> Integer.MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> Integer.MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + // 6 -> Long.MIN_VALUE + (byte) 0x30, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01, + // 7 -> Long.MAX_VALUE + (byte) 0x38, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[] results = new long[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0]); + assertEquals(1, results[1]); + assertEquals(-1, results[2]); + assertEquals(Integer.MIN_VALUE, results[3]); + assertEquals(Integer.MAX_VALUE, results[4]); + assertEquals(Long.MIN_VALUE, results[5]); + assertEquals(Long.MAX_VALUE, results[6]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1); + testReadCompat(Integer.MIN_VALUE); + testReadCompat(Integer.MAX_VALUE); + testReadCompat(Long.MIN_VALUE); + testReadCompat(Long.MAX_VALUE); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(long val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64; + final long fieldId = fieldFlags | ((long) 40 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.int64Field = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long result = 0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.int64Field, result); + } + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> Integer.MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> Integer.MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + // 6 -> Long.MIN_VALUE + (byte) 0x30, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01, + // 7 -> Long.MAX_VALUE + (byte) 0x38, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + + // 8 -> Long.MAX_VALUE + (byte) 0x40, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> Integer.MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> Integer.MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + // 6 -> Long.MIN_VALUE + (byte) 0x30, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01, + // 7 -> Long.MAX_VALUE + (byte) 0x38, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[][] results = new long[7][2]; + int[] indices = new int[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readLong(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + assertEquals(Long.MIN_VALUE, results[5][0]); + assertEquals(Long.MIN_VALUE, results[5][1]); + assertEquals(Long.MAX_VALUE, results[6][0]); + assertEquals(Long.MAX_VALUE, results[6][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new long[0]); + testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(long[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64; + final long fieldId = fieldFlags | ((long) 41 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.int64FieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long[] result = new long[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.int64FieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.int64FieldRepeated[i], result[i]); + } + } + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_INT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x02, + (byte) 0x00, + (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x02, + (byte) 0x01, + (byte) 0x01, + + // 8 -> Long.MAX_VALUE + (byte) 0x42, + (byte) 0x12, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + + // 3 -> -1 + (byte) 0x1a, + (byte) 0x14, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + // 4 -> Integer.MIN_VALUE + (byte) 0x22, + (byte) 0x14, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + // 5 -> Integer.MAX_VALUE + (byte) 0x2a, + (byte) 0x0a, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 6 -> Long.MIN_VALUE + (byte) 0x32, + (byte) 0x14, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01, + + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01, + + // 7 -> Long.MAX_VALUE + (byte) 0x3a, + (byte) 0x12, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[][] results = new long[7][2]; + int[] indices = new int[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readLong(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + assertEquals(Long.MIN_VALUE, results[5][0]); + assertEquals(Long.MIN_VALUE, results[5][1]); + assertEquals(Long.MAX_VALUE, results[6][0]); + assertEquals(Long.MAX_VALUE, results[6][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new long[0]); + testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testPackedCompat(long[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_INT64; + final long fieldId = fieldFlags | ((long) 42 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.int64FieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long[] result = new long[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.int64FieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.int64FieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x08, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readInt(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x01, + (byte) 0x01, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readLong(fieldId1); + // don't fail, varint is ok + break; + case (int) fieldId2: + pi.readLong(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readLong(fieldId3); + // don't fail, length delimited is ok (represents packed int64) + break; + case (int) fieldId6: + pi.readLong(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamObjectTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamObjectTest.java new file mode 100644 index 000000000000..5e49eeafb8af --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamObjectTest.java @@ -0,0 +1,507 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamObjectTest extends TestCase { + + + class SimpleObject { + public char mChar; + public char mLargeChar; + public String mString; + public SimpleObject mNested; + + void parseProto(ProtoInputStream pi) throws IOException { + final long uintFieldFlags = + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32; + final long stringFieldFlags = + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING; + final long messageFieldFlags = + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE; + final long charId = uintFieldFlags | ((long) 2 & 0x0ffffffffL); + final long largeCharId = uintFieldFlags | ((long) 5000 & 0x0ffffffffL); + final long stringId = stringFieldFlags | ((long) 4 & 0x0ffffffffL); + final long nestedId = messageFieldFlags | ((long) 5 & 0x0ffffffffL); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) charId: + mChar = (char) pi.readInt(charId); + break; + case (int) largeCharId: + mLargeChar = (char) pi.readInt(largeCharId); + break; + case (int) stringId: + mString = pi.readString(stringId); + break; + case (int) nestedId: + long token = pi.start(nestedId); + mNested = new SimpleObject(); + mNested.parseProto(pi); + pi.end(token); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + } + + } + + /** + * Test reading an object with one char in it. + */ + public void testObjectOneChar() throws IOException { + testObjectOneChar(0); + testObjectOneChar(1); + testObjectOneChar(5); + } + + /** + * Implementation of testObjectOneChar for a given chunkSize. + */ + private void testObjectOneChar(int chunkSize) throws IOException { + final long messageFieldFlags = + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE; + + final long messageId1 = messageFieldFlags | ((long) 1 & 0x0ffffffffL); + final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // Message 2 : { char 2 : 'c' } + (byte) 0x12, (byte) 0x02, (byte) 0x10, (byte) 0x63, + // Message 1 : { char 2 : 'b' } + (byte) 0x0a, (byte) 0x02, (byte) 0x10, (byte) 0x62, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + + SimpleObject result = null; + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) messageId1: + final long token = pi.start(messageId1); + result = new SimpleObject(); + result.parseProto(pi); + pi.end(token); + break; + case (int) messageId2: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertNotNull(result); + assertEquals('b', result.mChar); + } + + /** + * Test reading an object with one multibyte unicode char in it. + */ + public void testObjectOneLargeChar() throws IOException { + testObjectOneLargeChar(0); + testObjectOneLargeChar(1); + testObjectOneLargeChar(5); + } + + /** + * Implementation of testObjectOneLargeChar for a given chunkSize. + */ + private void testObjectOneLargeChar(int chunkSize) throws IOException { + final long messageFieldFlags = + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE; + + final long messageId1 = messageFieldFlags | ((long) 1 & 0x0ffffffffL); + final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // Message 2 : { char 5000 : '\u3110' } + (byte) 0x12, (byte) 0x05, (byte) 0xc0, (byte) 0xb8, + (byte) 0x02, (byte) 0x90, (byte) 0x62, + // Message 1 : { char 5000 : '\u3110' } + (byte) 0x0a, (byte) 0x05, (byte) 0xc0, (byte) 0xb8, + (byte) 0x02, (byte) 0x90, (byte) 0x62, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + + SimpleObject result = null; + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) messageId1: + final long token = pi.start(messageId1); + result = new SimpleObject(); + result.parseProto(pi); + pi.end(token); + break; + case (int) messageId2: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertNotNull(result); + assertEquals('\u3110', result.mLargeChar); + } + + /** + * Test reading a char, then an object, then a char. + */ + public void testObjectAndTwoChars() throws IOException { + testObjectAndTwoChars(0); + testObjectAndTwoChars(1); + testObjectAndTwoChars(5); + } + + /** + * Implementation of testObjectAndTwoChars for a given chunkSize. + */ + private void testObjectAndTwoChars(int chunkSize) throws IOException { + final long uintFieldFlags = + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32; + final long messageFieldFlags = + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE; + + final long charId1 = uintFieldFlags | ((long) 1 & 0x0ffffffffL); + final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL); + final long charId4 = uintFieldFlags | ((long) 4 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 'a' + (byte) 0x08, (byte) 0x61, + // Message 1 : { char 2 : 'b' } + (byte) 0x12, (byte) 0x02, (byte) 0x10, (byte) 0x62, + // 4 -> 'c' + (byte) 0x20, (byte) 0x63, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + + SimpleObject obj = null; + char char1 = '\0'; + char char4 = '\0'; + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) charId1: + char1 = (char) pi.readInt(charId1); + break; + case (int) messageId2: + final long token = pi.start(messageId2); + obj = new SimpleObject(); + obj.parseProto(pi); + pi.end(token); + break; + case (int) charId4: + char4 = (char) pi.readInt(charId4); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals('a', char1); + assertNotNull(obj); + assertEquals('b', obj.mChar); + assertEquals('c', char4); + } + + /** + * Test reading a char, then an object with an int and a string in it, then a char. + */ + public void testComplexObject() throws IOException { + testComplexObject(0); + testComplexObject(1); + testComplexObject(5); + } + + /** + * Implementation of testComplexObject for a given chunkSize. + */ + private void testComplexObject(int chunkSize) throws IOException { + final long uintFieldFlags = + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32; + final long messageFieldFlags = + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE; + + final long charId1 = uintFieldFlags | ((long) 1 & 0x0ffffffffL); + final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL); + final long charId4 = uintFieldFlags | ((long) 4 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 'x' + (byte) 0x08, (byte) 0x78, + // begin object 2 + (byte) 0x12, (byte) 0x10, + // 2 -> 'y' + (byte) 0x10, (byte) 0x79, + // 4 -> "abcdefghijkl" + (byte) 0x22, (byte) 0x0c, + (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, + (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6a, (byte) 0x6b, (byte) 0x6c, + // 4 -> 'z' + (byte) 0x20, (byte) 0x7a, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + + SimpleObject obj = null; + char char1 = '\0'; + char char4 = '\0'; + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) charId1: + char1 = (char) pi.readInt(charId1); + break; + case (int) messageId2: + final long token = pi.start(messageId2); + obj = new SimpleObject(); + obj.parseProto(pi); + pi.end(token); + break; + case (int) charId4: + char4 = (char) pi.readInt(charId4); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals('x', char1); + assertNotNull(obj); + assertEquals('y', obj.mChar); + assertEquals("abcdefghijkl", obj.mString); + assertEquals('z', char4); + } + + /** + * Test reading 3 levels deep of objects. + */ + public void testDeepObjects() throws IOException { + testDeepObjects(0); + testDeepObjects(1); + testDeepObjects(5); + } + + /** + * Implementation of testDeepObjects for a given chunkSize. + */ + private void testDeepObjects(int chunkSize) throws IOException { + final long messageFieldFlags = + ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE; + final long messageId2 = messageFieldFlags | ((long) 2 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // begin object id 2 + (byte) 0x12, (byte) 0x1a, + // 2 -> 'a' + (byte) 0x10, (byte) 0x61, + // begin nested object id 5 + (byte) 0x2a, (byte) 0x15, + // 5000 -> '\u3110' + (byte) 0xc0, (byte) 0xb8, + (byte) 0x02, (byte) 0x90, (byte) 0x62, + // begin nested object id 5 + (byte) 0x2a, (byte) 0x0e, + // 4 -> "abcdefghijkl" + (byte) 0x22, (byte) 0x0c, + (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66, + (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6a, (byte) 0x6b, (byte) 0x6c, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + + SimpleObject obj = null; + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) messageId2: + final long token = pi.start(messageId2); + obj = new SimpleObject(); + obj.parseProto(pi); + pi.end(token); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertNotNull(obj); + assertEquals('a', obj.mChar); + assertNotNull(obj.mNested); + assertEquals('\u3110', obj.mNested.mLargeChar); + assertNotNull(obj.mNested.mNested); + assertEquals("abcdefghijkl", obj.mNested.mNested.mString); + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> {1} + (byte) 0x0a, + (byte) 0x01, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readInt(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_MESSAGE; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x01, + (byte) 0x01, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readBytes(fieldId1); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId2: + pi.readBytes(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readBytes(fieldId3); + // don't fail, length delimited is ok + break; + case (int) fieldId6: + pi.readBytes(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed32Test.java new file mode 100644 index 000000000000..75c88a44614b --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed32Test.java @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamSFixed32Test extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x15, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> 1 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x1d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x25, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 5 -> Integer.MAX_VALUE + (byte) 0x2d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[] results = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0]); + assertEquals(1, results[1]); + assertEquals(-1, results[2]); + assertEquals(Integer.MIN_VALUE, results[3]); + assertEquals(Integer.MAX_VALUE, results[4]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1); + testReadCompat(Integer.MIN_VALUE); + testReadCompat(Integer.MAX_VALUE); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(int val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32; + final long fieldId = fieldFlags | ((long) 110 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sfixed32Field = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int result = 0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sfixed32Field, result); + } + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0d, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x15, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x1d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x25, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 5 -> Integer.MAX_VALUE + (byte) 0x2d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + + // 6 -> 1 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // 1 -> 0 - default value, written when repeated + (byte) 0x0d, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x15, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x1d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x25, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 5 -> Integer.MAX_VALUE + (byte) 0x2d, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new int[0]); + testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32; + final long fieldId = fieldFlags | ((long) 111 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sfixed32FieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sfixed32FieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.sfixed32FieldRepeated[i], result[i]); + } + } + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SFIXED32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x08, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> 1 + (byte) 0x32, + (byte) 0x08, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x1a, + (byte) 0x08, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x22, + (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 5 -> Integer.MAX_VALUE + (byte) 0x2a, + (byte) 0x08, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new int[0]); + testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testPackedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED32; + final long fieldId = fieldFlags | ((long) 112 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sfixed32FieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sfixed32FieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.sfixed32FieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x08, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x04, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readInt(fieldId1); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId2: + pi.readInt(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readInt(fieldId3); + // don't fail, length delimited is ok (represents packed sfixed32) + break; + case (int) fieldId6: + pi.readInt(fieldId6); + // don't fail, fixed32 is ok + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed64Test.java new file mode 100644 index 000000000000..4c65cf49318d --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSFixed64Test.java @@ -0,0 +1,648 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamSFixed64Test extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 8 -> 1 + (byte) 0x41, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x19, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x21, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 5 -> Integer.MAX_VALUE + (byte) 0x29, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> Long.MIN_VALUE + (byte) 0x31, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 7 -> Long.MAX_VALUE + (byte) 0x39, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[] results = new long[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0]); + assertEquals(1, results[1]); + assertEquals(-1, results[2]); + assertEquals(Integer.MIN_VALUE, results[3]); + assertEquals(Integer.MAX_VALUE, results[4]); + assertEquals(Long.MIN_VALUE, results[5]); + assertEquals(Long.MAX_VALUE, results[6]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1); + testReadCompat(Integer.MIN_VALUE); + testReadCompat(Integer.MAX_VALUE); + testReadCompat(Long.MIN_VALUE); + testReadCompat(Long.MAX_VALUE); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(long val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64; + final long fieldId = fieldFlags | ((long) 120 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sfixed64Field = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long result = 0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sfixed64Field, result); + } + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x09, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x19, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x21, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 5 -> Integer.MAX_VALUE + (byte) 0x29, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> Long.MIN_VALUE + (byte) 0x31, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 7 -> Long.MAX_VALUE + (byte) 0x39, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + + // 8 -> 1 + (byte) 0x41, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // 1 -> 0 - default value, written when repeated + (byte) 0x09, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x19, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x21, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 5 -> Integer.MAX_VALUE + (byte) 0x29, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> Long.MIN_VALUE + (byte) 0x31, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 7 -> Long.MAX_VALUE + (byte) 0x39, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[][] results = new long[7][2]; + int[] indices = new int[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readLong(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + assertEquals(Long.MIN_VALUE, results[5][0]); + assertEquals(Long.MIN_VALUE, results[5][1]); + assertEquals(Long.MAX_VALUE, results[6][0]); + assertEquals(Long.MAX_VALUE, results[6][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new long[0]); + testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(long[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64; + final long fieldId = fieldFlags | ((long) 121 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sfixed64FieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long[] result = new long[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sfixed64FieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.sfixed64FieldRepeated[i], result[i]); + } + } + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SFIXED64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x10, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 8 -> 1 + (byte) 0x42, + (byte) 0x10, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 -> -1 + (byte) 0x1a, + (byte) 0x10, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 4 -> Integer.MIN_VALUE + (byte) 0x22, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // 5 -> Integer.MAX_VALUE + (byte) 0x2a, + (byte) 0x10, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 -> Long.MIN_VALUE + (byte) 0x32, + (byte) 0x10, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x80, + // 7 -> Long.MAX_VALUE + (byte) 0x3a, + (byte) 0x10, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[][] results = new long[7][2]; + int[] indices = new int[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readLong(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + assertEquals(Long.MIN_VALUE, results[5][0]); + assertEquals(Long.MIN_VALUE, results[5][1]); + assertEquals(Long.MAX_VALUE, results[6][0]); + assertEquals(Long.MAX_VALUE, results[6][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new long[0]); + testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testPackedCompat(long[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SFIXED64; + final long fieldId = fieldFlags | ((long) 122 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sfixed64FieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long[] result = new long[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sfixed64FieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.sfixed64FieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x08, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readInt(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SFIXED64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x08, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readLong(fieldId1); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId2: + pi.readLong(fieldId2); + // don't fail, fixed32 is ok + break; + case (int) fieldId3: + pi.readLong(fieldId3); + // don't fail, length delimited is ok (represents packed sfixed64) + break; + case (int) fieldId6: + pi.readLong(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt32Test.java new file mode 100644 index 000000000000..6854cd8aad28 --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt32Test.java @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamSInt32Test extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x10, + (byte) 0x02, + // 6 -> MAX_VALUE + (byte) 0x30, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 3 -> -1 + (byte) 0x18, + (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[] results = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0]); + assertEquals(1, results[1]); + assertEquals(-1, results[2]); + assertEquals(Integer.MIN_VALUE, results[3]); + assertEquals(Integer.MAX_VALUE, results[4]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1); + testReadCompat(Integer.MIN_VALUE); + testReadCompat(Integer.MAX_VALUE); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(int val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32; + final long fieldId = fieldFlags | ((long) 70 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sint32Field = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int result = 0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sint32Field, result); + } + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x02, + // 3 -> -1 + (byte) 0x18, + (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + + // 6 -> MAX_VALUE + (byte) 0x30, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x02, + // 3 -> -1 + (byte) 0x18, + (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new int[0]); + testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32; + final long fieldId = fieldFlags | ((long) 71 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sint32FieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sint32FieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.sint32FieldRepeated[i], result[i]); + } + } + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SINT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x02, + (byte) 0x00, + (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x02, + (byte) 0x02, + (byte) 0x02, + // 6 -> MAX_VALUE + (byte) 0x32, + (byte) 0x0a, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 3 -> -1 + (byte) 0x1a, + (byte) 0x02, + (byte) 0x01, + (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x22, + (byte) 0x0a, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 5 -> MAX_VALUE + (byte) 0x2a, + (byte) 0x0a, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new int[0]); + testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testPackedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT32; + final long fieldId = fieldFlags | ((long) 72 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sint32FieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sint32FieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.sint32FieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x08, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x01, + (byte) 0x01, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readInt(fieldId1); + // don't fail, varint is ok + break; + case (int) fieldId2: + pi.readInt(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readInt(fieldId3); + // don't fail, length delimited is ok (represents packed sint32) + break; + case (int) fieldId6: + pi.readInt(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt64Test.java new file mode 100644 index 000000000000..c53e9d72562a --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamSInt64Test.java @@ -0,0 +1,622 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamSInt64Test extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x10, + (byte) 0x02, + // 8 -> Integer.MAX_VALUE + (byte) 0x40, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 3 -> -1 + (byte) 0x18, + (byte) 0x01, + // 4 -> Integer.MIN_VALUE + (byte) 0x20, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 5 -> Integer.MAX_VALUE + (byte) 0x28, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 6 -> Long.MIN_VALUE + (byte) 0x30, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 7 -> Long.MAX_VALUE + (byte) 0x38, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[] results = new long[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0]); + assertEquals(1, results[1]); + assertEquals(-1, results[2]); + assertEquals(Integer.MIN_VALUE, results[3]); + assertEquals(Integer.MAX_VALUE, results[4]); + assertEquals(Long.MIN_VALUE, results[5]); + assertEquals(Long.MAX_VALUE, results[6]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1); + testReadCompat(Integer.MIN_VALUE); + testReadCompat(Integer.MAX_VALUE); + testReadCompat(Long.MIN_VALUE); + testReadCompat(Long.MAX_VALUE); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(long val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64; + final long fieldId = fieldFlags | ((long) 80 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sint64Field = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long result = 0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sint64Field, result); + } + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x02, + // 3 -> -1 + (byte) 0x18, + (byte) 0x01, + // 4 -> Integer.MIN_VALUE + (byte) 0x20, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 5 -> Integer.MAX_VALUE + (byte) 0x28, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 6 -> Long.MIN_VALUE + (byte) 0x30, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 7 -> Long.MAX_VALUE + (byte) 0x38, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + // 8 -> Integer.MAX_VALUE + (byte) 0x40, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + + + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x02, + // 3 -> -1 + (byte) 0x18, + (byte) 0x01, + // 4 -> Integer.MIN_VALUE + (byte) 0x20, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 5 -> Integer.MAX_VALUE + (byte) 0x28, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 6 -> Long.MIN_VALUE + (byte) 0x30, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 7 -> Long.MAX_VALUE + (byte) 0x38, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[][] results = new long[7][2]; + int[] indices = new int[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readLong(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + assertEquals(Long.MIN_VALUE, results[5][0]); + assertEquals(Long.MIN_VALUE, results[5][1]); + assertEquals(Long.MAX_VALUE, results[6][0]); + assertEquals(Long.MAX_VALUE, results[6][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new long[0]); + testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(long[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64; + final long fieldId = fieldFlags | ((long) 81 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sint64FieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long[] result = new long[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sint64FieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.sint64FieldRepeated[i], result[i]); + } + } + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_SINT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x02, + (byte) 0x00, + (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x02, + (byte) 0x02, + (byte) 0x02, + // 8 -> Integer.MAX_VALUE + (byte) 0x42, + (byte) 0x0a, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 3 -> -1 + (byte) 0x1a, + (byte) 0x02, + (byte) 0x01, + (byte) 0x01, + // 4 -> Integer.MIN_VALUE + (byte) 0x22, + (byte) 0x0a, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 5 -> Integer.MAX_VALUE + (byte) 0x2a, + (byte) 0x0a, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x0f, + // 6 -> Long.MIN_VALUE + (byte) 0x32, + (byte) 0x14, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 7 -> Long.MAX_VALUE + (byte) 0x3a, + (byte) 0x14, + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0xfe, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01 + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[][] results = new long[7][2]; + int[] indices = new int[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readLong(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + assertEquals(Long.MIN_VALUE, results[5][0]); + assertEquals(Long.MIN_VALUE, results[5][1]); + assertEquals(Long.MAX_VALUE, results[6][0]); + assertEquals(Long.MAX_VALUE, results[6][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new long[0]); + testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testPackedCompat(long[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_SINT64; + final long fieldId = fieldFlags | ((long) 82 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.sint64FieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long[] result = new long[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.sint64FieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.sint64FieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x08, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readInt(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_SINT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x01, + (byte) 0x01, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readLong(fieldId1); + // don't fail, varint is ok + break; + case (int) fieldId2: + pi.readLong(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readLong(fieldId3); + // don't fail, length delimited is ok (represents packed sint64) + break; + case (int) fieldId6: + pi.readLong(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamStringTest.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamStringTest.java new file mode 100644 index 000000000000..816d5f900a3d --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamStringTest.java @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamStringTest extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> null - default value, not written + // 2 -> "" - default value, not written + // 3 -> "abcd\u3110!" + (byte) 0x1a, + (byte) 0x08, + (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, + (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21, + // 5 -> "Hi" + (byte) 0x2a, + (byte) 0x02, + (byte) 0x48, (byte) 0x69, + // 4 -> "Hi" + (byte) 0x22, + (byte) 0x02, + (byte) 0x48, (byte) 0x69, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + String[] results = new String[4]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0] = pi.readString(fieldId1); + break; + case (int) fieldId2: + results[1] = pi.readString(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readString(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readString(fieldId4); + break; + case (int) fieldId5: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertNull(results[0]); + assertNull(results[1]); + assertEquals("abcd\u3110!", results[2]); + assertEquals("Hi", results[3]); + } + + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(""); + testReadCompat("abcd\u3110!"); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(String val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING; + final long fieldId = fieldFlags | ((long) 140 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.stringField = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + String result = ""; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readString(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.stringField, result); + } + + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_STRING; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> null - default value, written when repeated + (byte) 0x0a, + (byte) 0x00, + // 2 -> "" - default value, written when repeated + (byte) 0x12, + (byte) 0x00, + // 3 -> "abcd\u3110!" + (byte) 0x1a, + (byte) 0x08, + (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, + (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21, + // 4 -> "Hi" + (byte) 0x22, + (byte) 0x02, + (byte) 0x48, (byte) 0x69, + + // 5 -> "Hi" + (byte) 0x2a, + (byte) 0x02, + (byte) 0x48, (byte) 0x69, + + // 1 -> null - default value, written when repeated + (byte) 0x0a, + (byte) 0x00, + // 2 -> "" - default value, written when repeated + (byte) 0x12, + (byte) 0x00, + // 3 -> "abcd\u3110!" + (byte) 0x1a, + (byte) 0x08, + (byte) 0x61, (byte) 0x62, (byte) 0x63, (byte) 0x64, + (byte) 0xe3, (byte) 0x84, (byte) 0x90, (byte) 0x21, + // 4 -> "Hi" + (byte) 0x22, + (byte) 0x02, + (byte) 0x48, (byte) 0x69, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + String[][] results = new String[4][2]; + int[] indices = new int[4]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readString(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readString(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readString(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readString(fieldId4); + break; + case (int) fieldId5: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals("", results[0][0]); + assertEquals("", results[0][1]); + assertEquals("", results[1][0]); + assertEquals("", results[1][1]); + assertEquals("abcd\u3110!", results[2][0]); + assertEquals("abcd\u3110!", results[2][1]); + assertEquals("Hi", results[3][0]); + assertEquals("Hi", results[3][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new String[0]); + testRepeatedCompat(new String[]{"", "abcd\u3110!", "Hi"}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(String[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_STRING; + final long fieldId = fieldFlags | ((long) 141 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.stringFieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + String[] result = new String[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readString(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.stringFieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.stringFieldRepeated[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> {1} + (byte) 0x0a, + (byte) 0x01, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readInt(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_STRING; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x01, + (byte) 0x01, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readString(fieldId1); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId2: + pi.readString(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readString(fieldId3); + // don't fail, length delimited is ok (represents packed booleans) + break; + case (int) fieldId6: + pi.readString(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } + +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt32Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt32Test.java new file mode 100644 index 000000000000..50fc537767a4 --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt32Test.java @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamUInt32Test extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 6 -> MAX_VALUE + (byte) 0x30, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[] results = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0]); + assertEquals(1, results[1]); + assertEquals(-1, results[2]); + assertEquals(Integer.MIN_VALUE, results[3]); + assertEquals(Integer.MAX_VALUE, results[4]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1); + testReadCompat(Integer.MIN_VALUE); + testReadCompat(Integer.MAX_VALUE); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(int val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32; + final long fieldId = fieldFlags | ((long) 50 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.uint32Field = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int result = 0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.uint32Field, result); + } + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 6 -> MAX_VALUE + (byte) 0x30, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new int[0]); + testRepeatedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32; + final long fieldId = fieldFlags | ((long) 51 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.uint32FieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.uint32FieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.uint32FieldRepeated[i], result[i]); + } + } + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_UINT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x02, + (byte) 0x00, + (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x02, + (byte) 0x01, + (byte) 0x01, + + // 6 -> MAX_VALUE + (byte) 0x32, + (byte) 0x0a, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 3 -> -1 + (byte) 0x1a, + (byte) 0x14, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + // 4 -> MIN_VALUE + (byte) 0x22, + (byte) 0x14, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + // 5 -> MAX_VALUE + (byte) 0x2a, + (byte) 0x0a, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + int[][] results = new int[5][2]; + int[] indices = new int[5]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readInt(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readInt(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readInt(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readInt(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readInt(fieldId5); + break; + case (int) fieldId6: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new int[0]); + testPackedCompat(new int[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testPackedCompat(int[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT32; + final long fieldId = fieldFlags | ((long) 52 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.uint32FieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + int[] result = new int[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readInt(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.uint32FieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.uint32FieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x08, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readLong(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT32; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x01, + (byte) 0x01, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readInt(fieldId1); + // don't fail, varint is ok + break; + case (int) fieldId2: + pi.readInt(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readInt(fieldId3); + // don't fail, length delimited is ok (represents packed uint32) + break; + case (int) fieldId6: + pi.readInt(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt64Test.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt64Test.java new file mode 100644 index 000000000000..20969e9056a9 --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoInputStreamUInt64Test.java @@ -0,0 +1,641 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoStream; +import android.util.proto.WireTypeMismatchException; + +import com.android.test.protoinputstream.nano.Test; + +import com.google.protobuf.nano.MessageNano; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ProtoInputStreamUInt64Test extends TestCase { + + public void testRead() throws IOException { + testRead(0); + testRead(1); + testRead(5); + } + + private void testRead(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, not written + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 8 -> Integer.MAX_VALUE + (byte) 0x40, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> Integer.MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> Integer.MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + // 6 -> Long.MIN_VALUE + (byte) 0x30, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01, + // 7 -> Long.MAX_VALUE + (byte) 0x38, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[] results = new long[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + fail("Should never reach this"); + break; + case (int) fieldId2: + results[1] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0]); + assertEquals(1, results[1]); + assertEquals(-1, results[2]); + assertEquals(Integer.MIN_VALUE, results[3]); + assertEquals(Integer.MAX_VALUE, results[4]); + assertEquals(Long.MIN_VALUE, results[5]); + assertEquals(Long.MAX_VALUE, results[6]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testReadCompat() throws Exception { + testReadCompat(0); + testReadCompat(1); + testReadCompat(-1); + testReadCompat(Integer.MIN_VALUE); + testReadCompat(Integer.MAX_VALUE); + testReadCompat(Long.MIN_VALUE); + testReadCompat(Long.MAX_VALUE); + } + + /** + * Implementation of testReadCompat with a given value. + */ + private void testReadCompat(long val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64; + final long fieldId = fieldFlags | ((long) 60 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.uint64Field = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long result = 0; // start off with default value + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.uint64Field, result); + } + + public void testRepeated() throws IOException { + testRepeated(0); + testRepeated(1); + testRepeated(5); + } + + private void testRepeated(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> Integer.MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> Integer.MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + // 6 -> Long.MIN_VALUE + (byte) 0x30, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01, + // 7 -> Long.MAX_VALUE + (byte) 0x38, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + + // 8 -> Integer.MAX_VALUE + (byte) 0x40, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 1 -> 0 - default value, written when repeated + (byte) 0x08, + (byte) 0x00, + // 2 -> 1 + (byte) 0x10, + (byte) 0x01, + // 3 -> -1 + (byte) 0x18, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 4 -> Integer.MIN_VALUE + (byte) 0x20, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + // 5 -> Integer.MAX_VALUE + (byte) 0x28, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + // 6 -> Long.MIN_VALUE + (byte) 0x30, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01, + // 7 -> Long.MAX_VALUE + (byte) 0x38, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[][] results = new long[7][2]; + int[] indices = new int[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readLong(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + assertEquals(Long.MIN_VALUE, results[5][0]); + assertEquals(Long.MIN_VALUE, results[5][1]); + assertEquals(Long.MAX_VALUE, results[6][0]); + assertEquals(Long.MAX_VALUE, results[6][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testRepeatedCompat() throws Exception { + testRepeatedCompat(new long[0]); + testRepeatedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testRepeatedCompat(long[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64; + final long fieldId = fieldFlags | ((long) 61 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.uint64FieldRepeated = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long[] result = new long[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.uint64FieldRepeated.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.uint64FieldRepeated[i], result[i]); + } + } + + public void testPacked() throws IOException { + testPacked(0); + testPacked(1); + testPacked(5); + } + + private void testPacked(int chunkSize) throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_PACKED | ProtoStream.FIELD_TYPE_UINT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId4 = fieldFlags | ((long) 4 & 0x0ffffffffL); + final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + final long fieldId7 = fieldFlags | ((long) 7 & 0x0ffffffffL); + final long fieldId8 = fieldFlags | ((long) 8 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 0 - default value, written when repeated + (byte) 0x0a, + (byte) 0x02, + (byte) 0x00, + (byte) 0x00, + // 2 -> 1 + (byte) 0x12, + (byte) 0x02, + (byte) 0x01, + (byte) 0x01, + + // 8 -> Integer.MAX_VALUE + (byte) 0x42, + (byte) 0x0a, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 3 -> -1 + (byte) 0x1a, + (byte) 0x14, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + // 4 -> Integer.MIN_VALUE + (byte) 0x22, + (byte) 0x14, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0xf8, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x01, + + // 5 -> Integer.MAX_VALUE + (byte) 0x2a, + (byte) 0x0a, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x07, + + // 6 -> Long.MIN_VALUE + (byte) 0x32, + (byte) 0x14, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01, + + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, + (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x80, (byte) 0x01, + + // 7 -> Long.MAX_VALUE + (byte) 0x3a, + (byte) 0x12, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x7f, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream, chunkSize); + long[][] results = new long[7][2]; + int[] indices = new int[7]; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + + switch (pi.getFieldNumber()) { + case (int) fieldId1: + results[0][indices[0]++] = pi.readLong(fieldId1); + break; + case (int) fieldId2: + results[1][indices[1]++] = pi.readLong(fieldId2); + break; + case (int) fieldId3: + results[2][indices[2]++] = pi.readLong(fieldId3); + break; + case (int) fieldId4: + results[3][indices[3]++] = pi.readLong(fieldId4); + break; + case (int) fieldId5: + results[4][indices[4]++] = pi.readLong(fieldId5); + break; + case (int) fieldId6: + results[5][indices[5]++] = pi.readLong(fieldId6); + break; + case (int) fieldId7: + results[6][indices[6]++] = pi.readLong(fieldId7); + break; + case (int) fieldId8: + // Intentionally don't read the data. Parse should continue normally + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + stream.close(); + + assertEquals(0, results[0][0]); + assertEquals(0, results[0][1]); + assertEquals(1, results[1][0]); + assertEquals(1, results[1][1]); + assertEquals(-1, results[2][0]); + assertEquals(-1, results[2][1]); + assertEquals(Integer.MIN_VALUE, results[3][0]); + assertEquals(Integer.MIN_VALUE, results[3][1]); + assertEquals(Integer.MAX_VALUE, results[4][0]); + assertEquals(Integer.MAX_VALUE, results[4][1]); + assertEquals(Long.MIN_VALUE, results[5][0]); + assertEquals(Long.MIN_VALUE, results[5][1]); + assertEquals(Long.MAX_VALUE, results[6][0]); + assertEquals(Long.MAX_VALUE, results[6][1]); + } + + /** + * Test that reading with ProtoInputStream matches, and can read the output of standard proto. + */ + public void testPackedCompat() throws Exception { + testPackedCompat(new long[0]); + testPackedCompat(new long[]{0, 1, -1, Integer.MIN_VALUE, Integer.MAX_VALUE}); + } + + /** + * Implementation of testRepeatedCompat with a given value. + */ + private void testPackedCompat(long[] val) throws Exception { + final long fieldFlags = ProtoStream.FIELD_COUNT_REPEATED | ProtoStream.FIELD_TYPE_UINT64; + final long fieldId = fieldFlags | ((long) 62 & 0x0ffffffffL); + + final Test.All all = new Test.All(); + all.uint64FieldPacked = val; + + final byte[] proto = MessageNano.toByteArray(all); + + final ProtoInputStream pi = new ProtoInputStream(proto); + final Test.All readback = Test.All.parseFrom(proto); + + long[] result = new long[val.length]; + int index = 0; + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (pi.getFieldNumber()) { + case (int) fieldId: + result[index++] = pi.readLong(fieldId); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } + + assertEquals(readback.uint64FieldPacked.length, result.length); + for (int i = 0; i < result.length; i++) { + assertEquals(readback.uint64FieldPacked[i], result[i]); + } + } + + /** + * Test that using the wrong read method throws an exception + */ + public void testBadReadType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 -> 1 + (byte) 0x08, + (byte) 0x01, + }; + + ProtoInputStream pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readFloat(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readDouble(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readInt(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBoolean(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readBytes(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + + pi = new ProtoInputStream(protobuf); + pi.isNextField(fieldId1); + try { + pi.readString(fieldId1); + fail("Should have throw IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // good + } + } + + /** + * Test that unexpected wrong wire types will throw an exception + */ + public void testBadWireType() throws IOException { + final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_UINT64; + + final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL); + final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL); + final long fieldId3 = fieldFlags | ((long) 3 & 0x0ffffffffL); + final long fieldId6 = fieldFlags | ((long) 6 & 0x0ffffffffL); + + final byte[] protobuf = new byte[]{ + // 1 : varint -> 1 + (byte) 0x08, + (byte) 0x01, + // 2 : fixed64 -> 0x1 + (byte) 0x11, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // 3 : length delimited -> { 1 } + (byte) 0x1a, + (byte) 0x01, + (byte) 0x01, + // 6 : fixed32 + (byte) 0x35, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + + InputStream stream = new ByteArrayInputStream(protobuf); + final ProtoInputStream pi = new ProtoInputStream(stream); + + while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + try { + switch (pi.getFieldNumber()) { + case (int) fieldId1: + pi.readLong(fieldId1); + // don't fail, varint is ok + break; + case (int) fieldId2: + pi.readLong(fieldId2); + fail("Should have thrown a WireTypeMismatchException"); + break; + case (int) fieldId3: + pi.readLong(fieldId3); + // don't fail, length delimited is ok (represents packed uint64) + break; + case (int) fieldId6: + pi.readLong(fieldId6); + fail("Should have thrown a WireTypeMismatchException"); + break; + default: + fail("Unexpected field id " + pi.getFieldNumber()); + } + } catch (WireTypeMismatchException wtme) { + // good + } + } + stream.close(); + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java new file mode 100644 index 000000000000..cdf6ae20f370 --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/ProtoTests.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.protoinputstream; + +import junit.framework.TestSuite; + +public class ProtoTests { + public static TestSuite suite() { + TestSuite suite = new TestSuite(ProtoTests.class.getName()); + + suite.addTestSuite(ProtoInputStreamDoubleTest.class); + suite.addTestSuite(ProtoInputStreamFloatTest.class); + suite.addTestSuite(ProtoInputStreamInt32Test.class); + suite.addTestSuite(ProtoInputStreamInt64Test.class); + suite.addTestSuite(ProtoInputStreamUInt32Test.class); + suite.addTestSuite(ProtoInputStreamUInt64Test.class); + suite.addTestSuite(ProtoInputStreamSInt32Test.class); + suite.addTestSuite(ProtoInputStreamSInt64Test.class); + suite.addTestSuite(ProtoInputStreamFixed32Test.class); + suite.addTestSuite(ProtoInputStreamFixed64Test.class); + suite.addTestSuite(ProtoInputStreamSFixed32Test.class); + suite.addTestSuite(ProtoInputStreamSFixed64Test.class); + suite.addTestSuite(ProtoInputStreamBoolTest.class); + suite.addTestSuite(ProtoInputStreamStringTest.class); + suite.addTestSuite(ProtoInputStreamBytesTest.class); + suite.addTestSuite(ProtoInputStreamEnumTest.class); + suite.addTestSuite(ProtoInputStreamObjectTest.class); + + return suite; + } +} diff --git a/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/test.proto b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/test.proto new file mode 100644 index 000000000000..9ff1d7e2d19a --- /dev/null +++ b/tests/ProtoInputStreamTests/src/com/android/test/protoinputstream/test.proto @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package com.android.test.protoinputstream; + +/** + * Enum that outside the scope of any classes. + */ +enum Outside { + OUTSIDE_0 = 0; + OUTSIDE_1 = 1; +}; + +/** + * Message that is recursive. + */ +message Nested { + optional int32 data = 10001; + optional Nested nested = 10002; +}; + +/** + * Message with all of the field types. + */ +message All { + /** + * Enum that is inside the scope of a class. + */ + enum Inside { + option allow_alias = true; + INSIDE_0 = 0; + INSIDE_1 = 1; + INSIDE_1A = 1; + }; + + optional double double_field = 10; + repeated double double_field_repeated = 11; + repeated double double_field_packed = 12 [packed=true]; + + optional float float_field = 20; + repeated float float_field_repeated = 21; + repeated float float_field_packed = 22 [packed=true]; + + optional int32 int32_field = 30; + repeated int32 int32_field_repeated = 31; + repeated int32 int32_field_packed = 32 [packed=true]; + + optional int64 int64_field = 40; + repeated int64 int64_field_repeated = 41; + repeated int64 int64_field_packed = 42 [packed=true]; + + optional uint32 uint32_field = 50; + repeated uint32 uint32_field_repeated = 51; + repeated uint32 uint32_field_packed = 52 [packed=true]; + + optional uint64 uint64_field = 60; + repeated uint64 uint64_field_repeated = 61; + repeated uint64 uint64_field_packed = 62 [packed=true]; + + optional sint32 sint32_field = 70; + repeated sint32 sint32_field_repeated = 71; + repeated sint32 sint32_field_packed = 72 [packed=true]; + + optional sint64 sint64_field = 80; + repeated sint64 sint64_field_repeated = 81; + repeated sint64 sint64_field_packed = 82 [packed=true]; + + optional fixed32 fixed32_field = 90; + repeated fixed32 fixed32_field_repeated = 91; + repeated fixed32 fixed32_field_packed = 92 [packed=true]; + + optional fixed64 fixed64_field = 100; + repeated fixed64 fixed64_field_repeated = 101; + repeated fixed64 fixed64_field_packed = 102 [packed=true]; + + optional sfixed32 sfixed32_field = 110; + repeated sfixed32 sfixed32_field_repeated = 111; + repeated sfixed32 sfixed32_field_packed = 112 [packed=true]; + + optional sfixed64 sfixed64_field = 120; + repeated sfixed64 sfixed64_field_repeated = 121; + repeated sfixed64 sfixed64_field_packed = 122 [packed=true]; + + optional bool bool_field = 130; + repeated bool bool_field_repeated = 131; + repeated bool bool_field_packed = 132 [packed=true]; + + optional string string_field = 140; + repeated string string_field_repeated = 141; + + optional bytes bytes_field = 150; + repeated bytes bytes_field_repeated = 151; + + optional Outside outside_field = 160; + repeated Outside outside_field_repeated = 161; + repeated Outside outside_field_packed = 162 [packed=true]; + + optional Nested nested_field = 170; + repeated Nested nested_field_repeated = 171; +}; diff --git a/tests/net/java/android/net/NetworkStackTest.java b/tests/net/java/android/net/NetworkStackTest.java new file mode 100644 index 000000000000..f7c6c99ba622 --- /dev/null +++ b/tests/net/java/android/net/NetworkStackTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net; + +import static android.Manifest.permission.NETWORK_STACK; +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; +import static android.net.NetworkStack.checkNetworkStackPermission; +import static android.net.NetworkStack.checkNetworkStackPermissionOr; + +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.when; + +import android.content.Context; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class NetworkStackTest { + private static final String [] OTHER_PERMISSION = {"otherpermission1", "otherpermission2"}; + + @Mock Context mCtx; + + @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testCheckNetworkStackPermission() throws Exception { + when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_GRANTED); + when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK))) + .thenReturn(PERMISSION_DENIED); + checkNetworkStackPermission(mCtx); + checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); + + when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_DENIED); + when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK))) + .thenReturn(PERMISSION_GRANTED); + checkNetworkStackPermission(mCtx); + checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); + + when(mCtx.checkCallingOrSelfPermission(any())).thenReturn(PERMISSION_DENIED); + + try { + checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); + } catch (SecurityException e) { + // Expect to get a SecurityException + return; + } + + fail("Expect fail but permission granted."); + } +} diff --git a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java index 68ff777a0160..22a2c94fc194 100644 --- a/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceRefcountedResourceTest.java @@ -18,6 +18,7 @@ package com.android.server; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; @@ -134,11 +135,11 @@ public class IpSecServiceRefcountedResourceTest { IBinder binderMock = mock(IBinder.class); doThrow(new RemoteException()).when(binderMock).linkToDeath(anyObject(), anyInt()); - RefcountedResource<IResource> refcountedResource = getTestRefcountedResource(binderMock); - - // Verify that cleanup is performed (Spy limitations prevent verification of method calls - // for binder death scenario; check refcount to determine if cleanup was performed.) - assertEquals(-1, refcountedResource.mRefCount); + try { + getTestRefcountedResource(binderMock); + fail("Expected exception to propogate when binder fails to link to death"); + } catch (RuntimeException expected) { + } } @Test diff --git a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java index 548a0c22c569..5fb23b0ad507 100644 --- a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java @@ -40,7 +40,7 @@ public class ActivityManagerPermissionTests extends TestCase { @SmallTest public void testREORDER_TASKS() { try { - mAm.moveTaskToFront(0, 0, null); + mAm.moveTaskToFront(null, null, 0, 0, null); fail("IActivityManager.moveTaskToFront did not throw SecurityException as" + " expected"); } catch (SecurityException e) { diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 4e6d07303b02..1aad4be5d66f 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -1177,7 +1177,7 @@ static int write_java_method_for_module( // Initialize the buffer with list data type. fprintf(out, " buff[pos] = LIST_TYPE;\n"); - fprintf(out, " buff[pos + 1] = %lu;\n", signature.size() + 2); + fprintf(out, " buff[pos + 1] = %zu;\n", signature.size() + 2); fprintf(out, " pos += LIST_TYPE_OVERHEAD;\n"); // Write timestamp. diff --git a/wifi/java/android/net/wifi/aware/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java index 1e46d1b16feb..b07d8edde3d4 100644 --- a/wifi/java/android/net/wifi/aware/ConfigRequest.java +++ b/wifi/java/android/net/wifi/aware/ConfigRequest.java @@ -213,7 +213,7 @@ public final class ConfigRequest implements Parcelable { * Builder used to build {@link ConfigRequest} objects. */ public static final class Builder { - private boolean mSupport5gBand = false; + private boolean mSupport5gBand = true; private int mMasterPreference = 0; private int mClusterLow = CLUSTER_ID_MIN; private int mClusterHigh = CLUSTER_ID_MAX; diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index 52bb28485c72..db8220b41910 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -576,7 +576,7 @@ public class WifiAwareManagerTest { equalTo(configRequest.mClusterLow)); collector.checkThat("mMasterPreference", 0, equalTo(configRequest.mMasterPreference)); - collector.checkThat("mSupport5gBand", false, equalTo(configRequest.mSupport5gBand)); + collector.checkThat("mSupport5gBand", true, equalTo(configRequest.mSupport5gBand)); collector.checkThat("mDiscoveryWindowInterval.length", 2, equalTo(configRequest.mDiscoveryWindowInterval.length)); collector.checkThat("mDiscoveryWindowInterval[2.4GHz]", ConfigRequest.DW_INTERVAL_NOT_INIT, |